Skip to content

Page entity-store scans in startup migrations instead of one unbounded read#872

Merged
phinze merged 1 commit into
mirendev:mainfrom
jcasimir:fix/paginate-entity-migration-reads
Jun 29, 2026
Merged

Page entity-store scans in startup migrations instead of one unbounded read#872
phinze merged 1 commit into
mirendev:mainfrom
jcasimir:fix/paginate-entity-migration-reads

Conversation

@jcasimir

Copy link
Copy Markdown
Contributor

What

MigrateEntityStore and MigrateShortIds each read the entire entity keyspace with a single client.Get(ctx, prefix, clientv3.WithPrefix()). This adds a small listEntitiesPaged helper that reads the prefix in bounded pages (WithLimit + a key cursor, ascending key order) and uses it in both migrations. Per-entity processing is unchanged — only the read is paged.

Why

A single unbounded prefix read loads the whole keyspace (keys and values) in one RPC. On a large or not-recently-compacted store that request can stall, and because these migrations run early in coordinator startup, a stall blocks the edge listener from coming up. We hit exactly this on a self-hosted node — the migration's full read hung for hours after a restart, leaving every app unreachable (more detail in the companion PR #871). Paging keeps each request bounded and predictable regardless of store size.

As with #871, we may be missing context here, so this is offered as "what worked for us" rather than a prescription.

Notes

MigrateEntityStore and MigrateShortIds each read the entire entity
keyspace with a single Get(WithPrefix()). On a large or
not-recently-compacted etcd that single unbounded request can stall, and
because these run early in coordinator startup it can block the edge
from coming up.

Add listEntitiesPaged, which reads the prefix in bounded pages
(WithLimit + a key cursor in ascending key order), and use it in both
migrations. Per-entity processing is unchanged; only the read is paged.

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

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@jcasimir, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b1ac27c6-3032-4703-b46c-135d71633bc5

📥 Commits

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

📒 Files selected for processing (3)
  • pkg/entity/migrate.go
  • pkg/entity/scan.go
  • pkg/entity/shortid_migrate.go

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:22
@jcasimir jcasimir requested a review from a team as a code owner June 29, 2026 17:22

@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.

Same as #871 - taking this as-is w/ a few follow ups I'll pick up in MIR-1272. Thanks again!

@phinze phinze merged commit ac1034e 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