lock_and_cache 4.0.4 → 6.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 4a3eaf745d9a9532d2062ea332fb63420fabbdd9
4
- data.tar.gz: d1f4ec88da5e0f728da43d9b2f40d536c4224498
2
+ SHA256:
3
+ metadata.gz: c89613cc8fdd7ed23861149ccfc49e664af988d6b917757f09c18b18b5415f0b
4
+ data.tar.gz: 286efd9473fedfbd8e7de5175d9a4ec374bde72757d103bd9c964faa6af1bc70
5
5
  SHA512:
6
- metadata.gz: f34529d617b048841b0d33234619846f49a7367ff5627e653298098f0780401314f37df6aa9afe73d967ad358eeaffc3fd0d70ccbc043ecb6a8c1b360c2b61f7
7
- data.tar.gz: d006e7f9e798425f6137d5611c3688b3e78fd906e15390ad0ff015f0834f3fd3e9a4d820c4ca546fa606f17f9ea481296d7f444dc0189cb48b4e368643251648
6
+ metadata.gz: 15f13a8204942588e436ddd5a0eec963148d179a10bf3268401316b8479f2857f50e5e83a47d324765c57f614c7e11b2656b7643c139ebbce6e6b67f1c2969ba
7
+ data.tar.gz: 28d901547a8b96a9065514151149176eef91c5ed9fb76274b7a7e655bba4f8f584b6246e20534342022ae1bddbfe4918cbe73cedcb7936fa886962b117c698bd
@@ -1,6 +1,7 @@
1
1
  language: ruby
2
2
  sudo: false
3
3
  rvm:
4
- - 2.1.2
4
+ - 2.3.0
5
+ - 2.4.1
5
6
  services:
6
7
  - redis-server
data/CHANGELOG CHANGED
@@ -1,3 +1,40 @@
1
+ 6.0.1
2
+
3
+ * Enhancements
4
+
5
+ * Use Redis#exists? to avoid deprec warning
6
+
7
+ 6.0.0
8
+
9
+ * Breaking changes
10
+
11
+ * Set lock_storage and cache_storage separately
12
+
13
+ 5.0.0
14
+
15
+ * Enhancements / breaking changes
16
+
17
+ * Propagate errors to all waiters up to 1 second after the error
18
+ * Stop using redlock, just use plain single-node Redis locking
19
+
20
+ 4.0.6
21
+
22
+ * ?
23
+
24
+ * Don't test on ruby 2.1
25
+
26
+ * Enhancements
27
+
28
+ * LockAndCache.cached?(*key_parts) to check if a value is cached
29
+
30
+ 4.0.5 / 2017-04-01
31
+
32
+ * Enhancements
33
+
34
+ * allow dates and times in keys
35
+ * Test on ruby 2.3.0 and 2.4.1
36
+ * 2x faster key generation
37
+
1
38
  4.0.4 / 2016-04-11
2
39
 
3
40
  * Bug fixes
