-
Notifications
You must be signed in to change notification settings - Fork 451
fix: on-demand lease manager for cold start concurrency queue #4281
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
Changes from 7 commits
8923d12
6c4daf6
cffa038
f461fd4
802003f
a171dd2
b52d11e
b223f55
467f99f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -177,10 +177,10 @@ func (t *tenantManager) listenForConcurrencyLeases(ctx context.Context) { | |
| case msg := <-t.concurrencyCh: | ||
| if msg.isIncremental { | ||
| for _, strategy := range msg.items { | ||
| t.addConcurrencyStrategy(strategy) | ||
| t.addConcurrencyStrategy(ctx, strategy) | ||
| } | ||
| } else { | ||
| t.setConcurrencyStrategies(msg.items) | ||
| t.setConcurrencyStrategies(ctx, msg.items) | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -238,7 +238,7 @@ func (t *tenantManager) addQueuer(queueName string) { | |
| t.queue(context.Background(), []string{queueName}) | ||
| } | ||
|
|
||
| func (t *tenantManager) setConcurrencyStrategies(strategies []*sqlcv1.V1StepConcurrency) { | ||
| func (t *tenantManager) setConcurrencyStrategies(ctx context.Context, strategies []*sqlcv1.V1StepConcurrency) { | ||
| t.concurrencyMu.Lock() | ||
| defer t.concurrencyMu.Unlock() | ||
|
|
||
|
|
@@ -263,13 +263,18 @@ func (t *tenantManager) setConcurrencyStrategies(strategies []*sqlcv1.V1StepConc | |
| } | ||
|
|
||
| for _, strategy := range strategiesSet { | ||
| newArr = append(newArr, newConcurrencyManager(t.cf, t.tenantId, strategy, t.concurrencyResultsCh, t.concurrencyAdvisoryLock, t.concurrencyParentAdvisoryLock)) | ||
| c := newConcurrencyManager(t.cf, t.tenantId, strategy, t.concurrencyResultsCh, t.concurrencyAdvisoryLock, t.concurrencyParentAdvisoryLock) | ||
| newArr = append(newArr, c) | ||
|
|
||
| // notify the new manager so it picks up already-queued work now instead of waiting for its first | ||
| // tick (matters on startup/rebalance). notify() is a non-blocking send, so it's fine under the lock. | ||
| c.notify(ctx) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same comment as above/below |
||
| } | ||
|
|
||
| t.concurrencyStrategies = newArr | ||
| } | ||
|
|
||
| func (t *tenantManager) addConcurrencyStrategy(strategy *sqlcv1.V1StepConcurrency) { | ||
| func (t *tenantManager) addConcurrencyStrategy(ctx context.Context, strategy *sqlcv1.V1StepConcurrency) { | ||
| t.concurrencyMu.Lock() | ||
| defer t.concurrencyMu.Unlock() | ||
|
|
||
|
|
@@ -279,7 +284,11 @@ func (t *tenantManager) addConcurrencyStrategy(strategy *sqlcv1.V1StepConcurrenc | |
| } | ||
| } | ||
|
|
||
| t.concurrencyStrategies = append(t.concurrencyStrategies, newConcurrencyManager(t.cf, t.tenantId, strategy, t.concurrencyResultsCh, t.concurrencyAdvisoryLock, t.concurrencyParentAdvisoryLock)) | ||
| c := newConcurrencyManager(t.cf, t.tenantId, strategy, t.concurrencyResultsCh, t.concurrencyAdvisoryLock, t.concurrencyParentAdvisoryLock) | ||
| t.concurrencyStrategies = append(t.concurrencyStrategies, c) | ||
|
|
||
| // notify the new manager so it schedules its waiting task now instead of waiting for its first tick. | ||
| c.notify(ctx) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: we should consider doing this on concurrency manager instantiation instead? I think the manager has a notifier channel as well, on instantiation we can just put a message into that channel so that it runs immediately after startup? having to remember to notify each time we call |
||
| } | ||
|
|
||
| func (t *tenantManager) replenish(ctx context.Context) { | ||
|
|
@@ -304,6 +313,9 @@ func (t *tenantManager) notifyConcurrency(ctx context.Context, strategyIds []int | |
| continue | ||
| } | ||
|
|
||
| // mark this strategy as already managed so we don't try to acquire a lease for it below | ||
| delete(strategyIdsMap, c.strategy.ID) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are we sure about this? I see immediately above this we're doing: if _, ok := strategyIdsMap[c.strategy.ID]; !ok {
continue
}So it seems like we might be doing more processing as a result of deleting this entry. Perhaps we need two copies of the map? |
||
|
|
||
| c.notify(ctx) | ||
|
|
||
| childStrategyIds := make([]int64, 0) | ||
|
|
@@ -354,6 +366,17 @@ func (t *tenantManager) notifyConcurrency(ctx context.Context, strategyIds []int | |
| } | ||
|
|
||
| t.concurrencyMu.RUnlock() | ||
|
|
||
| // anything still in the map has no manager here (cold strategy): acquire its lease on-demand so a | ||
| // manager is created now instead of waiting for the ~5s poll. run off the hot path since it hits the DB. | ||
| if len(strategyIdsMap) > 0 { | ||
| // don't inherit cancellation from the message handler returning, but keep telemetry/values | ||
| leaseCtx := context.WithoutCancel(ctx) | ||
|
|
||
| for id := range strategyIdsMap { | ||
| go t.notifyNewConcurrencyStrategy(leaseCtx, id) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func (t *tenantManager) notifyNewWorker(ctx context.Context, workerId uuid.UUID) { | ||
|
|
||
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: is this linted properly?