Skip to content

Bump migration timeout to 4h for hypertable backfills - #1113

Merged
Asherlc merged 9 commits into
mainfrom
Asherlc/fix-deploy-workflow
May 11, 2026
Merged

Asherlc merged 9 commits into
mainfrom
Asherlc/fix-deploy-workflow

Conversation

@Asherlc

@Asherlc Asherlc commented May 10, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Bumps migration_timeout_seconds in .github/workflows/deploy-web-stack.yml from 3300 (55min) to 14400 (4h).
  • Logs the incident in docs/production-incident-baseline.md.

Why

Deploy Web run 25609852303 failed: the prod migration step was killed at the 55-min cap while applying drizzle/0018_migrate_body_measurements_to_metric_stream.sql — a single-transaction backfill that copies all fitness.body_measurement rows into fitness.metric_stream and rebuilds dependent indexes/views on a TimescaleDB hypertable. The migration was still progressing when the workflow killed the container, so the transaction rolled back and prod stayed in the pre-migration state. Re-running CI alone wouldn't have unblocked the deploy.

Prod host itself was healthy throughout (SSH banner < 2s, dofek.asherlc.com/healthz returned 200 directly against the origin during the failure). The cap was the constraint, not the host or the migration.

Test plan

  • Trigger a prod deploy and confirm the migration step now has time to complete.
  • If the migration still exceeds 4h, restructure it (split DDL from data; batch UPDATE/DELETE/INSERT with --no-transaction keyed on hypertable chunks) rather than bumping the timeout again — guidance noted inline in the workflow.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores

    • Increased deployment workflow timeout to 4 hours to accommodate long-running database migrations.
    • Added a durable WAL retention guardrail to limit retained WAL per replication slot.
  • Bug Fixes / Reliability

    • Removed prior transient DB activity polling approach; revised migration guidance to avoid timeout-induced rollbacks.
  • Documentation

    • Added three production incident entries (2026-05-09, 2026-05-10) describing deploy-time migration failure and host outage, with mitigations and risks.

Review Change Stack

Production deploy run 25609852303 was killed at 3300s applying
0018_migrate_body_measurements_to_metric_stream.sql, a single-transaction
hypertable backfill that legitimately needs more than 55min on prod.
The migration was still making progress when the workflow's hard cap
fired, so the container was force-killed and the transaction rolled
back, leaving prod unchanged and unable to roll new code.

Sizes the timeout to fit the slowest known migration with headroom and
documents that future migrations exceeding the budget should be
restructured rather than bumping the timeout again.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 10, 2026 01:36
@coderabbitai

coderabbitai Bot commented May 10, 2026 •

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Raises CI migration timeout 3300→14400s with inline guidance, caps per-slot WAL retention via max_slot_wal_keep_size=4GB in the DB service, and appends three production incident entries describing disk-full replication slots, a wedged host, and a migration timeout.

Changes

Production Timeout and WAL Guardrail

Layer / File(s) Summary
DB config: WAL retention
deploy/stack.yml
Adds max_slot_wal_keep_size=4GB to the db service command arguments and comments documenting WAL retention per logical replication slot.
Workflow: migration timeout & comments
.github/workflows/deploy-web-stack.yml
Updates migration_timeout_seconds from 3300 → 14400 in the Run migrations step and adds inline comments recommending PostgreSQL monitoring views/extensions for observing long migrations.
Incident documentation
docs/production-incident-baseline.md
Adds three incident entries: 2026-05-10 disk-full caused by inactive PeerDB replication slots (~26GB WAL) and recovery/guardrail notes; 2026-05-10 host wedged after rescue-mode reboot with observed IO/blocking symptoms; 2026-05-09 migration killed at 3300s while backfilling a hypertable with recorded mitigations including raising timeout to 14400s.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Asherlc/dofek#1102: Modifies the same deploy workflow’s "Run migrations" step (migration logging/streaming and migration run behavior).

Suggested labels

