Repository navigation
fix(relay): lock only the target cell row, last and NOWAIT, in the rehome commit - #22449
Conversation
…home commit The idle-rehome commit runs on the source cell. From an Asia cell each statement is a cross-region round trip, and the transaction locked every relay_cells row plus every runtime, capability and safety row before about twenty more statements, so each Asia-source rehome held the whole fleet's cell rows for ~3.6 s and every reconnect, renewal and placement queued or timed out behind it. The commit now reads the cell inventory and the runtime, capability and safety tables unlocked, keeps the control and worker rows locked (now NOWAIT), and takes one cell lock: the target row, in a single statement that locks it NOWAIT, re-checks enabled, general admission and capacity, and reserves the units, issued as the last statement before COMMIT. A target that changed admission, filled up, or is locked by another writer rolls the whole commit back and answers deferred (candidate-ineligible). The hold is sampled under a site label, so cellInventoryHoldMsMax still sees rehome holds and rehomeTargetRowHoldMsMax reports them apart. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (11)
Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review. 📝 WalkthroughWalkthroughThe idle-rehome commit now reads candidate inventory without locks and reserves the target cell through a final NOWAIT row lock. It returns a deferred outcome if the target is no longer eligible or another writer holds its row. Lock-hold metrics now identify the lock site and report target-row hold duration and count. Tests cover lock order, target-row contention, deferral, and measured holds. The operations guide documents the updated commit behavior and metrics. Priority: ➖ Normal 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 15.79% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 19 functions across 10 files. (1 skipped: 1 unsupported.)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
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. Comment |
There was a problem hiding this comment.
ℹ️ No critical issues — one observability gap and a test-robustness note.
Reviewed changes
- Rehome commit drops the fleet-wide cell lock.
commitIdleRegionalRehomenow reads the cell inventory, runtime, capability and safety tables unlocked, keeps the global control and worker rows locked but NOWAIT, and takes its onlyrelay_cellslock — the target row plus itsrelay_cell_admissionrow — as the last statement before COMMIT (reserveRegionalRehomeTargetRow). - Target re-check folds into the locking statement. One
WITH ... FOR UPDATE OF cell, admission NOWAIT ... UPDATEre-checksenabled,admission_state = 'general'and request capacity, then adds the reservation; any miss rolls the whole transaction back todeferred/candidate-ineligibleand logsorca_relay_idle_rehome_target_deferred. - Lock-scoped telemetry.
CellInventoryHoldSamplesgains a hold-site label and the fieldscellInventoryHoldMaxSite,rehomeTargetRowHoldMsMaxandrehomeTargetRowHolds;RelayLockOptionsgainsholdSiteandlockClauseInStatement, withlockedStatement()centralizing theFOR UPDATE[ NOWAIT]suffix. - Coverage and docs. A new lock-order test pins control → worker → host rows → target row last/NOWAIT; Postgres tests measure the ~one-round-trip hold under injected latency and the admission-flip / busy-row deferrals; the lock census and
orca-relay-operations.mdare updated.
ℹ️ New hold fields are emitted in the runtime-metrics log but not registered as Cloud Monitoring metrics
index.ts spreads consumeRelayCellInventoryHold(database) into the orca_relay_runtime_metrics event, so rehomeTargetRowHoldMsMax and rehomeTargetRowHolds reach the log. But google_logging_metric.relay_snapshot is built with for_each = merge(local.relay_runtime_metrics, ...) in cloud/infra/terraform/relay-observability.tf, and neither field is in that map, so no metric series is created for them. The Asia rollout proof reads the raw log event, so it can still see them, but any Cloud Monitoring alert or dashboard on these two fields would find nothing.
Technical details
# Register the rehome target-row hold fields, or document them as log-only
## Affected sites
- `cloud/apps/relay/src/index.ts:97` — emits the new fields inside `orca_relay_runtime_metrics`.
- `cloud/infra/terraform/relay-observability.tf:52-105` — `local.relay_runtime_metrics`; `rehomeTargetRowHoldMsMax` and `rehomeTargetRowHolds` are absent.
- `cloud/infra/terraform/relay-observability.tf:300-309` — `relay_snapshot` logging metric is created only for keys in `local.relay_runtime_metrics`.
- `cloud/docs/orca-relay-operations.md:495-510` — describes the fields as runtime metrics.
## Required outcome
- Either add numeric entries for `rehomeTargetRowHoldMsMax` and `rehomeTargetRowHolds` to `local.relay_runtime_metrics`, or state in the docs that these fields are log-only context and the alert remains on `cellInventoryHoldMsMax` (with `cellInventoryHoldMaxSite` naming the holder).
- `cellInventoryHoldMaxSite` is a string, so it cannot be a `value_extractor` on the DISTRIBUTION metric; it would need a label extractor, which the shared `relay_snapshot` resource deliberately avoids (adding labels recreates the live metrics). Log-only is the reasonable choice for it.
## Open questions for the human
- Is a future alert planned on `rehomeTargetRowHoldMsMax`, or is `cellInventoryHoldMsMax` plus the site label sufficient?ℹ️ Nitpicks
regional-rehome-target-row-lock-postgres.test.ts:242bounds the busy-row commit at40 * STATEMENT_DELAY_MSwhile the commit itself is ~37 delayed trips, leaving only a few hundred ms of headroom and coupling the assertion to the current statement count. A bound expressed against the injected delay independently of the statement count (or an assertion that nolock_timeoutwait occurred) would be less brittle on a slow CI Postgres.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
The target-row statement now carries FOR UPDATE ... NOWAIT unless the dialect is explicitly SQLite, so a wrapper that omits the optional dialect can no longer run the reservation unlocked. Test wrappers and the fault injection entry forward the dialect they wrap. The latency test also probes the admission and region tables at every round trip; only the target's admission row may be locked, and only before COMMIT. The runbook notes that an Asia-sourced commit holds the rehome control row for about 6.5 s, so a pause that fails once is retried. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

