在 Rails 7 应用程序中,使用 Postgresql 数据库,我有一个模型
Stat
和 jsonb 字段 data
。我发生了很大的并发,有几个异步进程将在 jsonb 字段中添加自己的行data
。
为了测试并发弹性,我创建了一个 Rspec 测试。它将键值对
{"<index>" => "ok"}
添加到 data
jsonb 字段 20 次。最后,我得到数据字段的随机长度。如果它有弹性,长度应始终为“20”。
threads = 20.times.map.with_index do |_, index|
Thread.new do
stat = Stat.find(stat_id)
stat.with_lock do
stat.update!(data: stat.data.merge({index => "ok"}))
end
end
end
threads.map &:join
stat = Stat.find(stat_id)
expect(stat.data.length).to eq(20)
Rails 中的默认设置是在单个事务中运行测试示例,然后在测试结束时回滚以将数据库恢复到原始状态。这意味着,尽管您创建了所有这些线程,但它们都从池中汇集到同一个数据库连接中,这意味着每个线程中的锁并未与其他线程隔离。
如果您关闭
use_transactional_fixtures
,那么您将看到您想要的行为(RSpec::Rails v6 有一个用于关闭事务测试的简洁助手),尽管您还需要自己清理数据库。将其放入您的测试中:
include RSpec::Rails::FixtureSupport
uses_transaction "the name of your test"
after { Stat.delete_all }
您可能还需要增加
database.yml
中的连接池大小,以便至少有 20 个可用连接。