area/infra, type/bug, needs-tests

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly summarizes the main change (raising migration timeout to 4h) and uses imperative mood, is under 70 chars (53), and lacks trailing punctuation per requirements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 10, 2026 •

Copy link
Copy Markdown
Contributor

Storybook previews for 9b6e1d61 are ready:

This comment updates automatically on each PR push.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/deploy-web-stack.yml:
- Around line 415-420: The change increases migration_timeout_seconds to 14400
without first capturing the blocking SQL causing prior timeouts; instead revert
this timeout bump and instrument the migration to capture the first blocking
statement before any resilience-knob change: add diagnostics to the migration
job (e.g., query pg_stat_activity/pg_locks or enable log_lock_waits) to record
the exact blocking statement and transaction id when a lock/wait occurs, or
split/move the backfill out of deploy and run it as a separately-invoked
maintenance job; once the offending SQL is identified and fixed or the backfill
is split, only then consider adjusting migration_timeout_seconds if absolutely
needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2dcd84be-3922-4dd9-b186-f04cd5e607e6

📥 Commits

Reviewing files that changed from the base of the PR and between f17e27d and 5d64e52.

📒 Files selected for processing (2)
  • .github/workflows/deploy-web-stack.yml
  • docs/production-incident-baseline.md

Comment thread .github/workflows/deploy-web-stack.yml
CodeRabbit flagged that bumping the migration timeout without capturing
which SQL statement is actually running could hide lock/contention
failures behind a 4h delay. Adds periodic pg_stat_activity dumps to the
workflow log every 60s of migration elapsed time, plus a final snapshot
when the deploy-level timeout fires. Output includes wait_event_type
and wait_event so a stuck-on-lock case is distinguishable from genuine
data-volume slowness without waiting for the timeout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Review app is ready:

This environment runs on a dedicated Hetzner server for PR #1113 and updates on each push.

Copilot AI 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Captures: rescue+reboot dance moved prod from healthy to the same wedged
state staging had been in all session; ~20 swarm services on a cax11
2-core ARM box hit load >100 within minutes of any boot; soft reboots
and hard reset don't produce a stable host. Records the unknown swap
status, the symptom pattern shared with staging, and the realistic
recovery options (resize, trim stack, or both) so the next person has
context even if the live host stays unreachable.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/production-incident-baseline.md`:
- Around line 91-104: The two new incident entries that currently stop at the
"### Remaining Risk" heading need an added "### Follow-Up Work" section each:
for the 2026-05-10 entry and the 2026-05-09 entry, append a short bulleted
follow-up list that includes an owner (individual or team), a clear next action
(e.g., recover staging, investigate volume health, apply migrations
0018_metric_stream_location_point.sql and
0018_migrate_body_measurements_to_metric_stream.sql or roll-forward/rollback
plan), and a target date (use the dates requested where applicable); place these
directly after the existing "### Remaining Risk" paragraph for each incident so
the template fields (date, symptoms, impact, evidence, root cause, fix,
remaining risk, follow-up work) are complete.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d79b70f9-03a9-4802-a788-bc9de0b0d033

📥 Commits

Reviewing files that changed from the base of the PR and between 348b791 and 41858f4.

📒 Files selected for processing (1)
  • docs/production-incident-baseline.md

Comment on lines +91 to +104
### Remaining Risk

- We do not yet know whether the post-reboot wedge is a deterministic
consequence of the stack composition, or whether something specific to
the data volume (slow IO, near-full, corruption, etc.) is the proximate
cause. Resizing the host treats the symptom; if the underlying issue is
the volume, resize alone will not durably help.
- Staging has been wedged through the entire incident, so the deploy-time
validation surface is also down. Any fix attempted on prod cannot be
rehearsed on staging until staging is recovered.
- The pending `0018_metric_stream_location_point.sql` and
`0018_migrate_body_measurements_to_metric_stream.sql` migrations are
still blocked from applying. The deploy queue is effectively frozen.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add explicit ### Follow-Up Work sections for the new incident entries.

Both new entries stop at ### Remaining Risk. The incident template in this repo requires follow-up work to be captured explicitly. Add a short follow-up list (owner + next action + target date where possible) for 2026-05-10 and 2026-05-09.

As per coding guidelines, “After any production incident, deploy failure, infrastructure issue, database pressure event, CI/deploy outage, or operational debugging session, append a concise summary to docs/production-incident-baseline.md. Capture date, symptoms, user impact, evidence, root cause if known, fix or mitigation, remaining risk, and follow-up work.”

Also applies to: 158-169

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/production-incident-baseline.md` around lines 91 - 104, The two new
incident entries that currently stop at the "### Remaining Risk" heading need an
added "### Follow-Up Work" section each: for the 2026-05-10 entry and the
2026-05-09 entry, append a short bulleted follow-up list that includes an owner
(individual or team), a clear next action (e.g., recover staging, investigate
volume health, apply migrations 0018_metric_stream_location_point.sql and
0018_migrate_body_measurements_to_metric_stream.sql or roll-forward/rollback
plan), and a target date (use the dates requested where applicable); place these
directly after the existing "### Remaining Risk" paragraph for each incident so
the template fields (date, symptoms, impact, evidence, root cause, fix,
remaining risk, follow-up work) are complete.

