自助注册(含钉钉/OAuth)报 500→404 的根因:grok 自 2026-06 进入默认平台配额 (default_platform_quotas / auth_source_*),但 user_platform_quotas 的 CHECK 约束(迁移 142)仅允许 anthropic/openai/gemini/antigravity。注册时 snapshotPlatformQuotaDefaults 写 grok 行违反约束 → 整个注册事务被 Postgres 标记 aborted → consumePendingOAuthBrowserSessionTx 撞 "transaction aborted" → 500 → clearCookies → 用户重试拿到 404(PENDING_AUTH_SESSION_NOT_FOUND)。 影响面:所有新自助注册(不限钉钉)。 修复(两层): - 事务隔离(fix①):snapshotPlatformQuotaDefaults 用 ent.WithoutTx 剥离调用方事务, 在基础连接 autocommit 执行。best-effort 快照失败永不毒化注册主事务,从根上消除 "事务内 fail-open 形同虚设"陷阱——今后任何平台/约束漂移都不会再连累注册。 - 迁移 157:把 grok 加入 user_platform_quotas.platform 的 CHECK 约束,与代码平台 列表(domain/constants.go PlatformGrok)对齐(DROP IF EXISTS + ADD,可重入)。 新增 ent.WithoutTx(ctx) helper(手写文件,不动生成代码)。 测试: - 单测 TestSnapshotPlatformQuotaDefaults_DetachesCallerTransaction(RED→GREEN): 快照即便在事务 ctx 中也必须用脱离事务的 ctx 调 repo。 - 集成测试 TestUserPlatformQuotaRepository_BulkInsertInitial_GrokAllowed: 迁移 157 后 grok 可写入(真实 postgres 容器验证)。 验证:go build ./... / go vet -tags unit ./... / 全量单测(-tags unit,45 包) / 平台配额+迁移集成测试 全绿。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Database Migrations
Overview
This directory contains SQL migration files for database schema changes. The migration system uses SHA256 checksums to ensure migration immutability and consistency across environments.
Migration File Naming
Format: NNN_description.sql
NNN: Sequential number (e.g., 001, 002, 003)description: Brief description in snake_case
Example: 017_add_gemini_tier_id.sql
_notx.sql 命名与执行语义(并发索引专用)
当迁移包含 CREATE INDEX CONCURRENTLY 或 DROP INDEX CONCURRENTLY 时,必须使用 _notx.sql 后缀,例如:
062_add_accounts_priority_indexes_notx.sql063_drop_legacy_indexes_notx.sql
运行规则:
*.sql(不带_notx)按事务执行。*_notx.sql按非事务执行,不会包裹在BEGIN/COMMIT中。*_notx.sql仅允许并发索引语句,不允许混入事务控制语句或其他 DDL/DML。
幂等要求(必须):
- 创建索引:
CREATE INDEX CONCURRENTLY IF NOT EXISTS ... - 删除索引:
DROP INDEX CONCURRENTLY IF EXISTS ...
这样可以保证灾备重放、重复执行时不会因对象已存在/不存在而失败。
Migration File Structure
This project uses a custom migration runner (internal/repository/migrations_runner.go) that executes the full SQL file content as-is.
- Regular migrations (
*.sql): executed in a transaction. - Non-transactional migrations (
*_notx.sql): split by statement and executed without transaction (forCONCURRENTLY).
-- Forward-only migration (recommended)
ALTER TABLE usage_logs ADD COLUMN IF NOT EXISTS example_column VARCHAR(100);
⚠️ Do not place executable "Down" SQL in the same file. The runner does not parse goose Up/Down sections and will execute all SQL statements in the file.
Important Rules
⚠️ Immutability Principle
Once a migration is applied to ANY environment (dev, staging, production), it MUST NOT be modified.
Why?
- Each migration has a SHA256 checksum stored in the
schema_migrationstable - Modifying an applied migration causes checksum mismatch errors
- Different environments would have inconsistent database states
- Breaks audit trail and reproducibility
✅ Correct Workflow
-
Create new migration
# Create new file with next sequential number touch migrations/018_your_change.sql -
Write forward-only migration SQL
- Put only the intended schema change in the file
- If rollback is needed, create a new migration file to revert
-
Test locally
# Apply migration make migrate-up # Test rollback make migrate-down -
Commit and deploy
git add migrations/018_your_change.sql git commit -m "feat(db): add your change"
❌ What NOT to Do
- ❌ Modify an already-applied migration file
- ❌ Delete migration files
- ❌ Change migration file names
- ❌ Reorder migration numbers
🔧 If You Accidentally Modified an Applied Migration
Error message:
migration 017_add_gemini_tier_id.sql checksum mismatch (db=abc123... file=def456...)
Solution:
# 1. Find the original version
git log --oneline -- migrations/017_add_gemini_tier_id.sql
# 2. Revert to the commit when it was first applied
git checkout <commit-hash> -- migrations/017_add_gemini_tier_id.sql
# 3. Create a NEW migration for your changes
touch migrations/018_your_new_change.sql
Migration System Details
- Checksum Algorithm: SHA256 of trimmed file content
- Tracking Table:
schema_migrations(filename, checksum, applied_at) - Runner:
internal/repository/migrations_runner.go - Auto-run: Migrations run automatically on service startup
Best Practices
-
Keep migrations small and focused
- One logical change per migration
- Easier to review and rollback
-
Write reversible migrations
- Always provide a working Down migration
- Test rollback before committing
-
Use transactions
- Wrap DDL statements in transactions when possible
- Ensures atomicity
-
Add comments
- Explain WHY the change is needed
- Document any special considerations
-
Test in development first
- Apply migration locally
- Verify data integrity
- Test rollback
Example Migration
-- Add tier_id field to Gemini OAuth accounts for quota tracking
UPDATE accounts
SET credentials = jsonb_set(
credentials,
'{tier_id}',
'"LEGACY"',
true
)
WHERE platform = 'gemini'
AND type = 'oauth'
AND credentials->>'tier_id' IS NULL;
Troubleshooting
Checksum Mismatch
See "If You Accidentally Modified an Applied Migration" above.
Migration Failed
# Check migration status
psql -d sub2api -c "SELECT * FROM schema_migrations ORDER BY applied_at DESC;"
# Manually rollback if needed (use with caution)
# Better to fix the migration and create a new one
Need to Skip a Migration (Emergency Only)
-- DANGEROUS: Only use in development or with extreme caution
INSERT INTO schema_migrations (filename, checksum, applied_at)
VALUES ('NNN_migration.sql', 'calculated_checksum', NOW());
References
- Migration runner:
internal/repository/migrations_runner.go - PostgreSQL docs: https://www.postgresql.org/docs/