atomic_cache 0.5.1.rc1 → 0.5.5.rc1

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
2
  SHA256:
3
- metadata.gz: f03b8f8d9294f3a40ea719ea5165d4c6789c3eebb36459768c8fbd0c57d8fc3a
4
- data.tar.gz: b2a320b8323c57785e0202e29d89922b33e63fc9ebd898d6df61f76fb5e2600c
3
+ metadata.gz: 0e3ac94ddb9677147c7ac09a9a99773ba2f06c93c1c9133463f0e43645553bd5
4
+ data.tar.gz: 00734774a95fb01689e02b91de9c7f8aad7d0a9188512c9ff955c1bd1d9a0d54
5
5
  SHA512:
6
- metadata.gz: f461c2bf7d903c6f7940334b8f5d11672c7ed04d1d55ad5d25c2337c07906d24a39f8958ddf04a2dd5d5dc690c9a0920121fb99c9f25c809c08959e48ee13ae3
7
- data.tar.gz: 9100ad9f5e5012c3729e3bc3a3b95ca64705c562f14e5d610df9f223436ee9351ec58cddc64b61c87c186a588f18e6158e891308aa5d431739f56aa2562744fa
6
+ metadata.gz: 8ba37b1082a3f252d8d313303db631c349bc28ad713a4b4dc57c5bb259904576202e74e844fbdebb29fa557f3a47c0f3451a7f0ea015c817adba3a863070a222
7
+ data.tar.gz: ea17c5f647ab914079a48680bc79afc8f6958e087f532dd950fc31eee19d470689d28eeb053faf2fc72e717ca87119795a960f5e6d9500cefe34423ad583e992
data/docs/MODEL_SETUP.md CHANGED
@@ -29,3 +29,65 @@ class Foo < ActiveRecord::Base
29
29
  cache_version(5)
30
30
  end
31
31
  ```
32
+
33
+ ### Inheritance
34
+
35
+ When either `force_cache_class` or `cache_version` are used, those values will always be preferred by classes which inherit from the class on which those macros exist. When macros are used on descendant classes, the "closet" value wins.
36
+
37
+ #### Example #1
38
+ The keys used by `Bar` will be prefixed with `custom:v5`, as it prefers both the 'custom' class name and version 5 from the parent.
39
+ ```ruby
40
+ class Foo < ActiveRecord::Base
41
+ include AtomicCache::GlobalLMTCacheConcern
42
+ force_cache_class('custom')
43
+ cache_version(5)
44
+ end
45
+
46
+ class Bar < Foo
47
+ end
48
+ ```
49
+
50
+ #### Example #2
51
+ The keys used by `Bar` will be prefixed with `bar:v5`, as the version 5 is taken from the parent, but the use of forcing on the child class result in the cache class of 'bar'.
52
+ ```ruby
53
+ class Foo < ActiveRecord::Base
54
+ include AtomicCache::GlobalLMTCacheConcern
55
+ cache_version(5)
56
+ end
57
+
58
+ class Bar < Foo
59
+ force_cache_class('bar')
60
+ end
61
+ ```
62
+
63
+ #### Example #3
64
+ The keys used by `Bar` will be still be prefixed with `bar:v5` for the same reasons as above.
65
+ ```ruby
66
+ class Foo < ActiveRecord::Base
67
+ include AtomicCache::GlobalLMTCacheConcern
68
+ force_cache_class('custom')
69
+ cache_version(5)
70
+ end
71
+
72
+ class Bar < Foo
73
+ force_cache_class('bar')
74
+ end
75
+ ```
76
+
77
+ ### Rails Model Inheritance
78
+
79
+ It's not uncommon in rails to end up with model inhertiance. For example:
80
+
81
+ ```ruby
82
+ class Content < ActiveRecord::Base
83
+ include AtomicCache::GlobalLMTCacheConcern
84
+ force_cache_class('content')
85
+ end
86
+
87
+ class BlogPost < Content
88
+ end
89
+ ```
90
+
91
+ If these models will be cached together into a single key, it's preferable to force the cache class on the parent, causing all the descendant types to use the same keyspace. Not doing this will cause each subtype to use it's own last modified time.
92
+
93
+ If the models will be treated as separate collections and cached separately, this is not recommended. Alternately, if only some subtypes will be cached together, those should share a forced cache class and version.
data/docs/USAGE.md CHANGED
@@ -38,6 +38,12 @@ The ideal `generate_ttl_ms` time is just slightly longer than the average genera
38
38
 
39
39
  If metrics are enabled, the `<namespace>.generate.run` can be used to determine the min/max/average generate time for a particular cache and the `generate_ttl_ms` tuned using that.
40
40
 
41
+ ##### ⚠️ TTL Rounding
42
+ When using atomic_cache with memcached, be aware that the TTL will be rounded down to the nearest whole seconds. For example, a `generate_ttl_ms` value of 3500 will result in a 3s TTL with memcache.
43
+
44
+ ##### ⚠️ Max Rate of Change
45
+ Atomic_cache will *not* remove the lock after the generation process is done. This is both more efficient, and allows the `generate_ttl_ms` to be used to limit the total rate of change. For example, if the typical database query behind the cache takes 2s to run, but `generate_ttl_ms` is set to 10s, then the lock will live on for 8s after the generate process finishes, preventing other processes from querying for a new value. In many ways, `generate_ttl_ms` is the amount of time that the system will be un-allowed to make additional queries.
46
+
41
47
  #### `max_retries` & `backoff_duration_ms`
42
48
  _`max_retries` defaults to 5._
43
49
  _`backoff_duration_ms` defaults to 50ms._
@@ -93,8 +93,8 @@ module AtomicCache
93
93
  end
94
94
 
95
95
  new_key = @timestamp_manager.next_key(keyspace, lmt)
96
- @timestamp_manager.promote(keyspace, last_known_key: new_key, timestamp: lmt)
97
96
  @storage.set(new_key, new_value, options)
97
+ @timestamp_manager.promote(keyspace, last_known_key: new_key, timestamp: lmt)
98
98
 
99
99
  metrics(:increment, 'generate.current-thread', tags: tags)
100
100
  log(:debug, "Generating new value for `#{new_key}`")