Per CR feedback: the 30 lines of bash + nested heredoc'd psql were
rebuilding what pg_stat_statements (already in
shared_preload_libraries per stack.yml) and pg_stat_progress_*
already give us natively. SQL-level visibility into a slow migration
belongs in a query against the live db service, not in the deploy
workflow.

Keeps the migration_timeout_seconds=14400 bump and the migration
container's log streaming, which is the actual primary evidence
source.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/production-incident-baseline.md`:
- Around line 87-89: The doc currently gives conflicting statements about PR
`#1113`: it claims `pg_stat_activity` instrumentation from PR `#1113` remains valid
while elsewhere it notes periodic workflow snapshots were reverted; update the
observability statement so there is a single consistent source of truth — either
state that both `migration_timeout_seconds=14400` and the `pg_stat_activity`
instrumentation in PR `#1113` remain valid and can be exercised once the host is
stable, or explicitly note that `pg_stat_activity` instrumentation was impacted
by the revert of periodic workflow snapshots and is not currently usable;
reference `migration_timeout_seconds`, `pg_stat_activity`, PR `#1113` and the
periodic workflow snapshots revert in the updated sentence so operators see the
correct, non-conflicting status.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9149e10b-0879-40fc-87a0-b1b0d22273b4

📥 Commits

Reviewing files that changed from the base of the PR and between 41858f4 and fa0f5f2.

📒 Files selected for processing (2)
  • .github/workflows/deploy-web-stack.yml
  • docs/production-incident-baseline.md

Comment on lines +87 to +89
The previously-merged `migration_timeout_seconds=14400` bump and
`pg_stat_activity` instrumentation in PR #1113 are unaffected and remain
valid groundwork — but cannot be exercised until the host is stable.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Correct the PR #1113 observability statement to match current workflow behavior.

Line 87 says pg_stat_activity instrumentation in PR #1113 remains valid, but the next entry documents that periodic workflow snapshots were reverted. Keep one source of truth here so operators are not told both things.

Suggested edit
-The previously-merged `migration_timeout_seconds=14400` bump and
-`pg_stat_activity` instrumentation in PR `#1113` are unaffected and remain
-valid groundwork — but cannot be exercised until the host is stable.
+The previously-merged `migration_timeout_seconds=14400` bump in PR `#1113`
+remains valid groundwork — but cannot be exercised until the host is stable.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
The previously-merged `migration_timeout_seconds=14400` bump and
`pg_stat_activity` instrumentation in PR #1113 are unaffected and remain
valid groundwork — but cannot be exercised until the host is stable.
The previously-merged `migration_timeout_seconds=14400` bump in PR `#1113`
remains valid groundwork — but cannot be exercised until the host is stable.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/production-incident-baseline.md` around lines 87 - 89, The doc currently
gives conflicting statements about PR `#1113`: it claims `pg_stat_activity`
instrumentation from PR `#1113` remains valid while elsewhere it notes periodic
workflow snapshots were reverted; update the observability statement so there is
a single consistent source of truth — either state that both
`migration_timeout_seconds=14400` and the `pg_stat_activity` instrumentation in
PR `#1113` remain valid and can be exercised once the host is stable, or
explicitly note that `pg_stat_activity` instrumentation was impacted by the
revert of periodic workflow snapshots and is not currently usable; reference
`migration_timeout_seconds`, `pg_stat_activity`, PR `#1113` and the periodic
workflow snapshots revert in the updated sentence so operators see the correct,
non-conflicting status.

