Skip to content

Commit b6aff87

Browse files
committed
feat(gc): add timeout for store calls to prevent stalled sessions
1 parent b4e8517 commit b6aff87

2 files changed

Lines changed: 27 additions & 5 deletions

File tree

worker/gc.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ const workloadLabelKey = "ctrlplane.workload"
2929
// reaps via this field.
3030
const gcReasonOrphanSwept = "orphan_swept"
3131

32+
// gcStoreCallTimeout caps each individual store roundtrip
33+
// (ListTenants, instances.List, Delete) so a stalled driver session
34+
// can't pin a connection from the shared pool past the next tick.
35+
// 30 seconds is generous for cross-tenant scans on a healthy mongo
36+
// and tight enough that one slow tenant doesn't blind-spot the rest.
37+
const gcStoreCallTimeout = 30 * time.Second
38+
3239
// gcReasonTenantFailed tags an event published when a per-tenant
3340
// sweep aborted half-way. Lets operators correlate GC error spikes
3441
// with specific tenants (e.g. one tenant's storage backend down
@@ -156,7 +163,9 @@ func (g *GarbageCollector) Interval() time.Duration {
156163
// the sweep continues to the next tenant — one broken tenant
157164
// must not blind-spot the rest.
158165
func (g *GarbageCollector) Run(ctx context.Context) error {
159-
tenants, err := g.tenants.ListTenants(ctx, admin.ListTenantsOptions{Limit: 1000})
166+
listCtx, listCancel := context.WithTimeout(ctx, gcStoreCallTimeout)
167+
tenants, err := g.tenants.ListTenants(listCtx, admin.ListTenantsOptions{Limit: 1000})
168+
listCancel()
160169
if err != nil {
161170
return fmt.Errorf("gc: list tenants: %w", err)
162171
}
@@ -206,7 +215,9 @@ func (g *GarbageCollector) Run(ctx context.Context) error {
206215
func (g *GarbageCollector) sweepTenant(ctx context.Context, tenantID string) (int, error) {
207216
tCtx := withSystemClaims(ctx, tenantID)
208217

209-
res, err := g.instances.List(tCtx, instance.ListOptions{Limit: g.cfg.MaxInstancesPerTick})
218+
listCtx, listCancel := context.WithTimeout(tCtx, gcStoreCallTimeout)
219+
res, err := g.instances.List(listCtx, instance.ListOptions{Limit: g.cfg.MaxInstancesPerTick})
220+
listCancel()
210221
if err != nil {
211222
return 0, fmt.Errorf("list instances: %w", err)
212223
}
@@ -224,7 +235,10 @@ func (g *GarbageCollector) sweepTenant(ctx context.Context, tenantID string) (in
224235
continue
225236
}
226237

227-
if err := g.instances.Delete(tCtx, inst.ID); err != nil {
238+
delCtx, delCancel := context.WithTimeout(tCtx, gcStoreCallTimeout)
239+
err := g.instances.Delete(delCtx, inst.ID)
240+
delCancel()
241+
if err != nil {
228242
// Don't abort the tenant sweep on a single Delete
229243
// failure — convergent Delete will retry next tick.
230244
// We still emit a tagged event so operators see the

worker/reconciler.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ func (r *Reconciler) Run(ctx context.Context) error {
8282
return nil
8383
}
8484

85-
dcs, err := r.datacenters.ListDatacenters(ctx, "", datacenter.ListOptions{Limit: 1000})
85+
listCtx, listCancel := context.WithTimeout(ctx, gcStoreCallTimeout)
86+
dcs, err := r.datacenters.ListDatacenters(listCtx, "", datacenter.ListOptions{Limit: 1000})
87+
listCancel()
8688
if err != nil {
8789
return fmt.Errorf("reconciler: list datacenters: %w", err)
8890
}
@@ -108,7 +110,13 @@ func (r *Reconciler) Run(ctx context.Context) error {
108110
Labels: dc.Labels,
109111
}
110112

111-
if err := r.bootstraps.Reconcile(dcCtx, info, dc.BootstrapServices); err != nil {
113+
// Bound the Reconcile call too — it can fan out into provider
114+
// and instance.Service work that touches the same shared
115+
// driver pool the dispatch / GC loops use.
116+
recCtx, recCancel := context.WithTimeout(dcCtx, gcStoreCallTimeout)
117+
err := r.bootstraps.Reconcile(recCtx, info, dc.BootstrapServices)
118+
recCancel()
119+
if err != nil {
112120
// Isolate per-datacenter failures so the rest of the
113121
// tick still runs. The bootstrap service itself emits
114122
// a kind=bootstrap event with the error payload — no

0 commit comments

Comments
 (0)