@@ -148,7 +148,7 @@ module AtomicCache
148
148
  end
149
149
 
150
150
  metrics(:increment, 'wait.give-up')
151
- log(:warn, "Giving up waiting. Exceeded max retries (#{max_retries}).")
151
+ log(:warn, "Giving up waiting. Exceeded max retries (#{max_retries}) for root #{keyspace.root}.")
152
152
  nil
153
153
  end
154
154
 
@@ -63,15 +63,29 @@ module AtomicCache
63
63
  @timestamp_manager.last_modified_time
64
64
  end
65
65
 
66
+ # protected
67
+
68
+ def cache_version_get
69
+ return @atomic_cache_version if @atomic_cache_version.present?
70
+ return self.superclass.cache_version_get if self.superclass.respond_to?(:cache_version_get)
71
+ nil
72
+ end
73
+
74
+ def cache_class_get
75
+ return @atomic_cache_class if @atomic_cache_class.present?
76
+ return self.superclass.cache_class_get if self.superclass.respond_to?(:cache_class_get)
77
+ nil
78
+ end
79
+
66
80
  private
67
81
 
68
82
  def init_atomic_cache
69
83
  return if @atomic_cache.present?
70
84
  ATOMIC_CACHE_CONCERN_MUTEX.synchronize do
71
- cache_class = @atomic_cache_class || default_cache_class
85
+ cache_class = cache_class_get || default_cache_class
72
86
  prefix = [cache_class]
73
87
  prefix.unshift(DefaultConfig.instance.namespace) if DefaultConfig.instance.namespace.present?
74
- prefix.push("v#{@atomic_cache_version}") if @atomic_cache_version.present?
88
+ prefix.push("v#{cache_version_get}") if cache_version_get.present?
75
89
  @default_cache_keyspace = Keyspace.new(namespace: prefix, root: cache_class)
76
90
 
