diff --git a/README.md b/README.md index 2dec72f..d731c7c 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,14 @@ If you want to see if the current Thread is holding a lock, you can call `Tag.current_advisory_lock` which will return the name of the current lock. If no lock is currently held, `.current_advisory_lock` returns `nil`. +### ActiveRecord Query Cache + +You can optionally pass `disable_query_cache: true` to the options hash of +`with_advisory_lock` in order to disable ActiveRecord's query cache. This can +prevent problems when you query the database from within the lock and it returns +stale results. More info on why this can be a problem can be +[found here](https://github.com/ClosureTree/with_advisory_lock/issues/52) + ## Installation Add this line to your application's Gemfile: diff --git a/lib/with_advisory_lock/base.rb b/lib/with_advisory_lock/base.rb index a1d6912..19f6791 100644 --- a/lib/with_advisory_lock/base.rb +++ b/lib/with_advisory_lock/base.rb @@ -21,17 +21,18 @@ def lock_was_acquired? LockStackItem = Struct.new(:name, :shared) class Base - attr_reader :connection, :lock_name, :timeout_seconds, :shared, :transaction + attr_reader :connection, :lock_name, :timeout_seconds, :shared, :transaction, :disable_query_cache def initialize(connection, lock_name, options) options = { timeout_seconds: options } unless options.respond_to?(:fetch) - options.assert_valid_keys :timeout_seconds, :shared, :transaction + options.assert_valid_keys :timeout_seconds, :shared, :transaction, :disable_query_cache @connection = connection @lock_name = lock_name @timeout_seconds = options.fetch(:timeout_seconds, nil) @shared = options.fetch(:shared, false) @transaction = options.fetch(:transaction, false) + @disable_query_cache = options.fetch(:disable_query_cache, false) end def lock_str @@ -53,6 +54,16 @@ def already_locked? end def with_advisory_lock_if_needed(&block) + if disable_query_cache + return lock_and_yield do + ActiveRecord::Base.uncached(&block) + end + end + + lock_and_yield(&block) + end + + def lock_and_yield(&block) if already_locked? Result.new(true, yield) elsif timeout_seconds == 0 diff --git a/test/concern_test.rb b/test/concern_test.rb index 9bdfa24..5944207 100644 --- a/test/concern_test.rb +++ b/test/concern_test.rb @@ -19,3 +19,17 @@ assert_respond_to(Label.new, :advisory_lock_exists?) end end + +describe 'ActiveRecord query cache' do + it 'does not disable quary cache by default' do + ActiveRecord::Base.expects(:uncached).never + + Tag.with_advisory_lock('lock') { Tag.first } + end + + it 'can disable ActiveRecord query cache' do + ActiveRecord::Base.expects(:uncached).once + + Tag.with_advisory_lock('a-lock', disable_query_cache: true) { Tag.first } + end +end