Skip to content

Fix 502: extract materialized views into canonical files, detect migration collisions - #587

Merged
Asherlc merged 9 commits into
mainfrom
claude/fix-502-error-KerLd
Mar 28, 2026
Merged

Asherlc merged 9 commits into
mainfrom
claude/fix-502-error-KerLd

Conversation

@Asherlc

@Asherlc Asherlc commented Mar 28, 2026

Copy link
Copy Markdown
Owner

Two concurrent PRs (#581, #583) created conflicting 0049_* migrations that both
DROP and recreate v_activity/activity_summary. The second migration set
statement_timeout=60s which caused CREATE MATERIALIZED VIEW to timeout, leaving
views dropped but not recreated — crash-looping the container and causing 502s.

Root cause: PostgreSQL materialized views can't be ALTERed to add columns, so
any change requires DROP+CREATE. When two migrations do this independently,
the second clobbers the first's changes. This is a systemic migration problem.

Fix:

  • Extract view definitions into canonical files (drizzle/views/) that are the
    single source of truth. The migration runner recreates all views from these
    files after every migration run (similar to Flyway's repeatable migrations).
  • Add duplicate migration prefix detection — the runner now refuses to run if
    two files share a numeric prefix (e.g., two 0049_* files), catching this
    class of conflict at startup instead of silently corrupting state.
  • Merge both 0049 migrations' v_activity changes (timezone + source_external_ids)
    into the canonical v_activity.sql definition.
  • Remove statement_timeout from migrations (was causing view creation to timeout).
  • Strip view DROP/CREATE from individual migrations (now handled by canonical files).

https://claude.ai/code/session_01YWKdigbgtBfQYnkq4LwZHo

…ation collisions

Two concurrent PRs (#581, #583) created conflicting 0049_* migrations that both
DROP and recreate v_activity/activity_summary. The second migration set
statement_timeout=60s which caused CREATE MATERIALIZED VIEW to timeout, leaving
views dropped but not recreated — crash-looping the container and causing 502s.

Root cause: PostgreSQL materialized views can't be ALTERed to add columns, so
any change requires DROP+CREATE. When two migrations do this independently,
the second clobbers the first's changes. This is a systemic migration problem.

Fix:
- Extract view definitions into canonical files (drizzle/views/) that are the
  single source of truth. The migration runner recreates all views from these
  files after every migration run (similar to Flyway's repeatable migrations).
- Add duplicate migration prefix detection — the runner now refuses to run if
  two files share a numeric prefix (e.g., two 0049_* files), catching this
  class of conflict at startup instead of silently corrupting state.
- Merge both 0049 migrations' v_activity changes (timezone + source_external_ids)
  into the canonical v_activity.sql definition.
- Remove statement_timeout from migrations (was causing view creation to timeout).
- Strip view DROP/CREATE from individual migrations (now handled by canonical files).

https://claude.ai/code/session_01YWKdigbgtBfQYnkq4LwZHo
@Asherlc
Asherlc enabled auto-merge (squash) March 28, 2026 00:34
@codecov

codecov Bot commented Mar 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.32%. Comparing base (8944c23) to head (8c74b3c).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #587   +/-   ##
=======================================
  Coverage   96.31%   96.32%           
=======================================
  Files         258      258           
  Lines       38316    38370   +54     
  Branches     7620     7641   +21     
=======================================
+ Hits        36905    36959   +54     
  Misses       1411     1411           
Flag Coverage Δ
integration 64.45% <45.16%> (-0.04%) ⬇️
unit 89.82% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

claude added 8 commits March 28, 2026 00:42
…ations for duplicates

- test-helpers.ts: recreate materialized views from drizzle/views/ after
  running migrations (integration tests were missing source_external_ids)
- migrate.ts: only check pending migrations for duplicate prefixes, not
  all files (old migrations have legitimate duplicate prefixes from before
  this safeguard existed)

https://claude.ai/code/session_01YWKdigbgtBfQYnkq4LwZHo
…discovery

- Rename drizzle/views/ to drizzle/_views/ — vitest auto-discovers
  directories named "views" as workspace roots, breaking project resolution
- Add numeric prefixes (01_, 02_) to view files for dependency ordering:
  01_v_activity.sql must be created before 02_activity_summary.sql
- Drop existing views by querying pg_matviews instead of deriving names
  from filenames (avoids prefix-in-name issues)

https://claude.ai/code/session_01YWKdigbgtBfQYnkq4LwZHo
The recreateViews function was dropping ALL materialized views in the
fitness schema but only recreating v_activity and activity_summary.
This broke v_daily_metrics, v_sleep, and other views. Now only drops
views that have canonical definitions in drizzle/_views/.

Also adds matviews, matviewname, schemaname to cspell dictionary.

https://claude.ai/code/session_01YWKdigbgtBfQYnkq4LwZHo
Restructure parallel arrays into a single mapped array of objects,
removing the unreachable `if (!content) continue` guard that would
fail mutation testing.

https://claude.ai/code/session_01YWKdigbgtBfQYnkq4LwZHo
Covers the `if (!viewName) continue` branch in recreateViews to
prevent surviving mutation.

https://claude.ai/code/session_01YWKdigbgtBfQYnkq4LwZHo
On a fresh DB (E2E), all migrations are pending including historical
duplicates (0018, 0022, 0023, 0024, 0041). Throwing an error blocks
fresh-DB migrations entirely. Log a warning instead.

https://claude.ai/code/session_01YWKdigbgtBfQYnkq4LwZHo
Kills Stryker mutant that survived by removing the logger.warn call —
the test now verifies the warning is actually emitted.

https://claude.ai/code/session_01YWKdigbgtBfQYnkq4LwZHo
Verify info logging for migration application, view recreation, and
the count>0 conditional. Ensures removing logger calls fails tests.

https://claude.ai/code/session_01YWKdigbgtBfQYnkq4LwZHo
@Asherlc
Asherlc merged commit bfbe1c0 into main Mar 28, 2026
41 checks passed
@Asherlc
Asherlc deleted the claude/fix-502-error-KerLd branch March 28, 2026 04:47
Asherlc added a commit that referenced this pull request Mar 31, 2026
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