Skip to content

refactor(run-store): hold RoutingRunStore's stores in a keyed shard map - #4752

Merged
d-cs merged 3 commits into
mainfrom
refactor/routing-run-store-shard-map-tri-13426
Aug 21, 2026
Merged

refactor(run-store): hold RoutingRunStore's stores in a keyed shard map#4752
d-cs merged 3 commits into
mainfrom
refactor/routing-run-store-shard-map-tri-13426

Conversation

@d-cs

@d-cs d-cs commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

What

RoutingRunStore held two named store fields, #new and #legacy, and took its routing policy from the order the statements happened to run in. It now holds a Map<ShardKey, RunStore>, and the three policies that were implicit are readable data:

  • #probeOrder (newlegacy) — the sequential probe for a lookup with no routable id. The first non-null result wins, and the last entry owns the canonical not-found throw.
  • #precedence (legacynew) — ascending authority for a merge, so the highest-authority shard wins a duplicate id.
  • #idlessRouteShard (new) and #idlessWaitpointShard (legacy) — the two id-less defaults, which differ by role and were previously two unrelated literals in unrelated methods.

The two orders are the reverse of each other, which is why they are separate fields rather than one ordering. Nine sites observe the result-array order and must iterate #probeOrder; five decide a value by which shard wins a duplicate and must iterate #precedence. Five more sum counts and are order-independent, because addition commutes.

Four helpers absorb the twenty-six hand-written fan-outs — #probeFirst, #fanOut(order, fn), #fanOutPartitioned, #shardsExcept — and #shardKeyOf replaces the inline residency-to-store ternaries. #fanOut takes its order as an argument so every call site states which policy it uses.

The constructor keeps its exact options type. No union arm, no shards member: that would loosen the excess-property check and silently retire the @ts-expect-error onLegacyRead lock in the test corpus. N-way construction is a later change.

One behaviour change

findManyTaskRunWaitpoints merged its edge rows NEW-first into a last-wins dedupe, so a duplicate edge id resolved to the legacy row — the opposite of the rule the other four merges follow, and the opposite of what dedupeEdgesById's own comment claimed. No test pinned it in either direction.

It now resolves NEW-wins, consistent with every sibling merge, and a new test pins the winner so it cannot drift back silently.

Reaching this case needs one edge id present on both stores at the same time, with no routable taskRunId. That only arises from drain mirroring. The drain seam is removed (runOpsStore.test.ts, "fan-out spans NEW+LEGACY with no drain seam"), so no new duplicates can be created — but removing the code does not delete rows it previously wrote, and this class still carries comments treating mirrored rows as a live data condition. Whether any historical duplicate edge rows persist is an empirical question about production data, not something this diff settles.

If such a row is hit, the two copies either agree — in which case the winner is immaterial — or they have diverged, in which case NEW is the authoritative copy by the router's own precedence rule. So the corrected behaviour is at least as correct as the old one in every reachable case.

Everything else is behaviour-preserving.

How it was verified

  • internal-packages/run-store: 69 files, 379 tests pass. The corpus is the regression gate for this refactor. 67 of the 68 pre-existing test files are byte-identical; the one that differs (runOpsStore.mixedResidency.test.ts) changes only // comments.
  • internal-packages/run-engine: 12 files, 69 tests pass — every file that constructs the router, exercised at runtime.
  • The @ts-expect-error onLegacyRead lock still fires. tsconfig.build.json excludes *.test.ts, so a green typecheck does not cover it. A scratch probe confirmed tsc still reports TS2353 for onLegacyRead and no error for the three real options.
  • All 48 construction sites outside the package compile unchanged. tsconfig.check.json also excludes *.test.ts, so the 25 webapp test files were checked with the test exclusion dropped and compared against the same check on the base commit: 614 errors before, 614 after, zero present in one and not the other. Those 614 are pre-existing in never-typechecked test files.
  • typecheck passes for run-store, run-engine and webapp. knip reports nothing in run-store.

Also

Refreshes the sixteen stale runOpsStore.ts line references in runOpsStore.mixedResidency.test.ts, each verified against the symbol it names.

