Skip to content

fix(cloud): recalibrate the relay monitor's exhausted-retry freeze to a measured bar - #18569

Merged
Jinwoo-H merged 3 commits into
mainfrom
relay-monitor-exhausted-bar
Sep 4, 2026
Merged

Jinwoo-H merged 3 commits into
mainfrom
relay-monitor-exhausted-bar

Conversation

@Jinwoo-H

@Jinwoo-H Jinwoo-H commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​20 $\color{#cf222e}{\Huge{\mathbf{−}}}$​11 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​9
Prod 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​35 $\color{#cf222e}{\Huge{\mathbf{−}}}$​26 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​9

ELI5

Before any production cell roll, a 15-minute health check has to pass. One of its rules says "zero database lock give-ups allowed". Since #18521 made the relay give up on a contended lock quickly instead of waiting a full second, the fleet always has a few dozen give-ups per five minutes, so the health check fails at minute one every time and nobody can roll the cells. This PR sets that rule to a number the healthy fleet actually stays under while still catching the real incident.

What Changed

  • cloud/apps/relay-ops/src/incident-monitor.ts: relayPostgresRetryExhausted 0 → 300 per five minutes, with the measurement basis in the comment; adjacent comments that referred to the zero bar updated.
  • cloud/apps/relay-ops/src/incident-monitor.test.ts: the "freezes on signal(1) with threshold 0" test becomes "220 is green, 301 freezes with threshold 300". Reverting the constant to 0 fails it.
  • cloud/docs/relay-incident-monitor.md: threshold table row and an implementation-log entry with the basis.

Why

Every cloud-monitor-relay-production dry-run since the director deploy at 22:12Z on 2026-09-03 froze on this bar (runs 33746931085, 33747826907, 33812032190). cloud-deploy-relay-production-same-cap requires a fresh green dry-run, so the 23 GCE cells are stuck on the pre-#18521 image with the 1 s lock wait, which is exactly what keeps sqlLatencyMsMax pinned at 1.0–1.2 s on every cell (see #18565).

Measured from jsonPayload.event="orca_relay_postgres_transaction_exhausted" fleet-wide (director + cells), summed per five minutes, 2026-09-03T03Z to 2026-09-04T02Z:

window five-minute windows p50 p90 max
all 236 (236 non-zero) 15 98 220
quiet hours 03Z–08Z 17 2 9 36
pre-#18521 daytime 167 10 25 87
post-#18521 (director) 68 42 147 220
2026-08-23 incident peak 467

300 clears every healthy window measured and stays below the incident shape. relayPostgresRetries: 300 remains the ~10x discriminator for incident-class contention and is unchanged. User-facing /v1/assign 503 share did not move with #18521 (13.9% old image vs 12.3% new, same evening), so exhaustion is not a proxy for user harm.

Post-roll the cells will also fail fast, so the fleet-wide exhausted count will rise further while retries fall; the bar leaves room for that (roughly 2.5x the current p90).

Not in this PR

cloud/infra/terraform/relay-observability.tf has google_monitoring_alert_policy.relay_postgres_retry_exhausted with the same > 0 threshold on both the Cloud Run and GCE conditions, so that alert has been firing continuously since #18521. It should be recalibrated to the same 300 per 300 s, but that is a Terraform apply on the observability root and belongs in its own change.

Linked Issue

Fixes # (none filed; unblocks the cell roll described in the #18565 report)

Visual Proof

N/A: threshold constant, test, and docs only.

Testing

  • cloud/apps/relay-ops: vitest run 12 files / 76 passed; tsc --noEmit clean; mutation check (bar back to 0) fails the new test.
  • Real verification is the next dry-run dispatch: it should now run the full 15 minutes instead of freezing at minute one.

Agent skill upstream boundary

  • Not applicable

Checklist

  • Small and focused
  • ELI5 and why included
  • N/A visual proof with reason
  • Self-reviewed
  • Cross-platform / SSH: N/A (cloud ops tooling)
  • Local lint/typecheck/test pass for the touched package

… a measured bar

The pre-drain dry-run froze at minute one on relayPostgresRetryExhausted: 0
in every run since #18521 reached the director, blocking the cell roll that
carries the same fix. #18521 made contended request-path waiters fail fast
(500 ms) instead of succeeding slowly, so exhaustion is now a steady
contention rate: 236/236 five-minute windows non-zero over 23 h; post-#18521
p50 42 / p90 147 / max 220 fleet-wide; the 2026-08-23 incident peaked at 467.
300 clears every measured healthy window and stays under the incident shape.
/v1/assign 503 share was unchanged by #18521 (13.9% vs 12.3%).
@coderabbitai

coderabbitai Bot commented Sep 4, 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: Team

Run ID: 983f6fa4-8432-4482-8c07-630873e6617b

📥 Commits

Reviewing files that changed from the base of the PR and between 50ce3f8 and 011c7ac.

📒 Files selected for processing (3)
  • cloud/apps/relay/src/assignment-store.ts
  • cloud/apps/relay/src/cell-inventory-lock-census.test.ts
  • cloud/apps/relay/src/cell-inventory-lock-contention.test.ts

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


📝 Walkthrough

Walkthrough

The incident monitor threshold for relay.postgres_retry_exhausted increased from 0 to 300 retries per five minutes. Tests now accept 220 and 300 retries and freeze at 301. Comments and incident documentation describe the recalibrated threshold, lock-timeout behavior, retry measurements, dry-run freezes, and /v1/assign responses.

Merge Risk: ⚪ Minimal · up to 011c7

The relay monitor now permits up to 300 exhausted PostgreSQL retries per five minutes and freezes above that level. The updated boundary coverage and aligned documentation leave no identified merge-blocking risk.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed and covers the change, rationale, measurements, testing, scope, and pending dry-run verification. However, the required Linked Issue section is incomplete because it states… Link the applicable issue in the Linked Issue section. If no issue exists, create or obtain the required tracking issue before merging, then replace "Fixes # (none filed)" with the issue reference.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: recalibrating the relay monitor's exhausted-retry freeze threshold.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 5…
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.
Full details: Description check

Explanation

The description is detailed and covers the change, rationale, measurements, testing, scope, and pending dry-run verification. However, the required Linked Issue section is incomplete because it states "Fixes # (none filed)".

  • Fix all pre-merge checks with AI
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

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

🧹 Nitpick comments (1)
cloud/apps/relay-ops/src/incident-monitor.test.ts (1)

135-139: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover the exact 300-event boundary.

The freeze condition is “over 300,” but this test covers only 220 and 301. A regression from > to >= would pass while freezing at exactly 300. Add a 300-event sample and assert green before the 301-event case.

Proposed test addition
+    const atLimit = healthySample()
+    atLimit.sources['relay-logs']!.signals['relay.postgres_retry_exhausted'] = signal(300)
+    expect(evaluateIncidentSample(atLimit, startedAt).status).toBe('green')
+
     const incident = healthySample()
     incident.sources['relay-logs']!.signals['relay.postgres_retry_exhausted'] = signal(301)

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Team

Run ID: a863f318-fd1d-40df-bde5-87083d2836b1

📥 Commits

Reviewing files that changed from the base of the PR and between 97e5eb8 and 185464c.

📒 Files selected for processing (3)
  • cloud/apps/relay-ops/src/incident-monitor.test.ts
  • cloud/apps/relay-ops/src/incident-monitor.ts
  • cloud/docs/relay-incident-monitor.md

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

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

ℹ️ Minor suggestions only — the threshold change itself is sound and well-tested; two consistency items worth a look.

Reviewed changes

  • Threshold recalibration — relayPostgresRetryExhausted moves from 0 to 300 per five minutes, with the measurement basis (fleet-wide window stats plus the 467 incident peak) captured in the comment and adjacent comments updated.
  • Test rewrite — the "freezes on a single exhausted transaction" test becomes a boundary test pinning 220/300 green and 301 freeze with threshold: 300, so reverting the constant fails it.
  • Docs — the threshold table row and implementation-log entry are updated to over 300.

The semantics check out: relay.postgres_retry_exhausted is a five-minute delta sum (relayFiveMinuteDeltaSignal), and the rule uses max (value > threshold), so 300 is green and 301 freezes — matching the test and the relayPostgresRetries boundary style.

ℹ️ Sibling cloud/apps/relay comments still teach the zero-tolerance contract

The change redefines the exhausted-retry signal from "any terminal failure freezes the gate" to "over 300/5min freezes the gate", but three comments in the relay package (not the relay-ops package this PR touches) still describe the old contract:

  • cloud/apps/relay/src/assignment-store.ts:341 — "one terminal failure freezes the incident gate."
  • cloud/apps/relay/src/cell-inventory-lock-census.test.ts:174 — "relayPostgresRetryExhausted freezes the incident gate at zero."
  • cloud/apps/relay/src/cell-inventory-lock-contention.test.ts:303 — "the incident monitor freezes at zero exhausted transactions."
Technical details
# Update stale zero-tolerance comments

## Affected sites
- cloud/apps/relay/src/assignment-store.ts:341 — `pool-default` rationale cites a single terminal failure freezing the gate.
- cloud/apps/relay/src/cell-inventory-lock-census.test.ts:174 — "freezes the incident gate at zero".
- cloud/apps/relay/src/cell-inventory-lock-contention.test.ts:303 — "freezes at zero exhausted transactions".

## Required outcome
- Reword each to reflect the 300-per-5min bar (terminal failures now inflate the exhausted-retry counter toward the 300/5min bar, not freeze on a single failure), so the rationale stays accurate.

ℹ️ Alert policy still pages at > 0

google_monitoring_alert_policy.relay_postgres_retry_exhausted (both Cloud Run and GCE conditions) still uses threshold_value = 0, so it keeps paging on the steady contention rate until the follow-up Terraform change lands. The PR body already flags this as out of scope — worth confirming it is tracked as a real sequencing item, since it will keep firing continuously in the meantime.

Technical details
# Recalibrate the exhausted-retry alert policy

## Affected sites
- cloud/infra/terraform/relay-observability.tf:447 and :469 — `threshold_value = 0` on the exhausted-retry conditions (ALIGN_SUM over 300s mirrors the monitor's five-minute delta).

## Required outcome
- Recalibrate to 300 per 300s in its own observability-root change.

## Open questions for the human
- Is a tracking issue needed so the continuously-firing alert is not lost?

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@Jinwoo-H

Jinwoo-H commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Pullfrog item 1 done in 011c7ac: the three relay-package comments now describe the bounded 300/5min bar. Item 2 (Terraform alert policy at > 0) is intentionally left for its own observability-root change; noted in the PR body.

@Jinwoo-H
Jinwoo-H merged commit 4101505 into main Sep 4, 2026
24 checks passed
@Jinwoo-H
Jinwoo-H deleted the relay-monitor-exhausted-bar branch September 4, 2026 04:20
enrick-dev pushed a commit to enrick-dev/orca that referenced this pull request Oct 15, 2026
… a measured bar (stablyai#18569)

* fix(cloud): recalibrate the relay monitor's exhausted-retry freeze to a measured bar

The pre-drain dry-run froze at minute one on relayPostgresRetryExhausted: 0
in every run since stablyai#18521 reached the director, blocking the cell roll that
carries the same fix. stablyai#18521 made contended request-path waiters fail fast
(500 ms) instead of succeeding slowly, so exhaustion is now a steady
contention rate: 236/236 five-minute windows non-zero over 23 h; post-stablyai#18521
p50 42 / p90 147 / max 220 fleet-wide; the 2026-08-23 incident peaked at 467.
300 clears every measured healthy window and stays under the incident shape.
/v1/assign 503 share was unchanged by stablyai#18521 (13.9% vs 12.3%).

* test(cloud): pin the exhausted-retry freeze boundary at exactly 300

* docs(cloud): reword relay comments that still described the zero exhausted-retry bar
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.

1 participant