Database empty in new Thread / Process testing with Rspec + Factory Girl
Asked Answered
R

1

6

Following this blog post, http://blog.arkency.com/2015/09/testing-race-conditions/

I am trying to test concurrency. But in my specs, I am not able to find the record when I spin up a new thread or fork a process.

describe 'test concurrency' do
  let(:order_1) { create(:order) }
  let(:order_2) { create(:order) }
  let(:order_3) { create(:order) }
  let(:order_4) { create(:order) }
  let(:product) { create(:product) }

  it 'test concurrency' do
    wait_for_all_threads = true
    product.update(quantity_available: 4, quantity_sold: 0, quantity_in_carts: 0)
    orders = [order_1, order_2, order_3, order_4]

    # Objects are persisted in here
    # because order_n.persisted => true
    # product.persisted => true
    # product.reload works without issue (and this is essentially what causes the RecordNotFound error in Order#add_item)

    threads = orders.map do |order|
      Thread.new do

        # Objects cannot be found in the database

        true while wait_for_all_threads

        item = order.add_item(product, 1)
        order_items << item
      end
    end

    wait_for_all_threads = false

    threads.each(&:join)

    # ...
    # here comes all the expects ...
    # not gonna post it because it's not what matters in this question
  end
end

After research, I've set these:

DatabaseCleaner.strategy = :truncation
config.use_transactional_fixtures = false

But no luck.

So the issue is, within the new thread, none of the created objects can be found in the database.

Some clues as I am digging:

  • The database is empty the entire time when I connect directly via a sql client. (When I add a break point in the test, and verify the object is persisted?)
  • Does factory girl / rspec actually commit the data to the database? If not, then a new thread would not be able to read it because it's still in the bin log or memory? (This prompts to me because I remember after_commit won't run in rspec)
Remediable answered 2/3, 2017 at 0:8 Comment(2)
Hi... this is not all your code... and definitely not all the code relevant to this question. Can you show us the code where you actually set up the objects that are in the database too please? also - the code that shows how you are testing whether the objects are in the database or not?Sd
@TarynEast sure, please see my edit.Remediable
R
3

Using Rails 5 this worked for me. Place within the describe:

self.use_transactional_tests = false

"use_transactional_fixtures" has been deprecated.

I also had to bypass DatabaseCleaner via the following:

it 'tests concurrency', bypass_cleaner: true do

end

Then in rails_helper (or where you have DatabaseCleaner settings)

config.around(:each) do |example|
  if example.metadata[:bypass_cleaner]
    example.run
  else
    # database cleaner code
  end
end

I hope this helps you or somebody else out :)

Rhynchocephalian answered 15/8, 2018 at 21:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.