ELI5
When the relay moves an idle desktop host to a closer region, the cell doing the move used to lock the capacity row of every cell in the fleet for the whole move. From an Asia cell each database step is slow, so that lock stayed on for about 3.6 seconds. Anything else that needed any cell's row timed out. Now the move locks only the one cell it is moving the host to. It takes that lock at the very end, so the lock is held for about one database round trip.
What Changed
Before. An idle rehome that started on an Asia cell locked all
relay_cellsrows. It also locked every row in the runtime, capability and safety tables. Then it ran about 20 more statements at about 175 ms each before COMMIT. For those ~3.6 s, hosts anywhere in the fleet failed. Reconnects, control renewals, sticky refreshes and placements hit their 0.5-1 s lock timeouts and retried. The 2026-09-22 investigation tied 78% of all relay lock-timeout cancels over 49 hours to these holds.After. No other cell's row is locked at any point. The target row is held from its statement to COMMIT. In a test that adds 150 ms to every statement, that measured 169-176 ms.
Mechanism. The change is in
commitIdleRegionalRehomeandstartRegionalRehomeCandidateinassignment-store.ts.deferredwith the existingcandidate-ineligiblereason, so the director moves on to its next candidate. The cell also logsorca_relay_idle_rehome_target_deferredwith the cause.cellInventoryHoldMsMax, so the planned alert on that field still sees rehome holds. New runtime-metric fieldscellInventoryHoldMaxSite,rehomeTargetRowHoldMsMaxandrehomeTargetRowHoldstell rehome holds apart from full-inventory holds.Wire. Nothing changes between director and cell. The deferral uses a reason the director already accepts. The cell's status body is untouched, and the new fields appear only in the runtime-metrics log line.
Why
Alternatives considered:
relay_cellslock. That leaves fleet-wide locks on the runtime, capability and safety tables, which cell heartbeats write. None of those locks protected a decision, so all four are gone.Known limit. The connection hard-cap read is still a plain read that the target write does not re-check. A concurrent placement can therefore overshoot the cap by a few hosts. The control row still serialises rehome commits against each other.
Rollout. This needs a new cell image on the Asia cells, because they run the commit. The operator will roll it out as a separate Asia cell wave, one cell at a time. US-source commits were never the problem, and they pick this up with the next US image. This PR deploys nothing.
Linked Issue
No tracked issue. This is step 2(a) of the relay lock-convoy fix plan from the 2026-09-22 investigation.
Visual Proof
N/A. Server-side locking change with no UI.
Testing
Run in
cloud/apps/relaywithpnpm testagainst a freshpostgres:17container. Result: 102 files and 918 tests passed. The one skipped test needs a separate database on port 55440. Theidle-regional-rehome-storesuite also passed against that database, 17 of 17.pnpm typecheckpasses. The Postgres project also passed three times in a row, each on a fresh database.New tests:
regional-rehome-target-row-lock-postgres.test.ts). Every statement, BEGIN and COMMIT included, gets 150 ms of client-side delay. Before each of the 37 round trips, a second connection triesFOR UPDATE NOWAITon the source, target and a bystander cell in all four tables. Every row except the target cell row is lockable at every trip. The target cell row is locked only at the trip before COMMIT, and the hold sample is between 150 and 300 ms.deferred/candidate-ineligibleand leaves nothing behind: no attempt, no migration, no reservation. The host stays on its source, and the region-correction outcome report shows nothing for the target.assignment-store-lock-order.test.ts). The first rehome case pins the order control, worker, host rows, then the target cell row last. It pins NOWAIT on the control, worker and target rows, and checks that no other cell-table row is locked.Each new Postgres test was checked by breaking the code on purpose. Putting the inventory lock back, removing NOWAIT, or removing the admission re-check each makes the matching test fail. Removing NOWAIT or putting the inventory lock back also fails the lock-order test.
One existing test changed. The commit no longer locks the safety table, so "rechecks fleet safety under locks" now commits the safety change before the commit starts.
Review
Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
This is relay server code only, with no desktop, mobile, SSH or platform impact. Mixed director and cell versions stay compatible. An old director reads the new deferral as a known reason, and a new cell answers old directors unchanged.
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)