- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在使用 with_advisory_lock
gem 来尝试确保一条记录只创建一次。这是 github url到 gem 。
我有以下代码,它位于我编写的用于处理创建用户订阅的操作类中:
def create_subscription_for user
subscription = UserSubscription.with_advisory_lock("lock_%d" % user.id) do
UserSubscription.where({ user_id: user.id }).first_or_create
end
# do more stuff on that subscription
end
和伴随的测试:
threads = []
user = FactoryBot.create(:user)
rand(5..10).times do
threads << Thread.new do
subject.create_subscription_for(user)
end
end
threads.each(&:join)
expect(UserSubscription.count).to eq(1)
我期望发生的事情:
实际发生了什么:
ActiveRecord::RecordNotUnique
错误(我在表上有一个唯一索引,允许单个 user_subscription
具有相同的 user_id
)更奇怪的是,如果我在我的方法中的 find_or_create
方法之前添加一个几百毫秒的 sleep
,测试永远不会失败:
def create_subscription_for user
subscription = UserSubscription.with_advisory_lock("lock_%d" % user.id) do
sleep 0.2
UserSubscription.where({ user_id: user.id }).first_or_create
end
# do more stuff on that subscription
end
我的问题是:“为什么添加 sleep 0.2
让测试总是通过?”和“我应该在哪里调试它?”
谢谢!
更新:稍微调整测试会导致它们总是失败:
threads = []
user = FactoryBot.create(:user)
rand(5..10).times do
threads << Thread.new do
sleep
subject.create_subscription_for(user)
end
end
until threads.all? { |t| t.status == 'sleep' }
sleep 0.1
end
threads.each(&:wakeup)
threads.each(&:join)
expect(UserSubscription.count).to eq(1)
我还在事务中包装了 first_or_create
,这使得测试通过并且一切都按预期工作:
def create_subscription_for user
subscription = UserSubscription.with_advisory_lock("lock_%d" % user.id) do
UserSubscription.transaction do
UserSubscription.where({ user_id: user.id }).first_or_create
end
end
# do more stuff on that subscription
end
那么为什么将 first_or_create
包装在事务中才能使事情正常进行?
最佳答案
您要关闭此测试用例的事务测试吗?我正在研究类似的东西,事实证明这对实际模拟并发很重要。
参见uses_transaction
https://api.rubyonrails.org/classes/ActiveRecord/TestFixtures/ClassMethods.html
如果未关闭事务,Rails 会将整个测试包装在一个事务中,这将导致所有线程共享一个数据库连接。此外,在 Postgres 中, session 级咨询锁总是可以在同一 session 中重新获取。来自文档:
If a session already holds a given advisory lock, additional requests by it will always succeed, even if other sessions are awaiting the lock; this statement is true regardless of whether the existing lock hold and new request are at session level or transaction level.
基于此,我怀疑您的锁始终能够获取,因此始终执行 .first_or_create
调用,这会导致间歇性的 RecordNotUnique
异常。
关于ruby-on-rails - Ruby with_advisory_lock 多线程测试间歇性失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56146171/
我正在使用 with_advisory_lock gem 来尝试确保一条记录只创建一次。这是 github url到 gem 。 我有以下代码,它位于我编写的用于处理创建用户订阅的操作类中: def
我是一名优秀的程序员,十分优秀!