data/README.md CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/seamusabshere/lock_and_cache.svg?branch=master&v=2.2.0)](https://travis-ci.org/seamusabshere/lock_and_cache)
4
4
  [![Code Climate](https://codeclimate.com/github/seamusabshere/lock_and_cache/badges/gpa.svg?v=2.2.0)](https://codeclimate.com/github/seamusabshere/lock_and_cache)
5
- [![Dependency Status](https://gemnasium.com/seamusabshere/lock_and_cache.svg?v=2.2.0)](https://gemnasium.com/seamusabshere/lock_and_cache)
6
5
  [![Gem Version](https://badge.fury.io/rb/lock_and_cache.svg?v=2.2.0)](http://badge.fury.io/rb/lock_and_cache)
7
6
  [![Security](https://hakiri.io/github/seamusabshere/lock_and_cache/master.svg?v=2.2.0)](https://hakiri.io/github/seamusabshere/lock_and_cache/master)
8
7
  [![Inline docs](http://inch-ci.org/github/seamusabshere/lock_and_cache.svg?branch=master&v=2.2.0)](http://inch-ci.org/github/seamusabshere/lock_and_cache)
@@ -14,7 +13,8 @@ Most caching libraries don't do locking, meaning that >1 process can be calculat
14
13
  ## Quickstart
15
14
 
16
15
  ```ruby
17
- LockAndCache.storage = Redis.new
16
+ LockAndCache.lock_storage = Redis.new db: 3
17
+ LockAndCache.cache_storage = Redis.new db: 4
18
18
 
19
19
  LockAndCache.lock_and_cache(:stock_price, {company: 'MSFT', date: '2015-05-05'}, expires: 10, nil_expires: 1) do
20
20
  # get yer stock quote
@@ -27,9 +27,9 @@ end
27
27
 
28
28
  ## Sponsor
29
29
 
30
- <p><a href="http://faraday.io"><img src="http://cdn2.hubspot.net/hubfs/515497/img/logo.svg" alt="Faraday logo"/></a></p>
30
+ <p><a href="https://www.faraday.io"><img src="https://s3.amazonaws.com/faraday-assets/files/img/logo.svg" alt="Faraday logo"/></a></p>
31
31
 
32
- We use [`lock_and_cache`](https://github.com/seamusabshere/lock_and_cache) for [data-driven marketing at Faraday](http://faraday.io).
32
+ We use [`lock_and_cache`](https://github.com/seamusabshere/lock_and_cache) for [B2C customer intelligence at Faraday](https://www.faraday.io).
33
33
 
34
34
  ## TOC
35
35
 
@@ -70,21 +70,24 @@ We use [`lock_and_cache`](https://github.com/seamusabshere/lock_and_cache) for [
70
70
 
71
71
  As you can see, most caching libraries only take care of (1) and (4) (well, and (5) of course).
72
72
 
73
+ If an error is raised during calculation, that error is propagated to all waiters for 1 second.
74
+
73
75
  ## Practice
74
76
 
75
77
  ### Setup
76
78
 
77
79
  ```ruby
78
- LockAndCache.storage = Redis.new
80
+ LockAndCache.lock_storage = Redis.new db: 3
81
+ LockAndCache.cache_storage = Redis.new db: 4
79
82
  ```
80
83
 
81
84
  It will use this redis for both locking and storing cached values.
82
85
 
83
86
  ### Locking
84
87
 
85
- Based on [antirez's Redlock algorithm](http://redis.io/topics/distlock).
88
+ Just uses Redis naive locking with NX.
86
89
 
87
- Above and beyond Redlock, a 32-second heartbeat is used that will clear the lock if a process is killed. This is implemented using lock extensions.
90
+ A 32-second heartbeat is used that will clear the lock if a process is killed.
88
91
 
89
92
  ### Caching
90
93
 
@@ -189,7 +192,7 @@ Most caching libraries don't do locking, meaning that >1 process can be calculat
189
192
 
190
193
  ### Heartbeat
191
194
 
192
- If the process holding the lock dies, we automatically remove the lock so somebody else can do it (using heartbeats and redlock extends).
195
+ If the process holding the lock dies, we automatically remove the lock so somebody else can do it (using heartbeats).
193
196
 
194
197
  ### Context mode
195
198
 
@@ -203,14 +206,14 @@ You can expire nil values with a different timeout (`nil_expires`) than other va
203
206
 
204
207
  ## Tunables
205
208
 
206
- * `LockAndCache.storage=[redis]`
209
+ * `LockAndCache.lock_storage=[redis]`
210
+ * `LockAndCache.cache_storage=[redis]`
207
211
  * `ENV['LOCK_AND_CACHE_DEBUG']='true'` if you want some debugging output on `$stderr`
208
212
 
209
213
  ## Few dependencies
210
214
 
211
215
  * [activesupport](https://rubygems.org/gems/activesupport) (come on, it's the bomb)
212
216
  * [redis](https://github.com/redis/redis-rb)
213
- * [redlock](https://github.com/leandromoreira/redlock-rb)
214
217
 
215
218
  ## Known issues
216
219
 
@@ -0,0 +1,87 @@
1
+ require 'set'
2
+ require 'date'
3
+ require 'benchmark/ips'
4
+
5
+ ALLOWED_IN_KEYS = [
6
+ ::String,
7
+ ::Symbol,
8
+ ::Numeric,
9
+ ::TrueClass,
10
+ ::FalseClass,
11
+ ::NilClass,
12
+ ::Integer,
13
+ ::Float,
14
+ ::Date,
15
+ ::DateTime,
16
+ ::Time,
17
+ ].to_set
18
+ parts = RUBY_VERSION.split('.').map(&:to_i)
19
+ unless parts[0] >= 2 and parts[1] >= 4
20
+ ALLOWED_IN_KEYS << ::Fixnum
21
+ ALLOWED_IN_KEYS << ::Bignum
22
+ end
23
+
24
+ EXAMPLES = [
25
+ 'hi',
26
+ :there,
27
+ 123,
28
+ 123.54,
29
+ 1e99,
30
+ 123456789 ** 2,
31
+ 1e999,
32
+ true,
33
+ false,
34
+ nil,
35
+ Date.new(2015,1,1),
36
+ Time.now,
37
+ DateTime.now,
38
+ Mutex,
39
+ Mutex.new,
40
+ Benchmark,
41
+ { hi: :world },
42
+ [[]],
43
+ Fixnum,
44
+ Struct,
45
+ Struct.new(:a),
46
+ Struct.new(:a).new(123)
47
+ ]
48
+ EXAMPLES.each do |example|
49
+ puts "#{example} -> #{example.class}"
50
+ end
51
+
52
+ puts
53
+
54
+ [
55
+ Date.new(2015,1,1),
56
+ Time.now,
57
+ DateTime.now,
58
+ ].each do |x|
59
+ puts x.to_s
60
+ end
61
+
62
+ puts
63
+
64
+ EXAMPLES.each do |example|
65
+ a = ALLOWED_IN_KEYS.any? { |thing| example.is_a?(thing) }
66
+ b = ALLOWED_IN_KEYS.include? example.class
67
+ unless a == b
68
+ raise "#{example.inspect}: #{a.inspect} vs #{b.inspect}"
69
+ end
70
+ end
71
+
72
+ Benchmark.ips do |x|
73
+ x.report("any") do
74
+ example = EXAMPLES.sample
75
+ y = ALLOWED_IN_KEYS.any? { |thing| example.is_a?(thing) }
76
+ a = 1
77
+ y
78
+ end
79
+
80
+ x.report("include") do
81
+ example = EXAMPLES.sample
82
+ y = ALLOWED_IN_KEYS.include? example.class
83
+ a = 1
84
+ y
85
+ end
86
+
87
+ end
@@ -3,7 +3,6 @@ require 'timeout'
3
3
  require 'digest/sha1'
4
4
  require 'base64'
5
5
  require 'redis'
6
- require 'redlock'
7
6
  require 'active_support'
8
7
  require 'active_support/core_ext'
9
8
 
@@ -21,16 +20,26 @@ module LockAndCache
21
20
 
22
21
  class TimeoutWaitingForLock < StandardError; end
23
22
 
24
- # @param redis_connection [Redis] A redis connection to be used for lock and cached value storage
25
- def LockAndCache.storage=(redis_connection)
23
+ # @param redis_connection [Redis] A redis connection to be used for lock storage
24
+ def LockAndCache.lock_storage=(redis_connection)
26
25
  raise "only redis for now" unless redis_connection.class.to_s == 'Redis'
27
- @storage = redis_connection
28
- @lock_manager = Redlock::Client.new [redis_connection], retry_count: 1
26
+ @lock_storage = redis_connection
29
27
  end
30
28
 
31
29
  # @return [Redis] The redis connection used for lock and cached value storage
32
- def LockAndCache.storage
33
- @storage
30
+ def LockAndCache.lock_storage
31
+ @lock_storage
32
+ end
33
+
34
+ # @param redis_connection [Redis] A redis connection to be used for cached value storage
35
+ def LockAndCache.cache_storage=(redis_connection)
36
+ raise "only redis for now" unless redis_connection.class.to_s == 'Redis'
37
+ @cache_storage = redis_connection
38
+ end
39
+
40
+ # @return [Redis] The redis connection used for cached value storage
41
+ def LockAndCache.cache_storage
42
+ @cache_storage
34
43
  end
35
44
 
36
45
  # @param logger [Logger] A logger.
@@ -43,13 +52,22 @@ module LockAndCache
43
52
  @logger
44
53
  end
45
54
 
46
- # Flush LockAndCache's storage.
55
+ # Flush LockAndCache's cached value storage.
47
56
  #
48
57
  # @note If you are sharing a redis database, it will clear it...
49
58
  #
50
59
  # @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.
51
- def LockAndCache.flush
52
- storage.flushdb
60
+ def LockAndCache.flush_cache
61
+ cache_storage.flushdb
62
+ end
63
+
64
+ # Flush LockAndCache's lock storage.
65
+ #
66
+ # @note If you are sharing a redis database, it will clear it...
67
+ #
68
+ # @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.
69
+ def LockAndCache.flush_locks
70
+ lock_storage.flushdb
53
71
  end
54
72
 
55
73
  # Lock and cache based on a key.
@@ -83,6 +101,14 @@ module LockAndCache
83
101
  key.locked?
84
102
  end
85
103
 
104
+ # Check if a key is cached already
105
+ #
106
+ # @note Standalone mode. See also "context mode," where you mix LockAndCache into a class and call it from within its methods.
107
+ def LockAndCache.cached?(*key_parts)
108
+ key = LockAndCache::Key.new key_parts
109
+ key.cached?
110
+ end
111
+
86
112
  # @param seconds [Numeric] Maximum wait time to get a lock
87
113
  #
88
114
  # @note Can be overridden by putting `max_lock_wait:` in your call to `#lock_and_cache`
@@ -109,11 +135,6 @@ module LockAndCache
109
135
  @heartbeat_expires || DEFAULT_HEARTBEAT_EXPIRES
110
136
  end
111
137
 
112
- # @private
113
- def LockAndCache.lock_manager
114
- @lock_manager
115
- end
116
-
117
138
  # Check if a method is locked on an object.
118
139
  #
119
140
  # @note Subject mode - this is expected to be called on an object whose class has LockAndCache mixed in. See also standalone mode.
@@ -1,6 +1,8 @@
1
1
  module LockAndCache
2
2
  # @private
3
3
  class Action
4
+ ERROR_MAGIC_KEY = :lock_and_cache_error
5
+
4
6
  attr_reader :key
5
7
  attr_reader :options
6
8
  attr_reader :blk
@@ -30,8 +32,21 @@ module LockAndCache
30
32
  @lock_digest ||= key.lock_digest
31
33
  end
32
34
 
33
- def storage
34
- @storage ||= LockAndCache.storage or raise("must set LockAndCache.storage=[Redis]")
35
+ def lock_storage
36
+ @lock_storage ||= LockAndCache.lock_storage or raise("must set LockAndCache.lock_storage=[Redis]")
37
+ end
38
+
39
+ def cache_storage
40
+ @cache_storage ||= LockAndCache.cache_storage or raise("must set LockAndCache.cache_storage=[Redis]")
41
+ end
42
+
43
+ def load_existing(existing)
44
+ v = ::Marshal.load(existing)
45
+ if v.is_a?(::Hash) and (founderr = v[ERROR_MAGIC_KEY])
46
+ raise "Another LockAndCache process raised #{founderr}"
47
+ else
48
+ v
49
+ end
35
50
  end
36
51
 
37
52
  def perform
@@ -40,24 +55,25 @@ module LockAndCache
40
55
  raise "heartbeat_expires must be >= 2 seconds" unless heartbeat_expires >= 2
41
56
  heartbeat_frequency = (heartbeat_expires / 2).ceil
42
57
  LockAndCache.logger.debug { "[lock_and_cache] A1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
43
- if storage.exists(digest) and (existing = storage.get(digest)).is_a?(String)
44
- return ::Marshal.load(existing)
58
+ if cache_storage.exists?(digest) and (existing = cache_storage.get(digest)).is_a?(String)
59
+ return load_existing(existing)
45
60
  end
46
61
  LockAndCache.logger.debug { "[lock_and_cache] B1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
47
62
  retval = nil
48
- lock_manager = LockAndCache.lock_manager
49
- lock_info = nil
63
+ lock_secret = SecureRandom.hex 16
64
+ acquired = false
50
65
  begin
51
66
  Timeout.timeout(max_lock_wait, TimeoutWaitingForLock) do
52
- until lock_info = lock_manager.lock(lock_digest, heartbeat_expires*1000)
67
+ until lock_storage.set(lock_digest, lock_secret, nx: true, ex: heartbeat_expires)
53
68
  LockAndCache.logger.debug { "[lock_and_cache] C1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
54
69
  sleep rand
55
70
  end
71
+ acquired = true
56
72
  end
57
73
  LockAndCache.logger.debug { "[lock_and_cache] D1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
58
- if storage.exists(digest) and (existing = storage.get(digest)).is_a?(String)
74
+ if cache_storage.exists?(digest) and (existing = cache_storage.get(digest)).is_a?(String)
59
75
  LockAndCache.logger.debug { "[lock_and_cache] E1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
60
- retval = ::Marshal.load existing
76
+ retval = load_existing existing
61
77
  end
62
78
  unless retval
63
79
  LockAndCache.logger.debug { "[lock_and_cache] F1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
@@ -70,39 +86,50 @@ module LockAndCache
70
86
  sleep heartbeat_frequency
71
87
  break if done
72
88
  LockAndCache.logger.debug { "[lock_and_cache] heartbeat2 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
73
- lock_manager.lock lock_digest, heartbeat_expires*1000, extend: lock_info
89
+ # FIXME use lua to check the value
90
+ raise "unexpectedly lost lock for #{key.debug}" unless lock_storage.get(lock_digest) == lock_secret
91
+ lock_storage.set lock_digest, lock_secret, xx: true, ex: heartbeat_expires
74
92
  end
75
93
  end
76
- retval = blk.call
77
- retval.nil? ? set_nil : set_non_nil(retval)
94
+ begin
95
+ retval = blk.call
96
+ retval.nil? ? set_nil : set_non_nil(retval)
97
+ rescue
98
+ set_error $!
99
+ raise
100
+ end
78
101
  ensure
79
102
  done = true
80
103
  lock_extender.join if lock_extender.status.nil?
81
104
  end
82
105
  end
83
106
  ensure
84
- lock_manager.unlock lock_info if lock_info
107
+ lock_storage.del lock_digest if acquired
85
108
  end
86
109
  retval
87
110
  end
88
111
 
112
+ def set_error(exception)
113
+ cache_storage.set digest, ::Marshal.dump(ERROR_MAGIC_KEY => exception.message), ex: 1
114
+ end
115
+
89
116
  NIL = Marshal.dump nil
90
117
  def set_nil
91
118
  if nil_expires
92
- storage.setex digest, nil_expires, NIL
119
+ cache_storage.set digest, NIL, ex: nil_expires
93
120
  elsif expires
94
- storage.setex digest, expires, NIL
121
+ cache_storage.set digest, NIL, ex: expires
95
122
  else
96
- storage.set digest, NIL
123
+ cache_storage.set digest, NIL
97
124
  end
98
125
  end
99
126
 
100
127
  def set_non_nil(retval)
101
128
  raise "expected not null #{retval.inspect}" if retval.nil?
102
129
  if expires
103
- storage.setex digest, expires, ::Marshal.dump(retval)
130
+ cache_storage.set digest, ::Marshal.dump(retval), ex: expires
104
131
  else
105
- storage.set digest, ::Marshal.dump(retval)
132
+ cache_storage.set digest, ::Marshal.dump(retval)
106
133
  end
107
134
  end
108
135
  end
@@ -1,3 +1,5 @@
1
+ require 'date'
2
+
1
3
  module LockAndCache
2
4
  # @private
3
5
  class Key
@@ -22,8 +24,11 @@ module LockAndCache
22
24
  #
23
25
  # Recursively extract id from obj. Calls #lock_and_cache_key if available, otherwise #id
24
26
  def extract_obj_id(obj)
25
- if ALLOWED_IN_KEYS.any? { |k| obj.is_a?(k) }
27
+ klass = obj.class
28
+ if ALLOWED_IN_KEYS.include?(klass)
26
29
  obj
30
+ elsif DATE.include?(klass)
31
+ obj.to_s
27
32
  elsif obj.respond_to?(:lock_and_cache_key)
28
33
  extract_obj_id obj.lock_and_cache_key
29
34
  elsif obj.respond_to?(:id)
@@ -43,7 +48,19 @@ module LockAndCache
43
48
  ::TrueClass,
44
49
  ::FalseClass,
45
50
  ::NilClass,
46
- ]
51
+ ::Integer,
52
+ ::Float,
53
+ ].to_set
54
+ parts = ::RUBY_VERSION.split('.').map(&:to_i)
55
+ unless parts[0] >= 2 and parts[1] >= 4
56
+ ALLOWED_IN_KEYS << ::Fixnum
57
+ ALLOWED_IN_KEYS << ::Bignum
58
+ end
59
+ DATE = [
60
+ ::Date,
61
+ ::DateTime,
62
+ ::Time,
63
+ ].to_set
47
64
  METHOD_NAME_IN_CALLER = /in `([^']+)'/
48
65
 
49
66
  attr_reader :context
@@ -81,14 +98,17 @@ module LockAndCache
81
98
  end
82
99
 
83
100
  def locked?
84
- LockAndCache.storage.exists lock_digest
101
+ LockAndCache.lock_storage.exists? lock_digest
102
+ end
103
+
104
+ def cached?
105
+ LockAndCache.cache_storage.exists? digest
85
106
  end
86
107
 
87
108
  def clear
88
109
  LockAndCache.logger.debug { "[lock_and_cache] clear #{debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
89
- storage = LockAndCache.storage
90
- storage.del digest
91
- storage.del lock_digest
110
+ LockAndCache.cache_storage.del digest
111
+ LockAndCache.lock_storage.del lock_digest
92
112
  end
93
113
 
94
114
  alias debug key
@@ -1,3 +1,3 @@
1
1
  module LockAndCache
2
- VERSION = '4.0.4'
2
+ VERSION = '6.0.1'
3
3
  end
@@ -20,8 +20,6 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_runtime_dependency 'activesupport'
22
22
  spec.add_runtime_dependency 'redis'
23
- # temporary until https://github.com/leandromoreira/redlock-rb/pull/20 is merged
24
- spec.add_runtime_dependency 'redlock', '>=0.1.3'
25
23
 
26
24
  spec.add_development_dependency 'pry'
27
25
  spec.add_development_dependency 'bundler', '~> 1.6'
@@ -21,13 +21,23 @@ describe LockAndCache::Key do
21
21
  expect(described_class.new(a: 1).send(:parts)).to eq(described_class.new([[:a, 1]]).send(:parts))
22
22
  end
23
23
 
24
+ now = Time.now
25
+ today = Date.today
24
26
  {
25
27
  [1] => [1],
26
- ["you"] => ['you'],
27
- [["you"]] => [['you']],
28
- [["you"], "person"] => [["you"], "person"],
29
- [["you"], {:silly=>:person}] => [["you"], [[:silly, :person]] ],
30
- { hi: 'you' } => [[:hi, "you"]],
28
+ ['you'] => ['you'],
29
+ [['you']] => [['you']],
30
+ [['you'], "person"] => [['you'], "person"],
31
+ [['you'], {:silly=>:person}] => [['you'], [[:silly, :person]] ],
32
+ [now] => [now.to_s],
33
+ [[now]] => [[now.to_s]],
34
+ [today] => [today.to_s],
35
+ [[today]] => [[today.to_s]],
36
+ { hi: 'you' } => [[:hi, 'you']],
37
+ { hi: 123 } => [[:hi, 123]],
38
+ { hi: 123.0 } => [[:hi, 123.0]],
39
+ { hi: now } => [[:hi, now.to_s]],
40
+ { hi: today } => [[:hi, today.to_s]],
31
41
  [KeyTestId.new] => ['id'],
32
42
  [[KeyTestId.new]] => [['id']],
33
43
  { a: KeyTestId.new } => [[:a, "id"]],
@@ -140,7 +140,8 @@ end
140
140
 
141
141
  describe LockAndCache do
142
142
  before do
143
- LockAndCache.flush
143
+ LockAndCache.flush_locks
144
+ LockAndCache.flush_cache
144
145
  end
145
146
 
146
147
  it 'has a version number' do
@@ -337,6 +338,34 @@ describe LockAndCache do
337
338
  expect(count).to eq(1)
338
339
  end
339
340
 
341
+ it 'really caches' do
342
+ expect(LockAndCache.lock_and_cache('hello') { :red }).to eq(:red)
343
+ expect(LockAndCache.lock_and_cache('hello') { raise(Exception.new("stop")) }).to eq(:red)
344
+ end
345
+
346
+ it 'caches errors (briefly)' do
347
+ count = 0
348
+ expect {
349
+ LockAndCache.lock_and_cache('hello') { count += 1; raise("stop") }
350
+ }.to raise_error(/stop/)
351
+ expect(count).to eq(1)
352
+ expect {
353
+ LockAndCache.lock_and_cache('hello') { count += 1; raise("no no not me") }
354
+ }.to raise_error(/LockAndCache.*stop/)
355
+ expect(count).to eq(1)
356
+ sleep 1
357
+ expect {
358
+ LockAndCache.lock_and_cache('hello') { count += 1; raise("retrying") }
359
+ }.to raise_error(/retrying/)
360
+ expect(count).to eq(2)
361
+ end
362
+
363
+ it "can be queried for cached?" do
364
+ expect(LockAndCache.cached?('hello')).to be_falsy
365
+ LockAndCache.lock_and_cache('hello') { nil }
366
+ expect(LockAndCache.cached?('hello')).to be_truthy
367
+ end
368
+
340
369
  it 'allows expiry' do
341
370
  count = 0
342
371
  expect(LockAndCache.lock_and_cache('hello', expires: 1) { count += 1 }).to eq(1)
@@ -4,7 +4,8 @@ require 'lock_and_cache'
4
4
  require 'timeout'
5
5
 
6
6
  require 'redis'
7
- LockAndCache.storage = Redis.new
7
+ LockAndCache.lock_storage = Redis.new db: 3
8
+ LockAndCache.cache_storage = Redis.new db: 4
8
9
 
9
10
  require 'thread/pool'
10
11
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lock_and_cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.4
4
+ version: 6.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seamus Abshere
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-11 00:00:00.000000000 Z
11
+ date: 2020-10-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -38,20 +38,6 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: redlock
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: 0.1.3
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: 0.1.3
55
41
  - !ruby/object:Gem::Dependency
56
42
  name: pry
57
43
  requirement: !ruby/object:Gem::Requirement
@@ -167,6 +153,7 @@ files:
167
153
  - LICENSE.txt
168
154
  - README.md
169
155
  - Rakefile
156
+ - benchmarks/allowed_in_keys.rb
170
157
  - lib/lock_and_cache.rb
171
158
  - lib/lock_and_cache/action.rb
172
159
  - lib/lock_and_cache/key.rb
@@ -194,8 +181,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
181
  - !ruby/object:Gem::Version
195
182
  version: '0'
196
183
  requirements: []
197
- rubyforge_project:
198
- rubygems_version: 2.2.2
184
+ rubygems_version: 3.0.3
199
185
  signing_key:
200
186
  specification_version: 4
201
187
  summary: Lock and cache methods.
@@ -203,4 +189,3 @@ test_files:
203
189
  - spec/lock_and_cache/key_spec.rb
204
190
  - spec/lock_and_cache_spec.rb
205
191
  - spec/spec_helper.rb
206
- has_rdoc: