我们注意到在我们的数据库中的各种表中创建了大量重复记录,但是为什么会发生这种情况却不知所措.有趣的是,虽然记录在其他方面是重复的(甚至是created_at标记!),但在我们的用户表上,密码salt和hash在每条记录上都是不同的 – 这让我相信Rails不知何故以某种方式运行事务/保存操作两次.显然,我们不会在应用程序代码中多次调用save或create.
对于保存在数据库中的每条记录,似乎不会发生这种重复,我们似乎无法推断出模式.在User模型上还有一个validates_uniqueness_of验证(虽然不是表上的唯一键;我们需要清理所有重复项才能够这样做) – 所以如果记录已经存在,Rails应该自行停止,但是如果请求同时触发那是一个竞争条件.
我们目前在我们的应用服务器上运行Rails 3.2.2,在我们的应用服务器(目前其中有两个)上运行,并且有一个中央Nginx网络服务器向上游发送请求到应用服务器.这种设置可能会导致流程重复或某些原因吗?将请求锁定到一个上游服务器是否重要(即,如果一个用户请求包含静态内容的页面,如图像,可能会使用一个或两个应用服务器)? (我觉得这是抓住稻草,但我想涵盖所有可能性)
还有什么可能导致这种情况发生?
更新:例如,今天创建了一个具有重复记录的用户.两者都有2012-03-28 16:48:11的created_at标记,除hashed_password和salt之外的所有列都是相同的.从请求日志中,我可以看到以下内容:
App Server 1:
Started POST "/en/apply/create_user" for 1.2.3.4 at 2012-03-28 12:47:19 -0400
[2012-03-28 12:47:19] INFO : Processing by ApplyController#create_user as HTML
[2012-03-28 12:47:20] INFO : Rendered apply/new_user.html.erb within layouts/template (192.8ms)
Started POST "/en/apply/create_user" for 1.2.3.4 at 2012-03-28 12:48:10 -0400
[2012-03-28 12:48:10] INFO : Processing by ApplyController#create_user as HTML
[2012-03-28 12:48:11] INFO : Redirected to apply/initialize_job_application/3517
[2012-03-28 12:48:11] INFO : /app/controllers/apply_controller.rb:263:in `block (2 levels) in create_user'
App Server 2:
Started POST "/en/apply/create_user" for 1.2.3.4 at 2012-03-28 12:48:10 -0400
[2012-03-28 12:48:10] INFO : Processing by ApplyController#create_user as HTML
网络服务器:
1.2.3.4 - - [28/Mar/2012:12:48:10 -0400] "POST /en/apply/create_user HTTP/1.1" 499 0 "en/apply/create_user" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)" "-"
1.2.3.4 - - [28/Mar/2012:12:48:11 -0400] "POST /en/apply/create_user HTTP/1.1" 302 147 "en/apply/create_user" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)" "-"
因此创建操作被击中三次(可能由于错误而第一次返回到表单),并且每个服务器至少一次.后两者都由网络服务器注册为单独的请求,但第一个获取状态代码499客户端关闭请求(根据维基百科的Nginx扩展),第二个获得302预期. 499可能会引起这里的问题吗?
第一个是用作负载均衡器时Nginx的奇数(并且反对RFC)行为.它将针对下一个后端重试任何失败的请求. RFC仅允许安全方法(例如GET或HEAD).这样做的结果是,如果您的Nginx因某种原因认为请求失败,则可能是它被重新发送到下一个服务器.如果两个服务器完成了他们的事务,则会有重复的记录.从你的webservers日志(以及Nginx用来表示用户在浏览器中点击中止的499状态代码)来看,这看起来是最可能的原因.
第二种可能性是您的用户双击发送按钮.在合适的时机,他们的浏览器几乎可以同时发送两个完整的请求.
要确保您的用户记录确实是唯一的,您应该在数据库上创建唯一索引.然后实际确保这些(尽管与ActiveRecord检查相比,错误消息更糟.因此,您应该始终在数据库模式和模型上定义唯一性约束.