From 8a77a94d88f1eb4c0b41f32ff9f381b034afcf45 Mon Sep 17 00:00:00 2001 From: Sam Davies Date: Fri, 20 Nov 2015 17:16:43 -0300 Subject: [PATCH] Add working test case --- Rakefile | 18 ++++++- test/concurrency_test.rb | 48 +++++++++++++++++++ test/dummy/app/models/concurrent_badger.rb | 3 ++ test/dummy/config/database.yml | 2 +- test/dummy/config/environments/development.rb | 10 ++-- test/dummy/config/environments/test.rb | 3 ++ ...0151120190645_create_concurrent_badgers.rb | 10 ++++ 7 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 test/concurrency_test.rb create mode 100644 test/dummy/app/models/concurrent_badger.rb create mode 100644 test/dummy/db/migrate/20151120190645_create_concurrent_badgers.rb diff --git a/Rakefile b/Rakefile index fb6214b..be1155f 100644 --- a/Rakefile +++ b/Rakefile @@ -38,6 +38,22 @@ namespace :db do # File.expand_path is executed directory of generated Rails app rakefile = File.expand_path('Rakefile', 'test/dummy/') command = "rake -f '%s' db:create" % rakefile - sh(command) unless ENV["DISABLE_CREATE"] + sh(command) + end + + task :drop do + # File.expand_path is executed directory of generated Rails app + rakefile = File.expand_path('Rakefile', 'test/dummy/') + command = "rake -f '%s' db:drop" % rakefile + sh(command) + end + + namespace :test do + task :prepare do + # File.expand_path is executed directory of generated Rails app + rakefile = File.expand_path('Rakefile', 'test/dummy/') + command = "rake -f '%s' db:test:prepare" % rakefile + sh(command) + end end end diff --git a/test/concurrency_test.rb b/test/concurrency_test.rb new file mode 100644 index 0000000..014ccff --- /dev/null +++ b/test/concurrency_test.rb @@ -0,0 +1,48 @@ +require 'test_helper' + +# Test Models: +# +# Answer - :scope => :question_id +# Comment - :scope => :question_id (with an AR default scope) +# Invoice - :scope => :account_id, :start_at => 1000 +# Product - :scope => :account_id, :start_at => lambda { |r| r.computed_start_value } +# Order - :scope => :non_existent_column +# User - :scope => :account_id, :column => :custom_sequential_id +# Address - :scope => :account_id ('sequential_id' does not exist) +# Email - :scope => [:emailable_id, :emailable_type] +# Subscription - no options +# Rating - :scope => :comment_id, skip: { |r| r.score == 0 } +# Monster - no options +# Zombie - STI, inherits from Monster +# Werewolf - STI, inherits from Monster + +# ConcurrentBadger - scope: :concurrent_burrow_id, +# NOT NULL constraint on sequential_id, +# UNIQUE constraint on sequential_id within concurrent_burrow_id scope + +if ENV['DB'] == 'postgresql' + class ActsAsSequencedTest < ActiveSupport::TestCase + self.use_transactional_fixtures = false + + def teardown + ConcurrentBadger.delete_all + end + + test "creates records concurrently without data races" do + Thread.abort_on_exception = true + range = (1..50) + + threads = [] + range.each do + threads << Thread.new do + ConcurrentBadger.create!(burrow_id: 1) + end + end + + threads.each(&:join) + + sequential_ids = ConcurrentBadger.pluck(:sequential_id) + assert_equal range.to_a, sequential_ids + end + end +end diff --git a/test/dummy/app/models/concurrent_badger.rb b/test/dummy/app/models/concurrent_badger.rb new file mode 100644 index 0000000..075fcfc --- /dev/null +++ b/test/dummy/app/models/concurrent_badger.rb @@ -0,0 +1,3 @@ +class ConcurrentBadger < ActiveRecord::Base + acts_as_sequenced scope: :burrow_id +end diff --git a/test/dummy/config/database.yml b/test/dummy/config/database.yml index 995c862..e29f6e5 100644 --- a/test/dummy/config/database.yml +++ b/test/dummy/config/database.yml @@ -15,7 +15,7 @@ postgresql: &postgresql min_messages: ERROR defaults: &defaults - pool: 5 + pool: 100 timeout: 5000 host: localhost <<: *<%= ENV['DB'] || "sqlite" %> diff --git a/test/dummy/config/environments/development.rb b/test/dummy/config/environments/development.rb index 82c74d1..cc52fe0 100644 --- a/test/dummy/config/environments/development.rb +++ b/test/dummy/config/environments/development.rb @@ -22,16 +22,12 @@ # Only use best-standards-support built into browsers config.action_dispatch.best_standards_support = :builtin - # Raise exception on mass assignment protection for Active Record models - config.active_record.mass_assignment_sanitizer = :strict - - # Log the query plan for queries taking more than this (works - # with SQLite, MySQL, and PostgreSQL) - config.active_record.auto_explain_threshold_in_seconds = 0.5 - # Do not compress assets config.assets.compress = false # Expands the lines which load the assets config.assets.debug = true + + # Required for Rails >= 4.2 + config.eager_load = true end diff --git a/test/dummy/config/environments/test.rb b/test/dummy/config/environments/test.rb index 23c3bd5..c9517b8 100644 --- a/test/dummy/config/environments/test.rb +++ b/test/dummy/config/environments/test.rb @@ -35,4 +35,7 @@ # Print deprecation notices to the stderr config.active_support.deprecation = :stderr + + # Required for Rails >= 4.2 + config.eager_load = true end diff --git a/test/dummy/db/migrate/20151120190645_create_concurrent_badgers.rb b/test/dummy/db/migrate/20151120190645_create_concurrent_badgers.rb new file mode 100644 index 0000000..5f36ee1 --- /dev/null +++ b/test/dummy/db/migrate/20151120190645_create_concurrent_badgers.rb @@ -0,0 +1,10 @@ +class CreateConcurrentBadgers < ActiveRecord::Migration + def change + create_table :concurrent_badgers do |t| + t.integer :sequential_id, null: false + t.integer :burrow_id + end + + add_index :concurrent_badgers, [:sequential_id, :burrow_id], unique: true, name: 'unique_concurrent' + end +end