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 +4 -4
- data/docs/MODEL_SETUP.md +62 -0
- data/docs/USAGE.md +6 -0
- data/lib/atomic_cache/atomic_cache_client.rb +2 -2
- data/lib/atomic_cache/concerns/global_lmt_cache_concern.rb +16 -2
- data/lib/atomic_cache/key/last_mod_time_key_manager.rb +1 -2
- data/lib/atomic_cache/version.rb +1 -1
- data/spec/atomic_cache/concerns/global_lmt_cache_concern_spec.rb +55 -0
- metadata +20 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e3ac94ddb9677147c7ac09a9a99773ba2f06c93c1c9133463f0e43645553bd5
|
4
|
+
data.tar.gz: 00734774a95fb01689e02b91de9c7f8aad7d0a9188512c9ff955c1bd1d9a0d54
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 =
|
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#{
|
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
|
-
|
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
|
|
data/lib/atomic_cache/version.rb
CHANGED
@@ -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.
|
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-
|
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: '
|
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: '
|
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
|