name: ecto-constraint-debug description: 调试Ecto约束违规 - 追踪触发器、检查迁移、查找重复数据。当看到unique_constraint、foreign_key_constraint或check_constraint错误时使用。
Ecto约束调试
诊断约束违规的系统化方法。当你看到Ecto.ConstraintError、unique_constraint、foreign_key_constraint或与约束相关的changeset错误时加载。
铁律
- 阅读约束名称 — 约束名称(例如,
links_url_index)确切告诉你哪个索引/约束失败。首先从错误消息中解析它 - 先检查迁移再检查代码 — 验证
priv/repo/migrations/中的约束定义是否与模式期望匹配 - 追踪所有插入路径 — 找到所有插入到约束表的代码路径。错误通常在你未考虑的路径中
- 假定为竞争条件直到证明其他原因 — 如果验证通过但约束失败,假定并发插入直到你证明单请求原因
逐步调试
步骤1: 解析错误
从错误消息中提取:
- 约束名称(例如,
users_email_index) - 表名称(例如,
users) - 操作(插入、更新或删除)
- 冲突值(如果日志中可用)
步骤2: 查找迁移
grep -r "constraint_name" priv/repo/migrations/
# 同时检查:create unique_index, create index, add constraint
验证:迁移约束是否匹配模式的unique_constraint/3或foreign_key_constraint/3调用?
步骤3: 查找模式
# 在changeset中查找约束处理
grep -r "unique_constraint\|foreign_key_constraint\|check_constraint" lib/
步骤4: 追踪插入路径
查找所有插入/更新此模式的调用者:
grep -r "Repo.insert\|Repo.update\|Repo.insert_all\|cast_assoc.*:schema_name" lib/
步骤5: 识别原因
| 症状 | 可能原因 | 修复模式 |
|---|---|---|
| 同一用户触发两次 | 竞争条件(双击、重试) | 使用on_conflict进行upsert |
| 多个父级共享子级 | cast_assoc不会在changesets间去重 |
在构建changesets前去重 |
| 并发API请求 | 缺少事务隔离 | 包装在Repo.transaction中或使用upsert |
| 迁移为现有数据添加约束 | 数据违反新约束 | 首先回填或清理数据 |
步骤6: 应用修复
查看references/constraint-patterns.md获取详细修复模式。
按约束类型的快速修复
唯一约束违规 → Upsert: Repo.insert(changeset, on_conflict: :replace_all, conflict_target: [:field])
外键约束违规 → 检查:引用的记录是否存在?是否被并发删除?
检查约束 → 验证:值是否满足约束条件?
参考资料
references/constraint-patterns.md- 每种约束类型的详细模式