在我的应用中,工作人员可以在其帐户中建立贷方余额,并在他们认为合适时将其提取到他们的银行帐户。有一个MoneyController::withdraw
操作在.withdraw_funds
上调用了current_worker
方法,该方法调用了均衡付款API,如果amount
他们是Transaction
,则可以记入他们的银行帐户。要求退出的是< =余额中的金额。创建了Account
,附加到工作人员MoneyController
def withdraw
if current_worker.withdraw_funds((params[:amount].to_d*100).to_i, params[:bbank])
redirect_to worker_money_path, notice: "Successfully withdrew $#{params[:amount]}"
else
redirect_to worker_money_path, alert: "Failed to withdraw funds. Please contact us for assistance."
end
end
,其中列出了从其余额中扣除的金额并记入其银行帐户。
最近发生了一些事情,这个控制器动作被击中并且整个过程发生了两次,即使请求如果余额为空也应该被拒绝。撤销发生两次,并且使用相同或非常接近的时间戳生成两个事务。但是,它只发生在某些工作者身上,我无法在开发或登台服务器上重现错误。我希望有人可以给我一些关于如何进行调试的建议。以下是相关代码:
worker.rb
def withdraw_funds(amount, bbank_id)
bcust = self.get_balanced
bbank = Balanced::BankAccount.fetch("/bank_accounts/#{bbank_id}")
if bbank and (bbank.customer.id == bcust.id)
puts "bank belongs to worker"
if self.account.balance >= amount
res = bbank.credit(amount: amount)
self.account.debit(amount)
Transaction.create(amount: amount, tag: 'cashout', source_id: self.account.id, destination_id: nil, balanced_id: res.id)
return true
else
puts "worker #{self} doesn't have #{amount} in account"
return false
end
else
puts "bank does not belong to worker"
return false
end
end
/
if self.account.balance >= amount
如果工人的余额包含50美元,而且两个请求是50美元,则第一个应该成功,然后第二个应该失败,因为余额现在是0美元(因此Started POST "/money/withdraw" for 68.119.221.188 at 2014-11-05 13:56:41 -0500
Processing by MoneyController#withdraw as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"x5olIpvJf2K37lYRJypIIHYNhAdZUm1ptill13w9Evw=", "amount"=>"48.50", "bbank"=>"BA2...", "commit"=>"Withdraw"}
Started POST "/money/withdraw" for xx.xxx.xxx.xxx at 2014-11-05 13:56:46 -0500
Processing by MoneyController#withdraw as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"x5olIpvJf2K37lYRJypIIHYNhAdZUm1ptill13w9Evw=", "amount"=>"48.50", "bbank"=>"BA2...", "commit"=>"Withdraw"}
Redirected to http://myapp.com/money
Completed 302 Found in 4467.9ms (ActiveRecord: 54.7ms)
Started GET "/worker/money" for xx.xxx.xxx.xxx at 2014-11-05 13:56:50 -0500
Processing by Worker::MoneyController#index as HTML
Redirected to http://myapp.com/money
Completed 302 Found in 9099.1ms (ActiveRecord: 69.6ms)
)。
我也能够查看开发服务器的日志,并找到发生这种情况的日志:
{{1}}
我在日志中注意到这两个请求都具有相同的真实性令牌,但我不确定还有什么可以从中获取。我认为这可能就像工人点击" Withdraw"多次按钮,但我试图在开发和暂存中重新创建从未导致问题的问题。请求刚刚排队,随后的请求总是导致正确的响应,即余额为空。
编辑 我在生产服务器上设置了测试方案,并且可以通过多次单击“提取”按钮来重现问题。任何人都知道为什么会在生产中发生这种情况,而不是在开发或分期中发生?连接速度可能与它有关吗?
答案 0 :(得分:1)
由于您没有发布客户端代码,我建议您对Worker
进行某种锁定,这样他一次只能有一个正在运行的事务......类似于in_process:boolean
和{ {1}}(两者合并)。
因此,当您开始一项交易时,请确保process_start:timestamp
为in_process
并将其设为false
,同时修改true
(将其置于意外锁定位置)例如,如果process_start
,则假定锁定处于活动状态。
完成此过程后,将in_process && process_start>(Time.now - 10.minutes)
标志设置为false。
这样,每个用户只能有一个进程处于活动状态。
当然,如果我们有一些in_process
,这可能就没有必要了,但无论如何,在涉及金钱的流程背后都有业务(控制器)逻辑,这是一种很好的做法。
编辑:也许在交易结束后保持html
锁定几秒钟也是个好主意。