Skip to content

fix(alerts): maintenance window revision guard, locked org cap, shared weekdays - #2343

Merged
Makisuo merged 3 commits into
mainfrom
fix/alerts-maintenance-window-guards
Nov 7, 2026
Merged

Makisuo merged 3 commits into
mainfrom
fix/alerts-maintenance-window-guards

Conversation

@Makisuo

@Makisuo Makisuo commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Three fixes from the #2323 review (maintenance windows).

What changed

  • F6, stale materialize guard.
    • Problem: the sync checked that a window was unchanged by comparing updatedAt in milliseconds. A materialize pass that read the window in the same millisecond as an edit could rewrite its silences from the old schedule.
    • Fix: windows now carry a revision. It is 1 on create, and every update (enable and disable included) bumps it. The sync locks the row at the revision it read.
    • Migration: revision integer NOT NULL DEFAULT 1.
  • F7, racing org cap.
    • Problem: the 100-window cap was counted before the insert transaction, so two concurrent creates could both pass.
    • Fix: create now takes an org-scoped advisory lock (pg_advisory_xact_lock(hashtext(org), hashtext('alert_maintenance_windows'))) and recounts inside the transaction. The lock uses two keys, so it never contends with the rule lock. The count before the transaction is gone.
  • F8, duplicated weekday literals. The domain exports MAINTENANCE_WEEKDAYS, and MaintenanceWeekday is built from it. The MCP copy is deleted, and the create tool imports the domain array.

Verification

  • F6: a new sync test runs an edit and a materialize pass in the same millisecond. The pass holding the old revision writes nothing, and the next pass writes the new schedule. With the old updatedAt guard the test fails ("expected 4 to equal +0").
  • F7: a new test runs two creates at once with 99 windows in the org. Exactly one succeeds and the org ends at 100. Against the main code both succeed and the test fails.
    • Limit: the test runs on PGlite, which runs one transaction at a time, so it pins the recount inside the transaction but cannot prove the lock itself. The lock is the standard form used by the ingest-rule, source and scrape-target creates.
  • Suites:
    • backend maintenance window, silences and anomaly-maintenance suites: 44 tests;
    • org-scoped tables: 7;
    • db parity and migrations: 17;
    • domain maintenance windows: 24;
    • MCP maintenance window tools and the registry contract: 15.
  • Typecheck is clean in domain, db, backend, ai and api. Both oxlint configs are clean.

Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.

The sync locked the window row and checked updatedAt, which is epoch ms, so a
pass that read the window in the same millisecond as an edit could rewrite its
silences from the superseded schedule. Every write to the window row now bumps
an integer revision and the guard compares that.
createWindow counted the org's windows before its insert transaction, so two
concurrent creates could both pass the 100-window cap. The count now runs inside
the transaction after an org-scoped advisory lock of its own key.
@coderabbitai

coderabbitai Bot commented Nov 7, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 26 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: cb136b1f-4fb8-4e54-98d7-3491ab7dc444
Commits

Reviewing files that changed from the base of the PR and between 2cc3cac and d8eee5d.

Files selected for processing: 10

  • apps/ai/src/mcp/lib/maintenance-windows.ts
  • apps/ai/src/mcp/tools/create-maintenance-window.ts
  • packages/backend/src/services/alerts/AlertMaintenanceWindowsService.sync.test.ts
  • packages/backend/src/services/alerts/AlertMaintenanceWindowsService.ts
  • packages/backend/src/services/alerts/alert-maintenance-window-sync.ts
  • packages/backend/src/services/alerts/alert-persistence.ts
  • packages/db/drizzle/20261107115926_alert_maintenance_window_revision/migration.sql
  • packages/db/drizzle/20261107115926_alert_maintenance_window_revision/snapshot.json
  • packages/db/src/tables/alerts.ts
  • packages/domain/src/http/alert-maintenance-windows.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@maple-review-bot

maple-review-bot Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Maple review

🟢 Confidence 9/10 · safe to merge. The revision guard and recount are pinned by new tests; the advisory lock itself is untested because PGlite serialises transactions.
no findings · tests partial −1 · risk medium

Replaces the millisecond updatedAt guard with a revision column bumped by every write, moves the 100-window cap count inside an org-scoped advisory lock, and de-duplicates the weekday literals. Safe to merge.

  • alert_maintenance_windows.revision bumps on every update; syncWindow writes only at the revision it read
  • create counts the per-org cap inside pg_advisory_xact_lock(org, 'alert_maintenance_windows')
  • MAINTENANCE_WEEKDAYS moves to @maple/domain/http; the MCP copy is deleted

Before merge

d8eee5d · What was checked, coverage and telemetry details are on the Maple / review check. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@Makisuo
Makisuo merged commit 7a32c73 into main Nov 7, 2026
38 checks passed
@Makisuo
Makisuo deleted the fix/alerts-maintenance-window-guards branch November 7, 2026 16:50
@maple-review-bot

Copy link
Copy Markdown

Merged with 1 step from "Before merge" still open. If it is done, tick it on the review comment; if not, now is the time.

  • Migration · Check it is safe on production data and with the code still deployed

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