Skip to content
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

Implement priority queueing in the resource syncer #1052

Merged
merged 7 commits into from
Feb 5, 2025

Conversation

tpantelis
Copy link
Contributor

@tpantelis tpantelis commented Jan 24, 2025

All pre-existing resources are queued by the informer on startup. With many resources in the hundreds or thousands it can take significant time to process them all and the work queue rate limiter can cause further significant delays. This can block newly created resources or existing resource updates for potentially minutes. Most of the time a pod restart is fairly quick so most resources didn't likely change so it makes sense to give newly created/updated resources higher priority and process them in a more timely manner.

The client-go work queue allows the backend Queue implementation to be configurable. This PR adds an implementation to the admiral workqueue module that is backed by a priority queue that is used in conjunction with the container/heap module and implements heap.Interface. A new EnqueueWithPriority method was added to the admiral workqueue.Interface that allows the caller to specify the priority and whether or not to enqueue with rate limiting. The existing Enqueue method was modified to enqueue with normal priority 0.

The resource syncer was modified to use the EnqueueWithPriority method to prioritize newly created/updated resources over pre-existing resources on startup. The cache.ResourceEventHandler's OnAdd method has an isInInitialList param that indicates if the obj was pre-existing in the initial listing retrieved from the API server. If isInInitialList is true then enqueue with low priority otherwise use normal priority (Enqueue method).

See commits for more details.

Related to submariner-io/lighthouse#1706

@submariner-bot
Copy link
Contributor

🤖 Created branch: z_pr1052/tpantelis/priority_queue

}),
name: name,
}
}

func (q *queueType) Enqueue(obj interface{}) {
func (q *queueType) enqueue(obj interface{}, priority int) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could the priority use a specific type? It would make it more obvious that this expects one of the two constants, and not an arbitrary priority. (This isn’t all that important since the method is private.)

Copy link
Contributor Author

@tpantelis tpantelis Jan 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could but like you said it's private. I only defined and exposed two priorities (via specific functions) at least for now. Perhaps we'll have a future need for users to specify arbitrary priorities in which case a specific type wouldn't make sense. I figure we can deal with that if/when. For now I kept it simple with "low" and "normal".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another approach is to move the low and normal priority concept internally to the resource syncer and have the work queue take an arbitrary priority, ie change EnqueueLowPriority to EnqueueWithPriority(...int priority).

Copy link
Contributor Author

@tpantelis tpantelis Jan 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the method to EnqueueWithOpts and also added a RateLimiting flag (to mirror AddWithOpts in the new controller-runtime PriorityQueue). Looking at the K8s controller-runtime code, they do not enqueue items from the initial listing on startup with rate limiting which makes sense b/c it already knows it's a one-time burst. I did the same in the resource syncer.

...for use with the "container/heap" module that implements heap.Interface.
The items in the queue are ordered by descending priority such that
items with higher priority values are de-queued first.

Signed-off-by: Tom Pantelis <[email protected]>
tpantelis added a commit to tpantelis/submariner-website that referenced this pull request Jan 31, 2025
tpantelis added a commit to tpantelis/submariner-website that referenced this pull request Jan 31, 2025
...by configuring backend Queue for the client-go rate-limiting queue.
The work queue exposes two priorities for enqueue: normal priority via
the existing Enqueue method an arbitrary priority via a new
EnqueueWithOpts method. The new method also takes a 'RateLimited'
flag indicating to enqueue with or without rate limiting.

Signed-off-by: Tom Pantelis <[email protected]>
Utilize the workqueue's EnqueueWithOpts method to prioritize newly
created/updated resources over pre-existing resources on startup.
The cache.ResourceEventHandler's OnAdd method has an 'isInInitialList'
param that indicates if the obj is pre-existing in the initial listing
retrieved from the API server. If 'isInInitialList' is true then
enqueue with low priority otherwise use normal priority (Enqueue method).

The underlying priority queue might adjust the ordering of items with
the same priority so the ordering of items enqueued might not be the
same order that they're dequeued. As such, the namespace event handling
needed to be modified to use the 'operationQueues' to maintain the ordering
of adds and deletes.

Signed-off-by: Tom Pantelis <[email protected]>
On an informer re-sync, all resources are re-notified as update events.
Most likely the resources didn't actually change so enqueue with low
priority if the ResourceVersion didn't change.

Signed-off-by: Tom Pantelis <[email protected]>

func ConfigFromConfigMap(configMap *corev1.ConfigMap, keyPrefix string, defaultConfig *Config) *Config {
if configMap == nil {
return defaultConfig
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really minor, but perhaps

Suggested change
return defaultConfig
return DefaultConfigIfNil(defaultConfig)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't do that b/c I assumed the caller probably wants nil returned if they passed nil defaultConfig.

@yboaron yboaron merged commit 06f6cff into submariner-io:devel Feb 5, 2025
17 checks passed
@submariner-bot
Copy link
Contributor

🤖 Closed branches: [z_pr1052/tpantelis/priority_queue]

dfarrell07 pushed a commit to submariner-io/submariner-website that referenced this pull request Feb 5, 2025
@tpantelis tpantelis deleted the priority_queue branch February 5, 2025 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

5 participants