New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
apiserver/storage: test caching and indexing layers at the unit level #109831
Comments
/triage accepted |
Couple thoughts from me:
Indeed I would switch to storage.Interface.Get instead.
Making a generic test from that seems trivial, but I'm yet sure how to make a simple etcd-specific additional test...
Can we just make the test to take two implementations of storage.Interface as an input? And then it trivially generalizes, no?
Hmm - it's similar to TestGuaranteedUpdate - it seems trivial to generalize (by removing those calls), but I'm not sure how to test that functionality without duplicating the same test more-or-less...
I'm wondering if we would be to somehow pass a compaction callback or sth like that to the test.
Can we pass two stores to the test?
Roughly what you suggested... |
Thinking more about TestGuaranteedObject (and similar cases where we just check simple invariants). Can we pass an additional callback to the test function that will be called (in etcd test we can just pass checkStorageInvariants, in watchcache ones, we can basically just pass a no-op function)... |
maybe I'm misunderstanding, but the rv returned from a Get or from the Create is explicitly not what we want to be the RV in a delete watch event or delete response. The RV on a delete response or watch event must be distinct from and later than the RV on the last write to the object. instrumenting the current test: diff --git a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher_test.go b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher_test.go
index 273bb1c4389..21f42ec1f06 100644
--- a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher_test.go
+++ b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher_test.go
@@ -293,6 +293,7 @@ func TestWatchDeleteEventObjectHaveLatestRV(t *testing.T) {
t.Fatalf("Watch failed: %v", err)
}
rv, err := APIObjectVersioner{}.ObjectResourceVersion(storedObj)
+ t.Log("created",rv)
if err != nil {
t.Fatalf("failed to parse resourceVersion on stored object: %v", err)
}
@@ -310,12 +311,14 @@ func TestWatchDeleteEventObjectHaveLatestRV(t *testing.T) {
t.Fatalf("timed out waiting for watch event")
}
deletedRV, err := deletedRevision(watchCtx, etcdW)
+ t.Log("deleted",deletedRV)
if err != nil {
t.Fatalf("did not see delete event in raw watch: %v", err)
}
watchedDeleteObj := e.Object.(*example.Pod)
watchedDeleteRev, err := store.versioner.ParseResourceVersion(watchedDeleteObj.ResourceVersion)
+ t.Log("watched",watchedDeleteRev)
if err != nil {
t.Fatalf("ParseWatchResourceVersion failed: %v", err)
} you get
|
@liggitt thanks - seems like I had a mistaken assumption that |
@liggitt mentioned this test plan in a Slack thread, wanted to add it here for posterity:
Unfortunately, we can't do step 3. Clients definitely do not get anything back from a DELETE call, and it looks like the current implementation for etcd puts the resourceVersion of the value used for preconditions into the |
This is the type of test which should become possible once we introduce the interface layer right above etcdClient, so I think it's okay to omit this for now on the presumption that this will be tested in the interface we're proposing. |
@logicalhan would it be sufficient to test that the storage provides this semantic to the |
I would be okay with just testing this on the inner layer, it should transitively retain the properties on the outer layer. But that's just me. Porting this logic to the outer layer involves patching the |
The Kubernetes project currently lacks enough contributors to adequately respond to all issues and PRs. This bot triages issues and PRs according to the following rules:
You can:
Please send feedback to sig-contributor-experience at kubernetes/community. /lifecycle stale |
The Kubernetes project currently lacks enough active contributors to adequately respond to all issues and PRs. This bot triages issues and PRs according to the following rules:
You can:
Please send feedback to sig-contributor-experience at kubernetes/community. /lifecycle rotten |
/remove-lifecycle rotten |
I can take a stab at this if anyone isn't already working on it! |
A bit of that already hapenned in: #113427 and #113588 I have a bit more started in my local branch (#113666 is a preparation for making that possible), but my work (for now) is limited to lists. If you want to help with making that for other tests (in particular Create, GuaranteedUpdate and Delete tests), that would definitely be useful. |
and watches |
The Kubernetes project currently lacks enough contributors to adequately respond to all PRs. This bot triages PRs according to the following rules:
You can:
Please send feedback to sig-contributor-experience at kubernetes/community. /lifecycle stale |
/remove-lifecycle stale |
@wojtek-t @stevekuznetsov i see from the issue description that those tests are taken care of, is there anything else left that I can help with here? |
@MadhavJivrajani - the next step I would like to take here (which is a bit started, but not that much happened here) is to ensure that all tests that we generalized will actually also be run against watchcache: There are three of them already there (and one more on its way in #114656 ), but we need to enable all tests. If you have capacity to help with it, it would be great. The next bit (or ideally something that can be taken simultanously, we need to audit tests that exist in that file and either:
|
This issue has not been updated in over 1 year, and should be re-triaged. You can:
For more details on the triage process, see https://www.kubernetes.dev/docs/guide/issue-triage/ /remove-triage accepted |
/triage accepted |
What would you like to be added?
Today, unit-level tests only exist for
*etcd3.store{}
, and cannot be run against any other implementation ofstorage.Interface
. In general, these tests define what it means to be a correct implementation ofstorage.Interface
, and only use exported functionality. We aim to refactor the tests to accept anystorage.Interface
. This allows for testing the entire exported function space for an etcd storage layer with whichever caching and indexing layers on top.The following tests can be ported as-is, since they use only
storage.Interface
methods:In #109833:
TestCreateWithTTL
TestCreateWithKeyExist
TestGet
TestUnconditionalDelete
TestConditionalDelete
TestDeleteWithSuggestion
TestDeleteWithSuggestionAndConflict
TestDeleteWithSuggestionOfDeletedObject
TestValidateDeletionWithSuggestion
TestPreconditionalDeleteWithSuggestion
TestGetListNonRecursive
TestGuaranteedUpdateWithTTL
TestGuaranteedUpdateWithConflict
TestGuaranteedUpdateWithSuggestionAndConflict
TestCount
TestWatch
TestDeleteTriggerWatch
TestWatchFromNoneZero
TestWatchInitializationSignal
Other:
TestProgressNotify
(requires that the underlying etcd is configured to send bookmarks)The following tests currently require the etcd client, and need to be re-thought or adapted:
TestCreate
uses the etcd client to: storage/testing: move creation test to generic package #109909storage.Interface.Get()
instead?*etcd3.store{}
specific test onlyTestGuaranteedUpdate
uses the etcd client to:*etcd3.store{}
specific test onlyTestGuaranteedUpdateChecksStoredData
uses the etcd client to:TestList
uses the etcd client to: storage: split paginated and non-paginated list tests, make them generic #110024*etcd3.store{}
implementations (with and without paging)TestListContinuation
uses the etcd client to:TestListPaginationRareObject
uses the etcd client to:TestListContinuationWithFilter
uses the etcd client to:TestListInconsistentContinuation
uses the etcd client to:TestWatchFromZero
uses the etcd client to:TestWatchError
uses the etcd client to:*etcd3.store{}
implementations (with a valid and invalid codec)TestWatchDeleteEventObjectHaveLatestRV
uses the etcd client to:storage.Interface
uses to report the deletion ... but if we know nothing else is touching our storage, why not just usestorage.Interface.Get()
(or the returned value from creating) to check that the last RV we saw is what it's deleted at?The following tests currently touch internals of
*etcd3.store{}
:TestGuaranteedUpdate
touches:store.transformer
in order to inject staleness to cause an update when the semantic value of the data does not changeTestGuaranteedUpdateChecksStoredData
touches:store.transformer
in order to inject staleness to cause an update when the semantic value of the data does not changeTestTransformationFailure
touches:store.transformer
in order to test behavior under transformation failureTestListContinuation
touches:store.transformer
to count the number of items read from etcdTestListPaginationRareObject
touches:store.transformer
to count the number of items read from etcdTestListContinuationWithFilter
touches:store.transformer
to count the number of items read from etcdTestConsistentList
touches:store.transformer
to ... cause side-effect reads during a paginated LIST call ? @wojtek-t you wrote this, perhaps there's a less invasive way to do thisTestLeaseMaxObjectCount
touches:store.leaseManager
- this is etcd-specific, so perhaps it remains in the*etcd3.store{}
level?TestWatchFromZero
touches:store.versioner
, but that is a singleton and there's no real reason that needs to be an instance variable of the store as opposed to an exported package-level function?TestWatchContextCancel
touches: storage/testing: move cancelled watch test to generic package #109914store.watcher.Watch()
, but we're testing user-facing behavior of the watch method, so it's not clear why calling the watcher specifically is necessary hereTestWatchErrResultNotBlockAfterCancel
touches:store.watcher.createWatchChan()
- this looks like an implementation detail of the watcher?TestWatchDeleteEventObjectHaveLatestRV
touches:store.versioner
, but that is a singleton and there's no real reason that needs to be an instance variable of the store as opposed to an exported package-level function?Next steps:
*_tests.go
files and change their signature to accept astorage.Interface
, import these and use them to test caching and indexing layers (storage/etcd3: factor tests to acceptstorage.Interface
#109833)etcd
client whether they could instead use thestorage.Interface
itself, etc*etcd3.store{}
are useful to test otherstorage.Interface
implementations, and, if so, how they can be adapted/sig api-machinery
/assign @stevekuznetsov @wojtek-t
/cc @liggitt @lavalamp
Why is this needed?
To improve our ability to test the caching and indexing layers that sit on top of the etcd storage implementation.
The text was updated successfully, but these errors were encountered: