You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails中Call模型唯一性校验失效,webhook生成重复通话记录

问题根本原因

Rails 应用层的 validates_uniqueness_of 校验不具备并发安全性,你遇到的是典型的「竞态条件」问题,触发过程如下:

  • 同一个通话事件的 webhook 通知被重复推送(第三方平台通常会有重试机制),两个完全相同的请求几乎同时到达你的服务端
  • 两个请求被不同的 web 进程/线程并行处理,同时执行 Call.find_by(source: :aircall, external_id: @params["id"]) 逻辑,此时数据库还没有对应记录,两个进程都判定「记录不存在」,各自初始化了新的 Call 对象
  • 两个进程先后执行唯一性校验:校验逻辑是先查询数据库是否有重复记录,此时两边都没查到,校验都通过,最终先后写入数据库,就出现了两条数据完全一致、创建时间几乎相同的重复记录

额外潜在问题

你代码中存在一处逻辑不一致的问题,也可能加剧重复创建的概率:
查找记录时你使用的是 source: :aircall(符号类型),但后续赋值时又写了 @call.source = 1,如果 source 字段是枚举类型,请确认枚举映射规则正确,避免因类型/值不匹配导致 find_by 无法查询到已存在的记录。

修复方案

  1. 必须新增数据库层面的联合唯一索引,这是唯一能绝对保证数据唯一性的手段,执行如下迁移即可:
add_index :calls, [:external_id, :source], unique: true

加索引后即使出现并发写入,数据库会直接拒绝第二条重复数据的写入请求,抛出唯一约束异常,你可以在业务代码中捕获该异常做兼容处理。
2. 优化记录查找创建逻辑,优先使用 Rails 内置的原子性更强的方法,比如 find_or_create_by、upsert 替代当前先查询后创建的写法。
3. 高并发场景下可以额外增加防护手段:比如给相同 external_id 的 webhook 处理逻辑加分布式锁,或者将 webhook 处理任务放入异步队列,对相同 external_id 的任务做串行处理。


内容的提问来源于stack exchange,提问作者Jeremy Thomas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 18:27:03