Say I have an ActiveRecord model called Person
with a table name of people
that has id
and name
columns. There is a unique key constraint on the name
column so that no two Person
records may share a name. I am using find_or_create_by_name
coupled with a rescue block in case two threads try to insert the same value:
def get_or_make_person(name)
begin
person = Person.find_or_create_by_name(name)
rescue ActiveRecord::RecordNotUnique
retry
end
person
end
I am trying to test the thread-safety of my code. So I wrote a test:
name = 'Joe'
t1 = Thread.new do
person = get_or_make_person name
end
t1 = Thread.new do
person = get_or_make_person name
end
# Wait until both threads are done
t1.join
t2.join
# At this point, the person should be created no matter what. Let's find him:
person = Person.find_by_name name
assert_not_nil person
However, this test fails. I used Pry to inspect my program after joining both threads, and found that my Person
model in the main thread knows absolutely nothing about the record that was created in the other two threads. Looking in the database manually at this point shows that it is definitely in the database:
mysql> select * from people;
+----+------+
| id | name |
+----+------+
| 1 | Joe |
+----+------+
I even tried using Person.find_by_sql('select * from people')
, but that didn't return the record either.
And once I exit my debugging session and try the test again with a different name (say 'Jane'), I am able to retrieve the first record with Person.find_by_name('Joe')
or Person.find(1)
.
What's going on here? Is there any way to somehow force ActiveRecord to reload its knowledge of the people
table?
My use case is slightly more involved than this (which is why the example seems slightly contrived), but this should capture the essence of what I am running into.
Additional details:
The exact same code runs successfully and produces the expected results (using a print statement instead of assert_not_nil
of course) if run within a standalone script, but fails when run using Test::Unit as a unit test.