solid_cache 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -5
- data/Rakefile +2 -0
- data/app/jobs/solid_cache/expiry_job.rb +2 -0
- data/app/models/solid_cache/entry.rb +21 -7
- data/app/models/solid_cache/record.rb +7 -7
- data/db/migrate/20230724121448_create_solid_cache_entries.rb +2 -0
- data/lib/active_support/cache/solid_cache_store.rb +2 -0
- data/lib/generators/solid_cache/install/install_generator.rb +2 -0
- data/lib/solid_cache/cluster/connections.rb +2 -0
- data/lib/solid_cache/cluster/execution.rb +2 -0
- data/lib/solid_cache/cluster/expiry.rb +3 -1
- data/lib/solid_cache/cluster/stats.rb +3 -1
- data/lib/solid_cache/cluster.rb +1 -0
- data/lib/solid_cache/connections/sharded.rb +2 -0
- data/lib/solid_cache/connections/single.rb +2 -0
- data/lib/solid_cache/connections/unmanaged.rb +2 -0
- data/lib/solid_cache/connections.rb +2 -0
- data/lib/solid_cache/engine.rb +2 -0
- data/lib/solid_cache/maglev_hash.rb +2 -0
- data/lib/solid_cache/store/api.rb +3 -1
- data/lib/solid_cache/store/clusters.rb +6 -8
- data/lib/solid_cache/store/entries.rb +2 -0
- data/lib/solid_cache/store/failsafe.rb +2 -0
- data/lib/solid_cache/store.rb +2 -0
- data/lib/solid_cache/version.rb +1 -1
- data/lib/solid_cache.rb +4 -2
- data/lib/tasks/solid_cache_tasks.rake +2 -0
- metadata +32 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d92ba4d7c045821d0c54facd1329613e497a46a2b21daffe3a14931bf9621f86
|
4
|
+
data.tar.gz: 46c4b4b81c48a5a5598cc2460e8c1159e69497f130116e9f855a58f8f3e52a36
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f3505047c30f24d6f8928d439172f826becc79054a05f32b0c670b3cbb227a10b52889b1e64e2430fe2d279e98c6615bb92634c475fdb6016cd7d3c9c170c07
|
7
|
+
data.tar.gz: 4cb0c61cddd64a4d8093b73b06524ae5585cf5d0c70233bd31fd688b837d38c500df28479bbbb7ffa99b24e26dc3230789dad0d108f9d7fc3df4e02b0eeb0d55
|
data/README.md
CHANGED
@@ -80,12 +80,12 @@ Solid Cache supports these options in addition to the standard `ActiveSupport::C
|
|
80
80
|
- `error_handler` - a Proc to call to handle any `ActiveRecord::ActiveRecordError`s that are raises (default: log errors as warnings)
|
81
81
|
- `expiry_batch_size` - the batch size to use when deleting old records (default: `100`)
|
82
82
|
- `expiry_method` - what expiry method to use `thread` or `job` (default: `thread`)
|
83
|
-
- `max_age` - the maximum age of entries in the cache (default: `2.weeks.to_i`)
|
83
|
+
- `max_age` - the maximum age of entries in the cache (default: `2.weeks.to_i`). Can be set to `nil`, but this is not recommended unless using `max_entries` to limit the size of the cache.
|
84
84
|
- `max_entries` - the maximum number of entries allowed in the cache (default: `nil`, meaning no limit)
|
85
85
|
- `cluster` - a Hash of options for the cache database cluster, e.g `{ shards: [:database1, :database2, :database3] }`
|
86
86
|
- `clusters` - and Array of Hashes for multiple cache clusters (ignored if `:cluster` is set)
|
87
87
|
- `active_record_instrumentation` - whether to instrument the cache's queries (default: `true`)
|
88
|
-
- `clear_with` - clear the cache with `:truncate` or `:delete` (default `truncate`, except for when Rails.env.test
|
88
|
+
- `clear_with` - clear the cache with `:truncate` or `:delete` (default `truncate`, except for when `Rails.env.test?` then `delete`)
|
89
89
|
- `max_key_bytesize` - the maximum size of a normalized key in bytes (default `1024`)
|
90
90
|
|
91
91
|
For more information on cache clusters see [Sharding the cache](#sharding-the-cache)
|
@@ -155,7 +155,7 @@ To shard:
|
|
155
155
|
3. Pass the shards for the cache to use via the cluster option
|
156
156
|
|
157
157
|
For example:
|
158
|
-
```
|
158
|
+
```yml
|
159
159
|
# config/database.yml
|
160
160
|
production:
|
161
161
|
cache_shard1:
|
@@ -167,8 +167,9 @@ production:
|
|
167
167
|
cache_shard3:
|
168
168
|
database: cache3_production
|
169
169
|
host: cache3-db
|
170
|
+
```
|
170
171
|
|
171
|
-
|
172
|
+
```ruby
|
172
173
|
# config/environment/production.rb
|
173
174
|
Rails.application.configure do
|
174
175
|
config.solid_cache.connects_to = {
|
@@ -203,7 +204,7 @@ Rails.application.configure do
|
|
203
204
|
}
|
204
205
|
|
205
206
|
primary_cluster = { shards: [ :cache_primary_shard1, :cache_primary_shard2 ] }
|
206
|
-
secondary_cluster = { shards: [ :
|
207
|
+
secondary_cluster = { shards: [ :cache_secondary_shard1, :cache_secondary_shard2 ] }
|
207
208
|
config.cache_store = [ :solid_cache_store, clusters: [ primary_cluster, secondary_cluster ] ]
|
208
209
|
end
|
209
210
|
```
|
data/Rakefile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SolidCache
|
2
4
|
class Entry < Record
|
3
5
|
# This is all quite awkward but it achieves a couple of performance aims
|
@@ -113,7 +115,7 @@ module SolidCache
|
|
113
115
|
if connection.prepared_statements?
|
114
116
|
result = connection.select_all(sanitize_sql(query), "#{name} Load", Array(values), preparable: true)
|
115
117
|
else
|
116
|
-
result = connection.select_all(sanitize_sql([ query, values ]), "#{name} Load",
|
118
|
+
result = connection.select_all(sanitize_sql([ query, values ]), "#{name} Load", Array(values), preparable: false)
|
117
119
|
end
|
118
120
|
|
119
121
|
result.cast_values(SolidCache::Entry.attribute_types)
|
@@ -140,14 +142,26 @@ module SolidCache
|
|
140
142
|
|
141
143
|
def expiry_candidate_ids(count, max_age:, max_entries:)
|
142
144
|
cache_full = max_entries && max_entries < id_range
|
143
|
-
|
145
|
+
return [] unless cache_full || max_age
|
146
|
+
|
147
|
+
# In the case of multiple concurrent expiry operations, it is desirable to
|
148
|
+
# reduce the overlap of entries being addressed by each. For that reason,
|
149
|
+
# retrieve more ids than are being expired, and use random
|
150
|
+
# sampling to reduce that number to the actual intended count.
|
151
|
+
retrieve_count = count * 3
|
144
152
|
|
145
153
|
uncached do
|
146
|
-
order(:id)
|
147
|
-
|
148
|
-
|
149
|
-
.
|
150
|
-
|
154
|
+
candidates = order(:id).limit(retrieve_count)
|
155
|
+
|
156
|
+
candidate_ids = if cache_full
|
157
|
+
candidates.pluck(:id)
|
158
|
+
else
|
159
|
+
min_created_at = max_age.seconds.ago
|
160
|
+
candidates.pluck(:id, :created_at)
|
161
|
+
.filter_map { |id, created_at| id if created_at < min_created_at }
|
162
|
+
end
|
163
|
+
|
164
|
+
candidate_ids.sample(count)
|
151
165
|
end
|
152
166
|
end
|
153
167
|
end
|
@@ -1,21 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SolidCache
|
2
4
|
class Record < ActiveRecord::Base
|
3
5
|
NULL_INSTRUMENTER = ActiveSupport::Notifications::Instrumenter.new(ActiveSupport::Notifications::Fanout.new)
|
4
6
|
|
5
7
|
self.abstract_class = true
|
6
8
|
|
7
|
-
connects_to
|
9
|
+
connects_to(**SolidCache.connects_to) if SolidCache.connects_to
|
8
10
|
|
9
11
|
class << self
|
10
|
-
def disable_instrumentation
|
11
|
-
connection.with_instrumenter(NULL_INSTRUMENTER)
|
12
|
-
yield
|
13
|
-
end
|
12
|
+
def disable_instrumentation(&block)
|
13
|
+
connection.with_instrumenter(NULL_INSTRUMENTER, &block)
|
14
14
|
end
|
15
15
|
|
16
16
|
def with_shard(shard, &block)
|
17
|
-
if shard &&
|
18
|
-
|
17
|
+
if shard && SolidCache.connects_to
|
18
|
+
connected_to(shard: shard, role: default_role, prevent_writes: false, &block)
|
19
19
|
else
|
20
20
|
block.call
|
21
21
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "concurrent/atomic/atomic_fixnum"
|
2
4
|
|
3
5
|
module SolidCache
|
@@ -34,7 +36,7 @@ module SolidCache
|
|
34
36
|
end
|
35
37
|
|
36
38
|
def expiry_counter
|
37
|
-
@expiry_counters ||= connection_names.
|
39
|
+
@expiry_counters ||= connection_names.index_with { |connection_name| Counter.new(expire_every) }
|
38
40
|
@expiry_counters[Entry.current_shard]
|
39
41
|
end
|
40
42
|
|
data/lib/solid_cache/cluster.rb
CHANGED
data/lib/solid_cache/engine.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SolidCache
|
2
4
|
class Store
|
3
5
|
module Api
|
@@ -74,7 +76,7 @@ module SolidCache
|
|
74
76
|
end
|
75
77
|
|
76
78
|
def read_multi_entries(names, **options)
|
77
|
-
keys_and_names = names.
|
79
|
+
keys_and_names = names.index_by { |name| normalize_key(name, options) }
|
78
80
|
serialized_entries = read_serialized_entries(keys_and_names.keys)
|
79
81
|
|
80
82
|
keys_and_names.each_with_object({}) do |(key, name), results|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SolidCache
|
2
4
|
class Store
|
3
5
|
module Clusters
|
@@ -20,11 +22,9 @@ module SolidCache
|
|
20
22
|
end
|
21
23
|
|
22
24
|
private
|
23
|
-
def reading_key(key, failsafe:, failsafe_returning: nil)
|
25
|
+
def reading_key(key, failsafe:, failsafe_returning: nil, &block)
|
24
26
|
failsafe(failsafe, returning: failsafe_returning) do
|
25
|
-
primary_cluster.with_connection_for(key)
|
26
|
-
yield
|
27
|
-
end
|
27
|
+
primary_cluster.with_connection_for(key, &block)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -65,13 +65,11 @@ module SolidCache
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
def writing_all(failsafe:, failsafe_returning: nil)
|
68
|
+
def writing_all(failsafe:, failsafe_returning: nil, &block)
|
69
69
|
first_cluster_sync_rest_async do |cluster, async|
|
70
70
|
cluster.connection_names.each do |connection|
|
71
71
|
failsafe(failsafe, returning: failsafe_returning) do
|
72
|
-
cluster.with_connection(connection, async: async)
|
73
|
-
yield
|
74
|
-
end
|
72
|
+
cluster.with_connection(connection, async: async, &block)
|
75
73
|
end
|
76
74
|
end
|
77
75
|
end
|
data/lib/solid_cache/store.rb
CHANGED
data/lib/solid_cache/version.rb
CHANGED
data/lib/solid_cache.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "zeitwerk"
|
2
4
|
require "solid_cache/engine"
|
3
5
|
|
@@ -17,12 +19,12 @@ module SolidCache
|
|
17
19
|
connects_to && connects_to[:shards]
|
18
20
|
end
|
19
21
|
|
20
|
-
def self.each_shard
|
22
|
+
def self.each_shard(&block)
|
21
23
|
return to_enum(:each_shard) unless block_given?
|
22
24
|
|
23
25
|
if (shards = all_shards_config&.keys)
|
24
26
|
shards.each do |shard|
|
25
|
-
Record.
|
27
|
+
Record.with_shard(shard, &block)
|
26
28
|
end
|
27
29
|
else
|
28
30
|
yield
|
metadata
CHANGED
@@ -1,17 +1,45 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solid_cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Donal McBreen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '7'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activejob
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '7'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: railties
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
16
44
|
requirements:
|
17
45
|
- - ">="
|
@@ -109,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
137
|
- !ruby/object:Gem::Version
|
110
138
|
version: '0'
|
111
139
|
requirements: []
|
112
|
-
rubygems_version: 3.
|
140
|
+
rubygems_version: 3.5.1
|
113
141
|
signing_key:
|
114
142
|
specification_version: 4
|
115
143
|
summary: A database backed ActiveSupport::Cache::Store
|