Skip to content

Commit bae4e80

Browse files
KATO-Hiroclaude
andcommitted
fix(migration): fix failed vote grade CHECK constraint migration in staging
Rename migration with corrected table names (lowercase per @@Map) and add a one-time workflow_dispatch workflow to resolve the P3009 stuck state in staging DB. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 1f1ab4b commit bae4e80

3 files changed

Lines changed: 183 additions & 4 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Resolve Failed Migration (one-time use)
2+
on:
3+
workflow_dispatch:
4+
5+
jobs:
6+
resolve:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v6
10+
- uses: pnpm/action-setup@v5
11+
with:
12+
package_json_file: package.json
13+
run_install: false
14+
- uses: actions/setup-node@v6
15+
with:
16+
node-version: 24
17+
cache: 'pnpm'
18+
cache-dependency-path: pnpm-lock.yaml
19+
- run: pnpm install
20+
- name: Resolve failed migration in staging DB
21+
run: |
22+
pnpm exec prisma migrate resolve \
23+
--rolled-back 20260406112057_add_vote_grade_check_constraints
24+
env:
25+
DATABASE_URL: ${{ secrets.PREVIEW_DATABASE_URL }}
26+
DIRECT_URL: ${{ secrets.PREVIEW_DIRECT_URL }}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# staging 環境の failed migration 修復計画
2+
3+
## 概要
4+
5+
`20260406112057_add_vote_grade_check_constraints` migration が staging DB で P3009 エラー(失敗中状態)で止まっており、以降の migration deploy が全てブロックされている状態を GHA 経由で修復する。
6+
7+
## 根本原因
8+
9+
migration.sql 内のテーブル名が Pascal Case クォート形式(例: `"VotedGradeCounter"`)で記述されているが、`schema.prisma``@@map` により実際の PostgreSQL テーブル名は小文字(`votedgradecounter`)。
10+
11+
```sql
12+
-- 誤(失敗した migration)
13+
ALTER TABLE "VotedGradeCounter" ADD CONSTRAINT ...
14+
15+
-- 正(@@map に合わせた正しい記述)
16+
ALTER TABLE "votedgradecounter" ADD CONSTRAINT ...
17+
```
18+
19+
PostgreSQL はクォート付きの識別子を大文字小文字区別で処理するため、`"VotedGradeCounter"` は存在しないテーブルを参照して ALTER TABLE が失敗。`_prisma_migrations``finished_at = NULL` のレコードが残り P3009 状態となった。
20+
21+
## 他 AI 提案のレビュー・却下理由
22+
23+
| 提案内容 | 評価 | 理由 |
24+
| ---------------------------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------- |
25+
| `migrate resolve --rolled-back` の使用 | ✅ 正しい | P3009 を解消する唯一の手段 |
26+
| 同名 `migration.sql` を修正して再 deploy |**機能しない** | `--rolled-back` にマークされた migration は `migrate deploy` が永久にスキップする。同ファイルを修正しても実行されない |
27+
28+
**正しいアプローチ:** rolled-back 解消後は、**新しいタイムスタンプで新 migration ファイルを作成**して deploy する必要がある。
29+
30+
## 設計方針・却下した代替案
31+
32+
### 採用: workflow_dispatch + 新 migration 作成
33+
34+
- GHA の `workflow_dispatch` トリガーで `migrate resolve` を一度だけ実行
35+
- 旧 migration ファイルを git から削除(ローカル開発環境で `migrate dev` が失敗しないよう)
36+
- 新タイムスタンプで正しいテーブル名の migration を作成
37+
38+
### 却下: ci.yml への一時追記
39+
40+
- `resolve` ステップを既存 `preview` ジョブに追記する案
41+
- 問題: すでに成功した将来の deploy でも毎回 resolve コマンドが走る(冪等ではあるが冗長)
42+
- `workflow_dispatch` の方がスコープが明確で安全
43+
44+
### 却下: DB への直接接続
45+
46+
- staging DB に直接アクセスして `_prisma_migrations` を操作する案
47+
- 方針: GHA 経由でのみ DB を操作するため却下
48+
49+
## 実装フェーズ
50+
51+
### Phase 1: 変更を staging へ push(GHA に workflow_dispatch を公開)
52+
53+
**変更ファイル:**
54+
55+
| 操作 | パス |
56+
| ---- | --------------------------------------------------------------------------------- |
57+
| 追加 | `.github/workflows/resolve-migration.yml` |
58+
| 削除 | `prisma/migrations/20260406112057_add_vote_grade_check_constraints/` |
59+
| 追加 | `prisma/migrations/20260409000000_fix_vote_grade_check_constraints/migration.sql` |
60+
61+
**`.github/workflows/resolve-migration.yml` 内容:**
62+
63+
```yaml
64+
name: Resolve Failed Migration (one-time use)
65+
on:
66+
workflow_dispatch:
67+
68+
jobs:
69+
resolve:
70+
runs-on: ubuntu-latest
71+
steps:
72+
- uses: actions/checkout@v6
73+
- uses: pnpm/action-setup@v5
74+
with:
75+
package_json_file: package.json
76+
run_install: false
77+
- uses: actions/setup-node@v6
78+
with:
79+
node-version: 24
80+
cache: 'pnpm'
81+
cache-dependency-path: pnpm-lock.yaml
82+
- run: pnpm install
83+
- name: Resolve failed migration in staging DB
84+
run: |
85+
pnpm exec prisma migrate resolve \
86+
--rolled-back 20260406112057_add_vote_grade_check_constraints
87+
env:
88+
DATABASE_URL: ${{ secrets.PREVIEW_DATABASE_URL }}
89+
DIRECT_URL: ${{ secrets.PREVIEW_DIRECT_URL }}
90+
```
91+
92+
**新 migration SQL:**
93+
94+
```sql
95+
-- VotedGradeCounter.count must never go negative (application guard + DB last line of defense)
96+
ALTER TABLE "votedgradecounter" ADD CONSTRAINT "votedgradecounter_count_non_negative"
97+
CHECK (count >= 0);
98+
99+
-- VoteGrade.grade must not be PENDING (only non-pending grades are valid votes)
100+
ALTER TABLE "votegrade" ADD CONSTRAINT "votegrade_grade_not_pending"
101+
CHECK (grade != 'PENDING');
102+
103+
-- VotedGradeCounter.grade must not be PENDING
104+
ALTER TABLE "votedgradecounter" ADD CONSTRAINT "votedgradecounter_grade_not_pending"
105+
CHECK (grade != 'PENDING');
106+
107+
-- VotedGradeStatistics.grade must not be PENDING (median must be a real grade)
108+
ALTER TABLE "votedgradestatistics" ADD CONSTRAINT "votedgradestatistics_grade_not_pending"
109+
CHECK (grade != 'PENDING');
110+
```
111+
112+
> この push で preview ジョブが起動するが、staging DB はまだ stuck 状態のため `migrate deploy` は P3009 で失敗する。これは想定内。
113+
114+
### Phase 2: workflow_dispatch を手動トリガー
115+
116+
GitHub Actions UI から操作:
117+
118+
1. リポジトリの Actions タブを開く
119+
2. "Resolve Failed Migration (one-time use)" を選択
120+
3. "Run workflow" → **staging ブランチ**を選択して実行
121+
122+
これにより staging DB の `_prisma_migrations` の対象レコードに `rolled_back_at` がセットされ、P3009 が解消される。
123+
124+
### Phase 3: 失敗した preview ジョブを再実行
125+
126+
GitHub Actions UI から Phase 1 の push で失敗した preview ジョブを "Re-run jobs" で再実行。今度は `migrate deploy` が P3009 なしで通り、新 migration `20260409000000_fix_vote_grade_check_constraints` が適用される。
127+
128+
## 検証
129+
130+
| タイミング | 確認内容 |
131+
| -------------- | ------------------------------------------------------------ |
132+
| Phase 2 完了後 | `workflow_dispatch` ジョブの終了コードが 0 |
133+
| Phase 3 完了後 | GHA の "Apply all pending migrations" ステップが成功 |
134+
| Phase 3 完了後 | staging の Vercel デプロイが完了 |
135+
| Phase 3 完了後 | staging アプリで投票機能が正常動作(CHECK 制約が DB に存在) |
136+
137+
## ローカル開発環境への影響
138+
139+
旧 migration ファイルを git から削除することで、開発者が `git pull` 後に `migrate dev` を実行しても旧 SQL は実行されず、代わりに新 migration が適用される。
140+
141+
ただし、**旧 migration をすでにローカル DB に適用しようとして failed 状態にしている開発者**は別途対応が必要:
142+
143+
```bash
144+
# ローカル DB の stuck 状態を解消
145+
pnpm exec prisma migrate resolve --rolled-back 20260406112057_add_vote_grade_check_constraints
146+
147+
# その後 migrate dev を実行
148+
pnpm exec prisma migrate dev
149+
```
150+
151+
## 事後処理
152+
153+
`resolve-migration.yml` は使い捨て。fix 確認後に削除 PR を出してもよい(再実行しても冪等なので必須ではない)。

