-
Notifications
You must be signed in to change notification settings - Fork 40.9k
client-go/reflector: stop exposing UseWatchList #132453
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
client-go/reflector: stop exposing UseWatchList #132453
Conversation
This issue is currently awaiting triage. If a SIG or subproject determines this is a relevant issue, they will accept it by applying the The Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
/assign @wojtek-t |
@@ -437,9 +436,6 @@ func NewCacherFromConfig(config Config) (*Cacher, error) { | |||
// We don't want to terminate all watchers as recreating all watchers puts high load on api-server. | |||
// In most of the cases, leader is reelected within few cycles. | |||
reflector.MaxInternalErrorRetryDuration = time.Second * 30 | |||
// since the watch-list is provided by the watch cache instruct | |||
// the reflector to issue a regular LIST against the store | |||
reflector.UseWatchList = ptr.To(false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking through potential concerns:
- "go back in time" - this doesn't change, because without list-watch, we were using "rv=0" on startup anyway, right?
- does list-watch even work with etcd? etcd won't send you the bookmark, so we don't even know when the "list" part i finished. how is that solved?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- does list-watch even work with etcd? etcd won't send you the bookmark, so we don't even know when the "list" part i finished. how is that solved?
yes, we added support for streaming directly to the etcd storage layer - #119557
"go back in time" - this doesn't change, because without list-watch, we were using "rv=0" on startup anyway, right?
I think this translates to Quorum list + watch stream
both on startup and on resumption, because of RV=0, RVM=NotOlderThan, SendInitialEvents= true
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, we added support for streaming directly to the etcd storage layer - #119557
OK - great, forgot about it.
I think this translates to Quorum list + watch stream both on startup and on resumption, because of RV=0, RVM=NotOlderThan, SendInitialEvents= true
RV=0, RVM=NotOlderThan is not a Quorum list - it's "given me anything not older than 0 - so literally anything.
RV="" is what we need here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, since the cacher doesn't set the RV it actually will be RV="", which also translates to Quorum list + watch stream
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not handled by cacher - this part is explicitly handled by reflector.
But for posterity, we seem to be good here:
- With regular list (the old way):
- the initial list is using relistResourceVersion():
options := metav1.ListOptions{ResourceVersion: r.relistResourceVersion()} - it's setting RV="0" initially and later reusing the last seen RV:
kubernetes/staging/src/k8s.io/client-go/tools/cache/reflector.go
Lines 998 to 1003 in 6eff9db
if r.lastSyncResourceVersion == "" { // For performance reasons, initial list performed by reflector uses "0" as resource version to allow it to // be served from the watch cache if it is enabled. return "0" } return r.lastSyncResourceVersion
- With listwatch (the new way)
- we're using rewatchResourceVersion():
kubernetes/staging/src/k8s.io/client-go/tools/cache/reflector.go
Lines 739 to 751 in 6eff9db
lastKnownRV := r.rewatchResourceVersion() temporaryStore = NewStore(DeletionHandlingMetaNamespaceKeyFunc) // TODO(#115478): large "list", slow clients, slow network, p&f // might slow down streaming and eventually fail. // maybe in such a case we should retry with an increased timeout? timeoutSeconds := int64(r.minWatchTimeout.Seconds() * (rand.Float64() + 1.0)) options := metav1.ListOptions{ ResourceVersion: lastKnownRV, AllowWatchBookmarks: true, SendInitialEvents: pointer.Bool(true), ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan, TimeoutSeconds: &timeoutSeconds, } - this is using lastSyncResourceVersion:
return r.lastSyncResourceVersion - which is set to RV="" initially and later reusing the last seen RV
So we seem to be good here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not handled by cacher - this part is explicitly handled by reflector.
@wojtek-t the cacher provides a ListWatcher which is used by the reflector. The ListWatcher ignores the RV passed from the reflector, here: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/storage/cacher/lister_watcher.go#L61
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But if we use "watchlist" feature, the List call will not be used at all, because reflector will call watch:
w, err = r.listerWatcher.WatchWithContext(ctx, options) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought you were worried about a failure mode where the new mode fails and we fallback to the standard list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we might have the following cases:
RV = “” for list translates to quorum read
RV = 0 for list translates to quorum read
RV = “”, for watchlist also translates to quorum read.
RV = 0 for watchlist translates to quorum read
RV > 0 for watchlist translates to quorum read + check RV <= etcdRV
On startup and fallback we use list+watch with RV = “” which translates to quorum read.
On resumption and fallback we use list+watch with RV = “” which translates to quorum read.
On expiration and fallback we use list+watch with RV = “” which translates to quorum read.
On startup for watchlist we use RV = 0 which translates to quorum read.
On resumption for watchlist we use RV > 0 which translates to quorum read.
On expiration for watchlist we use RV=“” which translates to quorum read.
The test failures seem real - please fix |
) | ||
|
||
func TestReflectorWatchListFallback(t *testing.T) { | ||
t.Skipf("test") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix - we don't want to commit it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
first we need #132479
adccf59
to
f1b991a
Compare
target := cache.NewReflector(lw, &v1.Secret{}, store, time.Duration(0)) | ||
target.UseWatchList = ptr.To(true) | ||
clientfeaturestesting.SetFeatureDuringTest(t, clientfeatures.WatchListClient, false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is somewhat weird to me - what are we trying to achieve here?
Also - isn't it racy by definition?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setting the FG also affects the server (the second server, the informers used by the second server), wanted to enable the FG only for the informer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK - and this is relying on the fact that we're instantiating it as part of reflector creation here:
https://github.com/kubernetes/kubernetes/pull/132453/files#diff-9ccdf713e010f73dbebd01e936cb0077fc63e4f5ab941d865ded42da219d84ecR293
Let's maybe try to structure that cleaner and add some comment like:
var target *Reflector
func() {
// Enable ListWatchClient only for this reflector.
// We rely on the fact that instantiation of whether watchlist is used or not is done once
// during reflector creation in NewReflectorWithOptions
clientfeaturestesting.SetFeatureDuringTest(t, clientfeatures.WatchListClient, true)
defer clientfeaturestesting.SetFeatureDuringTest(t, clientfeatures.WatchListClient, false)
target = cache.NewReflector(lw, &v1.Secret{}, store, time.Duration(0))
}()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mhm, i am not sure why wrapping into a function is cleaner (?). Setting a FG during a test is serial, there is not race.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK - and this is relying on the fact that we're instantiating it as part of reflector creation here:
https://github.com/kubernetes/kubernetes/pull/132453/files#diff-9ccdf713e010f73dbebd01e936cb0077fc63e4f5ab941d865ded42da219d84ecR293
yes, we can add a comment to clarify.
I think we need one more fix. |
|
f1b991a
to
4437a62
Compare
@p0lyn0mial: The following test failed, say
Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
/lgtm |
LGTM label has been added. Git tree hash: 75f3d65910a221c0eece1318601d050c36c43397
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: p0lyn0mial, wojtek-t The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
What type of PR is this?
/kind cleanup
What this PR does / why we need it:
Which issue(s) this PR is related to:
Special notes for your reviewer:
Does this PR introduce a user-facing change?
Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.: