diff options
| author | Johan Van Ryseghem | 2017-12-13 15:12:48 +0100 |
|---|---|---|
| committer | Teddy Wing | 2017-12-13 15:16:03 +0100 |
| commit | c21bcff5849190d87a2f2869a0f4714de118cb15 (patch) | |
| tree | 72c6ebca48261a77d8df69cca73a08b82e453386 /spec/models/referential | |
| parent | 83a0f74b02d9dc2b3e1eb6b3f7c7c068148f0856 (diff) | |
| download | chouette-core-c21bcff5849190d87a2f2869a0f4714de118cb15.tar.bz2 | |
referential_lock_during_creation_spec: Extract method for threads
Refactor these tests to abstract the thread & transaction creation &
handling. Thread creation and joining can now live in one place apart
from the tests, and the tests have an interface to set up concurrent
referential saving.
Thanks to Johan for this refactor.
Refs #5024
Diffstat (limited to 'spec/models/referential')
| -rw-r--r-- | spec/models/referential/referential_lock_during_creation_spec.rb | 110 |
1 files changed, 57 insertions, 53 deletions
diff --git a/spec/models/referential/referential_lock_during_creation_spec.rb b/spec/models/referential/referential_lock_during_creation_spec.rb index a6696b9c3..9b5bef16d 100644 --- a/spec/models/referential/referential_lock_during_creation_spec.rb +++ b/spec/models/referential/referential_lock_during_creation_spec.rb @@ -1,6 +1,52 @@ RSpec.describe Referential, type: :model do let (:workbench) { create(:workbench) } + def with_a_mutual_lock timeout: false + @with_a_mutual_lock = true + yield + thread_1 = Thread.new do + ActiveRecord::Base.transaction do + # seize LOCK + @locking_thread_content.try :call + sleep 10 + # release LOCK + end + end + + thread_2 = Thread.new do + sleep 5 + ActiveRecord::Base.transaction do + if timeout + ActiveRecord::Base.connection.execute "SET lock_timeout = '1s'" + end + # waits for LOCK, (because of sleep 5) + @waiting_thread_content.try :call + # when lock was eventually obtained validation failed + end + end + + thread_1.join + if timeout + expect do + thread_2.join + end.to raise_error(TableLockTimeoutError) + else + thread_2.join + end + @locking_thread_content = nil + @waiting_thread_content = nil + @with_a_mutual_lock = false + end + + def locking_thread + raise "this method is intended to be called inside a `with_a_mutual_lock`" unless @with_a_mutual_lock + @locking_thread_content = yield + end + + def waiting_thread + @waiting_thread_content = yield + end + context "when two identical Referentials are created, only one is saved" do it "works synchronously" do referential_1 = build( @@ -41,28 +87,16 @@ RSpec.describe Referential, type: :model do referential_1.metadatas << metadata_1 referential_2.metadatas << metadata_2 - thread_1 = Thread.new do - ActiveRecord::Base.transaction do - # seize LOCK + with_a_mutual_lock do + locking_thread do referential_1.save - sleep 10 - # release LOCK end - end - - thread_2 = Thread.new do - sleep 5 - ActiveRecord::Base.transaction do - # waits for LOCK, (because of sleep 5) + waiting_thread do referential_2.save - # when lock was eventually obtained validation failed referential_3 = create(:referential) end end - thread_1.join - thread_2.join - expect(referential_1).to be_persisted expect(referential_2).not_to be_persisted expect(referential_3).to be_persisted @@ -95,29 +129,17 @@ RSpec.describe Referential, type: :model do metadata_2 = build(:referential_metadata, referential: nil) metadata_1 = metadata_2.dup - - thread_1 = Thread.new do - ActiveRecord::Base.transaction do - # seize LOCK + with_a_mutual_lock do + locking_thread do referential_1.metadatas_attributes = [metadata_1.attributes] - puts referential_1.save - sleep 10 - # release LOCK + referential_1.save end - end - - thread_2 = Thread.new do - sleep 5 - ActiveRecord::Base.transaction do - # waits for LOCK, (because of sleep 5) + waiting_thread do referential_2.metadatas_attributes = [metadata_2.attributes] - puts referential_2.save + referential_2.save end end - thread_1.join - thread_2.join - expect(referential_1).to be_valid expect(referential_2).not_to be_valid ensure @@ -144,34 +166,16 @@ RSpec.describe Referential, type: :model do referential_1.metadatas << metadata_1 referential_2.metadatas << metadata_2 - - thread_1 = Thread.new do - ActiveRecord::Base.transaction do - ActiveRecord::Base.connection.execute("SET LOCAL lock_timeout = '1s'") - - # seize LOCK + with_a_mutual_lock timeout: true do + locking_thread do referential_1.save - sleep 10 - # release LOCK end - end - - thread_2 = Thread.new do - sleep 5 - ActiveRecord::Base.transaction do - ActiveRecord::Base.connection.execute("SET LOCAL lock_timeout = '1s'") - # waits for LOCK, (because of sleep 5) + waiting_thread do referential_2.save - # when lock was eventually obtained validation failed referential_3 = create(:referential) end end - thread_1.join - expect do - thread_2.join - end.to raise_error(TableLockTimeoutError) - expect(referential_1).to be_persisted ensure Apartment::Tenant.drop(referential_1.slug) if referential_1.persisted? |
