Reputation: 195
I'm trying to test parts of my code for race conditions. The issue I had was related to uniqueness validations, which as it turns out is not safe from race conditions in rails. I believe I'll be able to fix the issue, but I'm not sure how to test my solution.
The closest I've come is the following(inspired by: http://blog.arkency.com/2015/09/testing-race-conditions/):
test "Can't create duplicate keys with same value and keyboard" do
assert_equal(5, ActiveRecord::Base.connection.pool.size)
begin
concurrency_level = 4
keyboard = create :keyboard
should_wait = true
statuses = {}
threads = Array.new(concurrency_level) do |i|
Thread.new do
true while should_wait
begin
# Unique validation for key values exists scoped to keyboard
key = keyboard.keys.new(value: 'a')
statuses[i] = key.save
rescue ActiveRecord::RecordNotUnique
statuses[i] = false
end
end
end
should_wait = false
threads.each(&:join)
assert_equal(1, keyboard.keys.count)
assert_equal(1, statuses.count { |_k, v| v })
assert_equal(3, statuses.count { |_k, v| !v })
ensure
ActiveRecord::Base.connection_pool.disconnect!
end
end
The code above is structured exactly like mine, but models have changed to be more general.
The test itself seems to work alright. However, keys created in the tests are not deleted afterwards. I'm using DatabaseCleaner, and I've tried all different strategies. Also, sometimes I get a Circular Dependency issue for constant Key. Not sure why, but I'm guessing its due to requires not being thread safe in ruby?
Is there a better way to my problem? As I specified above, I've gotten a few different issues with this, and I feel it should be a common enough problem that good testing standards should exist.
Upvotes: 1
Views: 2194
Reputation: 738
A few things:
1) Probably my ignorance, but the true while should_wait
line seems wrong to me. Something more like while should_wait do
seems more like what you intend. You also call pod.save
which seems to not make sense, so I'm guessing this is not exactly the code you're using.
2) I would expect database cleaner to work, because I think if you use the "truncation" strategy it will go through and truncate every table when the test is run. My wild ass guess is that you have configured it to only run for integration tests and this is a unit test, or something like that. If that's not it, try calling DatabaseCleaner.truncate
(or however you do that explicitly) at the end of the test and see if that works.
3) Can you solve the problem with a unique index in your DB? That removes the need for this test at all because you get to just trust your database. When you do get a non-unique value you can handle that in a non-validation way in your code. Much faster too, because you don't have to make the extra sql call every time you save.
4) Impossible to know from the information given why you're getting the circular dependency issue. I've had that issue before and did a puts caller
at the top of the file to try to diagnose.
Upvotes: 1