diff --git a/CHANGELOG b/CHANGELOG index f68a13a..4ae3add 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +6.0.0 + +* Breaking changes + + * Set lock_storage and cache_storage separately + 5.0.0 * Enhancements / breaking changes diff --git a/README.md b/README.md index 60c6eb0..e35fa18 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@ Most caching libraries don't do locking, meaning that >1 process can be calculat ## Quickstart ```ruby -LockAndCache.storage = Redis.new +LockAndCache.lock_storage = Redis.new db: 3 +LockAndCache.cache_storage = Redis.new db: 4 LockAndCache.lock_and_cache(:stock_price, {company: 'MSFT', date: '2015-05-05'}, expires: 10, nil_expires: 1) do # get yer stock quote @@ -76,7 +77,8 @@ If an error is raised during calculation, that error is propagated to all waiter ### Setup ```ruby -LockAndCache.storage = Redis.new +LockAndCache.lock_storage = Redis.new db: 3 +LockAndCache.cache_storage = Redis.new db: 4 ``` It will use this redis for both locking and storing cached values. @@ -204,7 +206,8 @@ You can expire nil values with a different timeout (`nil_expires`) than other va ## Tunables -* `LockAndCache.storage=[redis]` +* `LockAndCache.lock_storage=[redis]` +* `LockAndCache.cache_storage=[redis]` * `ENV['LOCK_AND_CACHE_DEBUG']='true'` if you want some debugging output on `$stderr` ## Few dependencies diff --git a/lib/lock_and_cache.rb b/lib/lock_and_cache.rb index 2fc54f2..8484cbd 100644 --- a/lib/lock_and_cache.rb +++ b/lib/lock_and_cache.rb @@ -20,15 +20,26 @@ module LockAndCache class TimeoutWaitingForLock < StandardError; end - # @param redis_connection [Redis] A redis connection to be used for lock and cached value storage - def LockAndCache.storage=(redis_connection) + # @param redis_connection [Redis] A redis connection to be used for lock storage + def LockAndCache.lock_storage=(redis_connection) raise "only redis for now" unless redis_connection.class.to_s == 'Redis' - @storage = redis_connection + @lock_storage = redis_connection end # @return [Redis] The redis connection used for lock and cached value storage - def LockAndCache.storage - @storage + def LockAndCache.lock_storage + @lock_storage + end + + # @param redis_connection [Redis] A redis connection to be used for cached value storage + def LockAndCache.cache_storage=(redis_connection) + raise "only redis for now" unless redis_connection.class.to_s == 'Redis' + @cache_storage = redis_connection + end + + # @return [Redis] The redis connection used for cached value storage + def LockAndCache.cache_storage + @cache_storage end # @param logger [Logger] A logger. @@ -41,13 +52,22 @@ def LockAndCache.logger @logger end - # Flush LockAndCache's storage. + # Flush LockAndCache's cached value storage. + # + # @note If you are sharing a redis database, it will clear it... + # + # @note If you want to clear a single key, try `LockAndCache.clear(key)` (standalone mode) or `#lock_and_cache_clear(method_id, *key_parts)` in context mode. + def LockAndCache.flush_cache + cache_storage.flushdb + end + + # Flush LockAndCache's lock storage. # # @note If you are sharing a redis database, it will clear it... # # @note If you want to clear a single key, try `LockAndCache.clear(key)` (standalone mode) or `#lock_and_cache_clear(method_id, *key_parts)` in context mode. - def LockAndCache.flush - storage.flushdb + def LockAndCache.flush_locks + lock_storage.flushdb end # Lock and cache based on a key. diff --git a/lib/lock_and_cache/action.rb b/lib/lock_and_cache/action.rb index c250046..b229490 100644 --- a/lib/lock_and_cache/action.rb +++ b/lib/lock_and_cache/action.rb @@ -32,8 +32,12 @@ def lock_digest @lock_digest ||= key.lock_digest end - def storage - @storage ||= LockAndCache.storage or raise("must set LockAndCache.storage=[Redis]") + def lock_storage + @lock_storage ||= LockAndCache.lock_storage or raise("must set LockAndCache.lock_storage=[Redis]") + end + + def cache_storage + @cache_storage ||= LockAndCache.cache_storage or raise("must set LockAndCache.cache_storage=[Redis]") end def load_existing(existing) @@ -51,7 +55,7 @@ def perform raise "heartbeat_expires must be >= 2 seconds" unless heartbeat_expires >= 2 heartbeat_frequency = (heartbeat_expires / 2).ceil LockAndCache.logger.debug { "[lock_and_cache] A1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" } - if storage.exists(digest) and (existing = storage.get(digest)).is_a?(String) + if cache_storage.exists(digest) and (existing = cache_storage.get(digest)).is_a?(String) return load_existing(existing) end LockAndCache.logger.debug { "[lock_and_cache] B1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" } @@ -60,14 +64,14 @@ def perform acquired = false begin Timeout.timeout(max_lock_wait, TimeoutWaitingForLock) do - until storage.set(lock_digest, lock_secret, nx: true, ex: heartbeat_expires) + until lock_storage.set(lock_digest, lock_secret, nx: true, ex: heartbeat_expires) LockAndCache.logger.debug { "[lock_and_cache] C1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" } sleep rand end acquired = true end LockAndCache.logger.debug { "[lock_and_cache] D1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" } - if storage.exists(digest) and (existing = storage.get(digest)).is_a?(String) + if cache_storage.exists(digest) and (existing = cache_storage.get(digest)).is_a?(String) LockAndCache.logger.debug { "[lock_and_cache] E1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" } retval = load_existing existing end @@ -83,8 +87,8 @@ def perform break if done LockAndCache.logger.debug { "[lock_and_cache] heartbeat2 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" } # FIXME use lua to check the value - raise "unexpectedly lost lock for #{key.debug}" unless storage.get(lock_digest) == lock_secret - storage.set lock_digest, lock_secret, xx: true, ex: heartbeat_expires + raise "unexpectedly lost lock for #{key.debug}" unless lock_storage.get(lock_digest) == lock_secret + lock_storage.set lock_digest, lock_secret, xx: true, ex: heartbeat_expires end end begin @@ -100,32 +104,32 @@ def perform end end ensure - storage.del lock_digest if acquired + lock_storage.del lock_digest if acquired end retval end def set_error(exception) - storage.set digest, ::Marshal.dump(ERROR_MAGIC_KEY => exception.message), ex: 1 + cache_storage.set digest, ::Marshal.dump(ERROR_MAGIC_KEY => exception.message), ex: 1 end NIL = Marshal.dump nil def set_nil if nil_expires - storage.set digest, NIL, ex: nil_expires + cache_storage.set digest, NIL, ex: nil_expires elsif expires - storage.set digest, NIL, ex: expires + cache_storage.set digest, NIL, ex: expires else - storage.set digest, NIL + cache_storage.set digest, NIL end end def set_non_nil(retval) raise "expected not null #{retval.inspect}" if retval.nil? if expires - storage.set digest, ::Marshal.dump(retval), ex: expires + cache_storage.set digest, ::Marshal.dump(retval), ex: expires else - storage.set digest, ::Marshal.dump(retval) + cache_storage.set digest, ::Marshal.dump(retval) end end end diff --git a/lib/lock_and_cache/key.rb b/lib/lock_and_cache/key.rb index 506f29e..064ab2d 100644 --- a/lib/lock_and_cache/key.rb +++ b/lib/lock_and_cache/key.rb @@ -98,18 +98,17 @@ def key end def locked? - LockAndCache.storage.exists lock_digest + LockAndCache.lock_storage.exists lock_digest end def cached? - LockAndCache.storage.exists digest + LockAndCache.cache_storage.exists digest end def clear LockAndCache.logger.debug { "[lock_and_cache] clear #{debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" } - storage = LockAndCache.storage - storage.del digest - storage.del lock_digest + LockAndCache.cache_storage.del digest + LockAndCache.lock_storage.del lock_digest end alias debug key diff --git a/lib/lock_and_cache/version.rb b/lib/lock_and_cache/version.rb index 3c33bec..2a07477 100644 --- a/lib/lock_and_cache/version.rb +++ b/lib/lock_and_cache/version.rb @@ -1,3 +1,3 @@ module LockAndCache - VERSION = '5.0.0' + VERSION = '6.0.0' end diff --git a/spec/lock_and_cache_spec.rb b/spec/lock_and_cache_spec.rb index c5d45d4..b071258 100644 --- a/spec/lock_and_cache_spec.rb +++ b/spec/lock_and_cache_spec.rb @@ -140,7 +140,8 @@ def lock_and_cache_key describe LockAndCache do before do - LockAndCache.flush + LockAndCache.flush_locks + LockAndCache.flush_cache end it 'has a version number' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8fc1153..9c02614 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,7 +4,8 @@ require 'timeout' require 'redis' -LockAndCache.storage = Redis.new +LockAndCache.lock_storage = Redis.new db: 3 +LockAndCache.cache_storage = Redis.new db: 4 require 'thread/pool'