Asherlc and others added 5 commits May 10, 2026 10:25
Adds max_slot_wal_keep_size=4GB to the dofek_db postgres service.
Today's outage chain started when three PeerDB logical replication
slots went inactive, and postgres dutifully retained 26GB of WAL to
keep them resumeable — until it consumed the entire 100GB data
volume and started PANIC'ing on WAL writes. Without this setting
there's no upper bound; with it, a slot that falls more than 4GB
behind gets invalidated (recoverable via resync) instead of taking
the host down.

Logs the incident in docs/production-incident-baseline.md with the
full evidence chain and the recovery steps that were already
executed against prod (drop slots, checkpoint, image swap with
chown, compression policy tightened to 1 day).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds CREATE PUBLICATION for the dedicated no-IMU publication that the
analytics metric_stream mirror now consumes from, and points the
mirror DDL at it. Without this row filter at the postgres source,
~92% of the metric_stream WAL is IMU events that PeerDB has to
decode, ship to MinIO staging, and then discard — that's what filled
the data volume during today's outage. publish_via_partition_root
applies the filter to all TimescaleDB chunks via a single publication
entry.

Documents the full afternoon recovery in production-incident-baseline.md:
disk-full root cause, slot drops, image swap, host resize, ClickHouse
rebuild via the postgresql() table function, the QREP catalog-corruption
bug we sidestepped, and the remaining risks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 2026-05-10 late-evening recovery exposed an unbounded-memory
problem: with the 4 GB CH limit we set earlier, the full stack at
load exceeded the 8 GB host and the kernel triggered a global OOM
that took the box down (traefik invoked oom-killer, ClickHouse got
killed at 3.5 GB RSS, system halted).

Adds `deploy.resources.limits.memory` to every long-running service
in stack.yml. Sized from measured idle RSS with growth headroom; sum
of caps ≈ 8 GB on the 8 GB host. Per-container limits convert
host-wide OOMs into single-container cgroup OOMs that swarm restarts
cleanly. Tightens CH from 4 GB → 3 GB (it actually idles at 1.8 GB).

Drops the now-redundant "Ensure ClickHouse resource limits" workflow
step — stack.yml's deploy.resources.limits.memory is reconciled by
the normal docker stack deploy, so the standalone enforcement step
isn't needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI's clickhouse-cdc unit tests caught that the CREATE PUBLICATION I
added to src/db/peerdb/metric-stream-cdc.sql in 828d2eb was being
sent to PeerDB by the template runner, when publications actually
live on the source postgres and need to go there. Reverts the SQL
template addition and adds ensureMetricStreamNoImuPublication() in
clickhouse-cdc.ts as a sibling to ensureAnalyticsPublication(), which
is what already runs the existing publication setup on the source
postgres client during setupClickHouseCdc.

Updates the unit tests to expect the second source-postgres query
and the shifted positions in the shared-mock peerDbQueries array.

Also discovered (the hard way) that the existing splitPeerDbSqlStatements
splitter does not strip line comments, so apostrophes or semicolons in
comments confuse it. Comments in the SQL template now flag this and
avoid both characters.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Asherlc
Asherlc merged commit 060b630 into main May 11, 2026
62 checks passed
@Asherlc
Asherlc deleted the Asherlc/fix-deploy-workflow branch May 11, 2026 01:33
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