Notes for the reviewer

  • The riskiest possible mistake in this diff is a fan-out passing the wrong order — the compiler cannot catch it, because both orders are readonly ShardKey[]. The five #precedence sites are #findRunsOpen, findRunsByIdempotencyKeys, #collectManyWaitpoints, findManyTaskRunWaitpoints and findManyWaitpointTags. Those are the lines worth the closest read.
  • Four sites previously derived "the other store" by object identity (home === this.#new ? ...). They now compare keys. The two are equivalent: in single-database mode both keys map to the same store object, and when the stores are distinct, identity and key comparison agree.
  • No changeset and no .server-changes note: the package is internal and the one behaviour change is unreachable in production, so a release note would tell a user nothing.
  • Two CI checks fail for reasons that predate this branch and reproduce on the base commit: lint (~16 unknown react/* rules make .oxlintrc.json fail to parse, which disables oxlint entirely — including the two trigger-runops fences) and knip (unrun, an unused devDependency on the default branch). Both want their own fix.

RoutingRunStore held two named store fields and took its routing policy
from the order the statements happened to run in. It now holds a
Map<ShardKey, RunStore>, and the three policies are readable data:

- #probeOrder — sequential probe for a lookup with no routable id; the
  first non-null result wins and the last entry owns the not-found throw
- #precedence — ascending authority for a merge, so the highest-authority
  shard wins a duplicate id
- #idlessRouteShard / #idlessWaitpointShard — the two id-less defaults,
  which differ by role and were previously two unrelated literals

The two orders are the reverse of one another, which is why they are
separate fields: some sites observe the result-array order, others only
the winner of a duplicate.

Four helpers absorb the twenty-six hand-written fan-outs (#probeFirst,
#fanOut, #fanOutPartitioned, #shardsExcept), and #shardKeyOf replaces the
inline residency-to-store ternaries. The constructor keeps its exact
options type, so every existing call site compiles unchanged and the
@ts-expect-error onLegacyRead lock keeps firing.

One behaviour changes. findManyTaskRunWaitpoints merged its edge rows
NEW-first into a last-wins dedupe, so a duplicate edge id resolved to the
LEGACY row — the opposite of the rule every sibling merge follows, and of
what dedupeEdgesById's own comment claimed. It now resolves NEW-wins, with
a test pinning it. The case needs one edge id present on both stores at
once, which is the drain-mirror window; the drain seam is removed, so it
is unreachable in practice.

Also refreshes the sixteen stale runOpsStore.ts line references in
runOpsStore.mixedResidency.test.ts.
@changeset-bot

changeset-bot Bot commented Aug 21, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 4d29a86

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 09f778da-96db-4c59-8636-faa770d6ac8a

📥 Commits

Reviewing files that changed from the base of the PR and between bb5cf6d and 4d29a86.

📒 Files selected for processing (1)
  • internal-packages/run-store/src/runOpsStore.mixedResidency.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal-packages/run-store/src/runOpsStore.mixedResidency.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (32)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (16, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (17, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (4, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (11, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (20, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (24, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (22, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (21, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (19, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (1, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (14, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (13, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (23, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (9, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (15, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (3, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (18, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (10, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (12, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (2, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (6, 24)
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (1, 2)
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (2, 2)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: internal / 🧪 Unit Tests: Internal
  • GitHub Check: runops-guard / runops-guard
  • GitHub Check: fk-cascade-guard / fk-cascade-guard
  • GitHub Check: code-quality / code-quality
  • GitHub Check: Analyze (javascript-typescript)

Walkthrough

RoutingRunStore now uses a keyed shard map with configurable probe order, merge precedence, and id-less routing defaults. Shared helpers handle shard classification, probing, fan-out, partitioning, deduplication, and count aggregation. Run, batch, snapshot, waitpoint, edge, and tag operations use these helpers. New tests validate probing, primary reads, merge precedence, not-found handling, and id-less routing. Mixed-residency test comments were updated.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring check was indeterminate for this PR — some files could not be analyzed in time. Not blocking.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description clearly explains the refactor, behavior change, testing, compatibility, and review risks, but it omits the template checklist, issue reference, and screenshots section.
Title check ✅ Passed The title clearly and concisely identifies the main refactor to store RoutingRunStore stores in a keyed shard map.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/routing-run-store-shard-map-tri-13426

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.

@d-cs d-cs self-assigned this Aug 21, 2026
coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

d-cs added 2 commits August 21, 2026 15:28
…ocstring

The docstring said "In single-DB both stores are the same, so routing is a
no-op passthrough". That has not been true since the injecting seam started
returning a bare PostgresRunStore when the split is off — this class is not
constructed at all in single-DB.

The stale sentence made the counting fan-outs look like they could double
count one store. State the actual invariant instead: every shard is a
distinct database, enforced by the seam and by the fail-closed boot sentinel,
and two keys resolving to one store would double count.

Comment-only. Also re-anchors the line references in
runOpsStore.mixedResidency.test.ts, which the docstring shifted.
…comments

The comments cited runOpsStore.ts line numbers. They rotted three times
inside this PR alone — a docstring edit or a formatter pass moves every one
of them, and a stale pointer is worse than none.

Keeps the symbol names, which are what actually locate the code and survive
edits. Matches the convention unblockRouteCatalog states for itself:
anchored by method name, never a line number.

Comment-only.
@d-cs
d-cs marked this pull request as ready for review August 21, 2026 15:55
@d-cs
d-cs merged commit aa9b888 into main Aug 21, 2026
52 checks passed
@d-cs
d-cs deleted the refactor/routing-run-store-shard-map-tri-13426 branch August 21, 2026 16:05
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