diff options
| author | Teddy Wing | 2017-12-12 17:01:49 +0100 | 
|---|---|---|
| committer | Teddy Wing | 2017-12-12 17:01:49 +0100 | 
| commit | df13a9b7b26b568b5edcea590a2e867d26580d22 (patch) | |
| tree | 6e3f668b8829e636cfb19bbbb493114977693333 | |
| parent | 55b995531b2504792dfa1b0314b5cc5b55a775ac (diff) | |
| download | chouette-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.rb | 2 | ||||
| -rw-r--r-- | spec/models/referential/referential_lock_during_creation_spec.rb | 51 | 
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 | 
