aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTeddy Wing2017-12-12 17:01:49 +0100
committerTeddy Wing2017-12-12 17:01:49 +0100
commitdf13a9b7b26b568b5edcea590a2e867d26580d22 (patch)
tree6e3f668b8829e636cfb19bbbb493114977693333
parent55b995531b2504792dfa1b0314b5cc5b55a775ac (diff)
downloadchouette-core-df13a9b7b26b568b5edcea590a2e867d26580d22.tar.bz2
Referential: Lock table on :update
We had been locking the `referentials` table on :create, but we also want to handle :update. Paired on this with Johan. When I used: referential_2.metadatas << metadata_2 the referential was saved. To add the metadata without automatically saving the referential + metadata, Johan suggested using the nested attribute method: referential_2.metadatas_attributes = [metadata_2.attributes] This allows us to add the metadata and still use the `#save` method to lock the table. Also change the callback from `before_validation` to `before_save` because before_validation :lock_table, on: [:create, :update] didn't work. That caused an error in our `expect`, as the `be_valid` triggered the lock callback. To enable the callback on both :create and :update, use a `before_save` instead. Refs #5024
-rw-r--r--app/models/referential.rb2
-rw-r--r--spec/models/referential/referential_lock_during_creation_spec.rb51
2 files changed, 52 insertions, 1 deletions
diff --git a/app/models/referential.rb b/app/models/referential.rb
index b44bb15c6..6cdab8fb7 100644
--- a/app/models/referential.rb
+++ b/app/models/referential.rb
@@ -210,7 +210,7 @@ class Referential < ActiveRecord::Base
# Lock the `referentials` table to prevent duplicate referentials from being
# created simultaneously in separate transactions. This must be the last hook
# to minimise the duration of the lock.
- before_validation :lock_table, on: :create
+ before_save :lock_table, on: [:create, :update]
before_create :create_schema
after_create :clone_schema, if: :created_from
diff --git a/spec/models/referential/referential_lock_during_creation_spec.rb b/spec/models/referential/referential_lock_during_creation_spec.rb
index d17327d39..169162af9 100644
--- a/spec/models/referential/referential_lock_during_creation_spec.rb
+++ b/spec/models/referential/referential_lock_during_creation_spec.rb
@@ -76,6 +76,57 @@ RSpec.describe Referential, type: :model do
end
end
+ it "works asynchronously when one is updated", truncation: true do
+ begin
+ referential_1 = nil
+ referential_2 = nil
+
+ ActiveRecord::Base.transaction do
+ referential_1 = create(
+ :referential,
+ workbench: workbench,
+ organisation: workbench.organisation
+ )
+ referential_2 = referential_1.dup
+ referential_2.name = 'Another'
+ referential_2.slug = "#{referential_1.slug}_different"
+ referential_2.save!
+ end
+
+ metadata_2 = build(:referential_metadata, referential: nil)
+ metadata_1 = metadata_2.dup
+
+ thread_1 = Thread.new do
+ ActiveRecord::Base.transaction do
+ # seize LOCK
+ referential_1.metadatas_attributes = [metadata_1.attributes]
+ puts 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)
+ referential_2.metadatas_attributes = [metadata_2.attributes]
+ puts 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
+ Apartment::Tenant.drop(referential_1.slug) if referential_1.persisted?
+ Apartment::Tenant.drop(referential_2.slug) if referential_2.persisted?
+ end
+ end
+ end
+
context "when two Referentials are created at the same time" do
it "raises an error when the DB lock timeout is reached", truncation: true do
begin