-
Notifications
You must be signed in to change notification settings - Fork 40k
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
Avoid deadlock in gc resync if available resources change during sync #64235
Avoid deadlock in gc resync if available resources change during sync #64235
Conversation
/sig api-machinery |
/assign @jennybuckley |
9daa867
to
03d55fc
Compare
@@ -288,15 +288,18 @@ func (gb *GraphBuilder) IsSynced() bool { | |||
defer gb.monitorLock.Unlock() | |||
|
|||
if len(gb.monitors) == 0 { | |||
glog.V(2).Info("garbage controller monitor not synced: no monitors") |
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.
Just curious, why isn't the set of all monitors considered synced? It makes more sense to me to say that it is synced, because all 0 of the monitors are synced.
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.
Looks like it used to do just that,
kubernetes/pkg/controller/garbagecollector/graph_builder.go
Lines 216 to 223 in 3d3d392
func (gb *GraphBuilder) HasSynced() bool { | |
for _, monitor := range gb.monitors { | |
if !monitor.HasSynced() { | |
return false | |
} | |
} | |
return true | |
} |
But got changed by d08dfb9#diff-b26daf763d446f92d20456ca698ce237R303
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'd be open to revisiting this, but wanted to keep current behavior in this PR
/retest |
for attempt := 0; ; attempt++ { | ||
// If this is a re-attempt, check if available resources have changed | ||
if attempt > 0 { | ||
newResources = GetDeletableResources(discoveryClient) |
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.
We may want to check if the list is empty again, like here https://github.com/kubernetes/kubernetes/pull/64235/files#diff-1481f32cd1a60ce2a66e5d8b5e6cf383R175 but instead of returning we should probably continue,
if GetDeletableResources encounters any errors it will return an empty list of resources. if we allow it to go forward with the sync with an empty list of resources, resyncMonitors will remove all the work that has been done.
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.
Good idea. Probably add a protection against a hotloop here and in the resyncFailure case as well
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.
checked for zero length rediscovery results, used wait.PollImmediateInfinite
instead of a plain for loop to get hotloop and panic protection
close(waitForSyncStop) | ||
}() | ||
|
||
if controller.WaitForCacheSync("garbage collector", waitForSyncStop, gc.dependencyGraphBuilder.IsSynced) { |
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.
nit: the monitor-cache-sync progress (for resources that aren't added or removed) won't get reset when we timeout, so we don't need exponential backoff timeout
the "timeout" is not limiting the time resource used for syncing cache. not sure if it's a pro or con
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.
the "timeout" is not limiting the time resource used for syncing cache.
Can you clarify what you mean by this
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.
nit: the monitor-cache-sync progress (for resources that aren't added or removed) won't get reset when we timeout, so we don't need exponential backoff timeout
Agree we don't need exponential, a simple timeout before we attempt rediscovery/resync seems sufficient
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.
E.g. we discover a new resource and start a new monitor for it with gc.resyncMonitors()
. We set a timeout of 30s and wait for the monitor cache to get synced. A long sync time (like 50s) won't violate the timeout. We don't need to increase the timeout, instead we just wait till the second timeout period. It was confusing to me that the timeout is not for guarding the sync time, it's the period when we decide to poll discovery again.
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.
got it... that's what this comment was trying to explain, though I'm completely open to better wording (I understood what I meant, but... I didn't get that understanding from reading my own comment)
// wait for caches to fill for a while (our sync period) before attempting to rediscover resources and retry syncing.
// this protects us from deadlocks where available resources changed and one of our informer caches will never fill.
// informers keep attempting to sync in the background, so retrying doesn't interrupt them.
// the call to resyncMonitors on the reattempt will no-op for resources that still exist.
// note that workers stay paused until we successfully resync.
waitForSyncStop := make(chan struct{})
9dad2d5
to
77f0563
Compare
/test pull-kubernetes-e2e-gce |
77f0563
to
62ea336
Compare
@roycaihw @jennybuckley I think this is ready. there are still other issues going on (seeing timeouts on watch-fed things, including garbage collection, just like in master), but this resolves the deadlock and unsafe worker restart (and adds logging to help us track down additional master flakes) @deads2k can you take a look at this as well? |
removed.Insert(fmt.Sprintf("%+v", old)) | ||
} | ||
} | ||
for new := range newResources { |
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.
nit: don't use new
(keyword) as variable name
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.
fixed
9e67f06
to
565d96e
Compare
/lgtm |
565d96e
to
7da3d65
Compare
ask and you shall receive on master:
with this change:
|
/milestone v1.11 |
/lgtm |
/assign caesarxuchao lavalamp for approval |
[MILESTONENOTIFIER] Milestone Pull Request: Up-to-date for process @caesarxuchao @jennybuckley @lavalamp @liggitt @roycaihw Pull Request Labels
|
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.
/approve
// informers keep attempting to sync in the background, so retrying doesn't interrupt them. | ||
// the call to resyncMonitors on the reattempt will no-op for resources that still exist. | ||
// note that workers stay paused until we successfully resync. | ||
if !controller.WaitForCacheSync("garbage collector", waitForStopOrTimeout(stopCh, period), gc.dependencyGraphBuilder.IsSynced) { |
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.
Timeout when waiting for cache sync seems to be useful in general, consider adding a WaitForCacheSyncUntil
to client-go.
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: caesarxuchao, jennybuckley, liggitt, roycaihw 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 |
Automatic merge from submit-queue (batch tested with PRs 62266, 64351, 64366, 64235, 64560). If you want to cherry-pick this change to another branch, please follow the instructions here. |
retry GC sync if waiting for cache sync times out, without unpausing workers
viewing ignoring whitespace reveals the actual change:
https://github.com/kubernetes/kubernetes/pull/64235/files?w=1
xref #61057 #56446 (comment)