Skip to content

Bound startup entity maintenance with a timeout so a slow etcd can't block the edge#871

Merged
phinze merged 1 commit into
mirendev:mainfrom
jcasimir:fix/bound-startup-migration-read
Jun 29, 2026
Merged

Bound startup entity maintenance with a timeout so a slow etcd can't block the edge#871
phinze merged 1 commit into
mirendev:mainfrom
jcasimir:fix/bound-startup-migration-read

Conversation

@jcasimir

@jcasimir jcasimir commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

What

The coordinator's startup maintenance phase — entity-format migration (MigrateEntityStore), short-id backfill (MigrateShortIds), and reindex (checkAndReindex) — scans the entire entity store. Each step reads with an unbounded client.Get(ctx, prefix, clientv3.WithPrefix()). On a large or not-recently-compacted etcd, that single request can stall, and because it runs before the edge listener is up, the node stays dark until it returns.

This wraps the maintenance phase in a context.WithTimeout (2 min). Each step already treats failure as non-fatal (logs a warning and proceeds), so a timeout turns "hang forever" into "fail fast, continue startup, and retry on the next boot once compaction catches up."

Why

We hit this on a self-hosted single-node cluster (v0.10.0). After a routine restart the coordinator parked in starting entity migration and never bound :80/:443 — every app on the box was unreachable for ~7.5 h, while the app containers themselves kept running fine. etcd had accumulated a lot of MVCC history (its scheduled compaction kept getting interrupted by the resulting restart loop), and the unbounded read stalled indefinitely. Small / --limit reads stayed instant; only the full prefix read hung. Manually compacting + restarting cleared it, and the migration then completed in ~50 ms.

We may well be missing context on why the read is structured this way, so please treat this as "here's what we saw" rather than a prescription — happy to adjust.

Notes

  • Minimal and behavior-preserving: only adds a bounding context; no logic change.
  • go build, go vet, and gofmt are clean. The pkg/entity integration tests require the dev etcd environment (make dev), so they weren't run locally.
  • Companion PR Page entity-store scans in startup migrations instead of one unbounded read #872 paginates the reads themselves so they complete regardless of store size. The two are independent and complementary — this one is the safety net; that one fixes the read. Either can merge on its own.

The coordinator's startup maintenance phase (entity-format migration,
short-id backfill, reindex) scans the entire entity store. Each step
reads with an unbounded Get(WithPrefix()); on a large or
not-recently-compacted etcd that read can stall, blocking the
coordinator -- and therefore the edge listener -- indefinitely.

Each step already treats failure as non-fatal (warn and proceed), so
wrap the phase in a 2-minute context timeout. A slow store now fails
fast and startup continues, deferring the work to a later boot once
compaction catches up, instead of hanging the edge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f0322654-2b84-4c14-809b-cb356c72d4f7

📥 Commits

Reviewing files that changed from the base of the PR and between 9d1c679 and 232996f.

📒 Files selected for processing (1)
  • components/coordinate/coordinate.go

📝 Walkthrough

Walkthrough

In Coordinator.Start, a new bounded context (maintCtx) is created with a 2-minute timeout (startupMaintenanceTimeout) via context.WithTimeout, with its cancel deferred. The three startup maintenance operations—entity format migration (entity.MigrateEntityStore), short-id backfill (entity.MigrateShortIds), and automatic reindex (c.checkAndReindex)—are switched from the unbounded main ctx to this maintCtx. Error handling and logging remain unchanged.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@jcasimir jcasimir marked this pull request as ready for review June 29, 2026 17:19
@jcasimir jcasimir requested a review from a team as a code owner June 29, 2026 17:19

@phinze phinze left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oof a cluster stuck for 7.5h on boot is no fun - sorry you hit this!

Both this and #872 look sound and I'm merging them as-is. I've filed MIR-1272 with a few follow up details I'll pick up rather than bouncing nits back to you.

Really appreciate you for coming around with not just a bug report but a full on diagnosis and fix... thank you for taking the extra time to make our job easier ❤️

@phinze phinze merged commit e2b4fe3 into mirendev:main Jun 29, 2026
12 checks passed
phinze added a commit that referenced this pull request Jun 29, 2026
#872 added listEntitiesPaged to bound two startup scans, but the
principled home is one primitive every full-keyspace read shares.
Rename it scanPaged, give it keys-only and page-size options plus an
EtcdStore method, and route the three reads #872 missed through it:
the short-id unique-key set, ListAllEntityIDs, and reindex's
stale-cleanup scan. No startup scan open-codes Get(WithPrefix()) now.

Also collapse the reindex timeout nesting from #871. checkAndReindex's
inner 5-min budget always clamped to whatever was left of the 2-min
startup ceiling, so it was dead. Drop it and let reindex share the
single 2-min bound that keeps the coordinator off the edge listener's
back; an unfinished reindex retries on the next boot.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants