Module: LockAndCache

Defined in:
lib/lock_and_cache.rb,
lib/lock_and_cache/key.rb,
lib/lock_and_cache/action.rb,
lib/lock_and_cache/version.rb

Overview

Lock and cache using redis!

Most caching libraries don’t do locking, meaning that >1 process can be calculating a cached value at the same time. Since you presumably cache things because they cost CPU, database reads, or money, doesn’t it make sense to lock while caching?

Defined Under Namespace

Classes: TimeoutWaitingForLock

Constant Summary collapse

DEFAULT_MAX_LOCK_WAIT =

1 day in seconds

60 * 60 * 24
DEFAULT_HEARTBEAT_EXPIRES =

32 seconds

32
VERSION =
'5.0.0'

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.cached?(*key_parts) ⇒ Boolean

Note:

Standalone mode. See also “context mode,” where you mix LockAndCache into a class and call it from within its methods.

Check if a key is cached already



87
88
89
90
# File 'lib/lock_and_cache.rb', line 87

def LockAndCache.cached?(*key_parts)
  key = LockAndCache::Key.new key_parts
  key.cached?
end

.clear(*key_parts) ⇒ Object

Note:

Standalone mode. See also “context mode,” where you mix LockAndCache into a class and call it from within its methods.

Clear a single key



71
72
73
74
# File 'lib/lock_and_cache.rb', line 71

def LockAndCache.clear(*key_parts)
  key = LockAndCache::Key.new key_parts
  key.clear
end

.flushObject

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.

Flush LockAndCache’s storage.



49
50
51
# File 'lib/lock_and_cache.rb', line 49

def LockAndCache.flush
  storage.flushdb
end

.heartbeat_expires=(seconds) ⇒ Object

Note:

Can be overridden by putting ‘heartbeat_expires:` in your call to `#lock_and_cache`



107
108
109
110
111
# File 'lib/lock_and_cache.rb', line 107

def LockAndCache.heartbeat_expires=(seconds)
  memo = seconds.to_f
  raise "heartbeat_expires must be greater than 2 seconds" unless memo >= 2
  @heartbeat_expires = memo
end

.lock_and_cache(*key_parts_and_options, &blk) ⇒ Object

Note:

Standalone mode. See also “context mode,” where you mix LockAndCache into a class and call it from within its methods.

Note:

A single hash arg is treated as a cache key, e.g. ‘LockAndCache.lock_and_cache(foo: :bar, expires: 100)` will be treated as a cache key of `foo: :bar, expires: 100` (which is probably wrong!!!). Try `LockAndCache.lock_and_cache({ foo: :bar }, expires: 100)` instead. This is the opposite of context mode.

Lock and cache based on a key.



60
61
62
63
64
65
66
# File 'lib/lock_and_cache.rb', line 60

def LockAndCache.lock_and_cache(*key_parts_and_options, &blk)
  options = (key_parts_and_options.last.is_a?(Hash) && key_parts_and_options.length > 1) ? key_parts_and_options.pop : {}
  raise "need a cache key" unless key_parts_and_options.length > 0
  key = LockAndCache::Key.new key_parts_and_options
  action = LockAndCache::Action.new key, options, blk
  action.perform
end

.locked?(*key_parts) ⇒ Boolean

Note:

Standalone mode. See also “context mode,” where you mix LockAndCache into a class and call it from within its methods.

Check if a key is locked



79
80
81
82
# File 'lib/lock_and_cache.rb', line 79

def LockAndCache.locked?(*key_parts)
  key = LockAndCache::Key.new key_parts
  key.locked?
end

.loggerLogger



40
41
42
# File 'lib/lock_and_cache.rb', line 40

def LockAndCache.logger
  @logger
end

.logger=(logger) ⇒ Object



35
36
37
# File 'lib/lock_and_cache.rb', line 35

def LockAndCache.logger=(logger)
  @logger = logger
end

.max_lock_wait=(seconds) ⇒ Object

Note:

Can be overridden by putting ‘max_lock_wait:` in your call to `#lock_and_cache`



95
96
97
# File 'lib/lock_and_cache.rb', line 95

def LockAndCache.max_lock_wait=(seconds)
  @max_lock_wait = seconds.to_f
end

.storageRedis



30
31
32
# File 'lib/lock_and_cache.rb', line 30

def LockAndCache.storage
  @storage
end

.storage=(redis_connection) ⇒ Object



24
25
26
27
# File 'lib/lock_and_cache.rb', line 24

def LockAndCache.storage=(redis_connection)
  raise "only redis for now" unless redis_connection.class.to_s == 'Redis'
  @storage = redis_connection
end

Instance Method Details

#lock_and_cache(*key_parts_and_options, &blk) ⇒ Object

Note:

Subject mode - this is expected to be called on an object whose class has LockAndCache mixed in. See also standalone mode.

Note:

A single hash arg is treated as an options hash, e.g. ‘lock_and_cache(expires: 100)` will be treated as options `expires: 100`. This is the opposite of standalone mode.

Lock and cache a method given key parts.

The cache key will automatically include the class name of the object calling it (the context!) and the name of the method it is called from.



145
146
147
148
149
150
# File 'lib/lock_and_cache.rb', line 145

def lock_and_cache(*key_parts_and_options, &blk)
  options = key_parts_and_options.last.is_a?(Hash) ? key_parts_and_options.pop : {}
  key = LockAndCache::Key.new key_parts_and_options, context: self, caller: caller
  action = LockAndCache::Action.new key, options, blk
  action.perform
end

#lock_and_cache_clear(method_id, *key_parts) ⇒ Object

Note:

Subject mode - this is expected to be called on an object whose class has LockAndCache mixed in. See also standalone mode.

Clear a lock and cache given exactly the method and exactly the same arguments



129
130
131
132
# File 'lib/lock_and_cache.rb', line 129

def lock_and_cache_clear(method_id, *key_parts)
  key = LockAndCache::Key.new key_parts, context: self, method_id: method_id
  key.clear
end

#lock_and_cache_locked?(method_id, *key_parts) ⇒ Boolean

Note:

Subject mode - this is expected to be called on an object whose class has LockAndCache mixed in. See also standalone mode.

Check if a method is locked on an object.



121
122
123
124
# File 'lib/lock_and_cache.rb', line 121

def lock_and_cache_locked?(method_id, *key_parts)
  key = LockAndCache::Key.new key_parts, context: self, method_id: method_id
  key.locked?
end