prisma/migrations/20260406112057_add_vote_grade_check_constraints/migration.sql renamed to prisma/migrations/20260409000000_fix_vote_grade_check_constraints/migration.sql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
-- VotedGradeCounter.count must never go negative (application guard + DB last line of defense)
2-
ALTER TABLE "VotedGradeCounter" ADD CONSTRAINT "votedgradecounter_count_non_negative"
2+
ALTER TABLE "votedgradecounter" ADD CONSTRAINT "votedgradecounter_count_non_negative"
33
CHECK (count >= 0);
44

55
-- VoteGrade.grade must not be PENDING (only non-pending grades are valid votes)
6-
ALTER TABLE "VoteGrade" ADD CONSTRAINT "votegrade_grade_not_pending"
6+
ALTER TABLE "votegrade" ADD CONSTRAINT "votegrade_grade_not_pending"
77
CHECK (grade != 'PENDING');
88

99
-- VotedGradeCounter.grade must not be PENDING
10-
ALTER TABLE "VotedGradeCounter" ADD CONSTRAINT "votedgradecounter_grade_not_pending"
10+
ALTER TABLE "votedgradecounter" ADD CONSTRAINT "votedgradecounter_grade_not_pending"
1111
CHECK (grade != 'PENDING');
1212

1313
-- VotedGradeStatistics.grade must not be PENDING (median must be a real grade)
14-
ALTER TABLE "VotedGradeStatistics" ADD CONSTRAINT "votedgradestatistics_grade_not_pending"
14+
ALTER TABLE "votedgradestatistics" ADD CONSTRAINT "votedgradestatistics_grade_not_pending"
1515
CHECK (grade != 'PENDING');

0 commit comments

Comments
 (0)