77
91
  @timestamp_manager = LastModTimeKeyManager.new(
@@ -44,8 +44,7 @@ module AtomicCache
44
44
  # @param last_known_key [String] a key with a known value to refer other processes to
45
45
  # @param timestamp [String, Numeric, Time] the timestamp with which the last_known_key was updated at
46
46
  def promote(keyspace, last_known_key:, timestamp:)
47
- key = keyspace.last_known_key_key
48
- @storage.set(key, last_known_key)
47
+ @storage.set(keyspace.last_known_key_key, last_known_key)
49
48
  @storage.set(last_modified_time_key, self.format(timestamp))
50
49
  end
51
50
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AtomicCache
4
- VERSION = "0.5.1.rc1"
4
+ VERSION = "0.5.5.rc1"
5
5
  end
@@ -113,6 +113,61 @@ describe 'AtomicCacheConcern' do
113
113
  subject.expire_cache
114
114
  expect(key_storage.store).to have_key(:'foo:v3:lmt')
115
115
  end
116
+
117
+ context 'with version < class inheritance' do
118
+ subject do
119
+ class Foo3
120
+ include AtomicCache::GlobalLMTCacheConcern
121
+ cache_version(6)
122
+ end
123
+ class Foo4 < Foo3
124
+ force_cache_class('foo')
125
+ end
126
+ Foo4
127
+ end
128
+
129
+ it 'uses the version from the parent and the forced class name from the macro' do
130
+ subject.expire_cache
131
+ expect(key_storage.store).to have_key(:'foo:v6:lmt')
132
+ end
133
+ end
134
+
135
+ context 'with class < version inheritance' do
136
+ subject do
137
+ class Foo5
138
+ include AtomicCache::GlobalLMTCacheConcern
139
+ force_cache_class('foo')
140
+ end
141
+ class Foo6 < Foo5
142
+ cache_version(6)
143
+ end
144
+ Foo6
145
+ end
146
+
147
+ it 'uses the cache class from the parent and version from the macro' do
148
+ subject.expire_cache
149
+ expect(key_storage.store).to have_key(:'foo:v6:lmt')
150
+ end
151
+ end
152
+
153
+ context 'dual force_cache_class' do
154
+ subject do
155
+ class Foo6
156
+ include AtomicCache::GlobalLMTCacheConcern
157
+ force_cache_class('foo')
158
+ end
159
+ class Foo7 < Foo6
160
+ force_cache_class('bar')
161
+ cache_version(6)
162
+ end
163
+ Foo7
164
+ end
165
+
166
+ it 'uses the prefers the forced class name from the macro' do
167
+ subject.expire_cache
168
+ expect(key_storage.store).to have_key(:'bar:v6:lmt')
169
+ end
170
+ end
116
171
  end
117
172
 
118
173
  context 'storage macros' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atomic_cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1.rc1
4
+ version: 0.5.5.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ibotta Developers
@@ -9,22 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-07-12 00:00:00.000000000 Z
12
+ date: 2021-11-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - "~>"
18
+ - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '1.14'
20
+ version: '0'
21
21
  type: :development
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - "~>"
25
+ - - ">="
26
26
  - !ruby/object:Gem::Version
27
- version: '1.14'
27
+ version: '0'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: gems
30
30
  requirement: !ruby/object:Gem::Requirement
@@ -137,6 +137,20 @@ dependencies:
137
137
  - - "~>"
138
138
  - !ruby/object:Gem::Version
139
139
  version: 0.8.1
140
+ - !ruby/object:Gem::Dependency
141
+ name: pry
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
140
154
  - !ruby/object:Gem::Dependency
141
155
  name: activesupport
142
156
  requirement: !ruby/object:Gem::Requirement
@@ -144,9 +158,6 @@ dependencies:
144
158
  - - ">="
145
159
  - !ruby/object:Gem::Version
146
160
  version: '4.2'
147
- - - "<"
148
- - !ruby/object:Gem::Version
149
- version: '6'
150
161
  type: :runtime
151
162
  prerelease: false
152
163
  version_requirements: !ruby/object:Gem::Requirement
@@ -154,9 +165,6 @@ dependencies:
154
165
  - - ">="
155
166
  - !ruby/object:Gem::Version
156
167
  version: '4.2'
157
- - - "<"
158
- - !ruby/object:Gem::Version
159
- version: '6'
160
168
  - !ruby/object:Gem::Dependency
161
169
  name: murmurhash3
162
170
  requirement: !ruby/object:Gem::Requirement