solid_cache 0.3.0 → 0.4.1
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/MIT-LICENSE +1 -1
- data/README.md +4 -1
- data/app/models/solid_cache/entry/expiration.rb +49 -0
- data/app/models/solid_cache/entry.rb +85 -55
- data/db/migrate/20230724121448_create_solid_cache_entries.rb +0 -2
- data/db/migrate/20240108155507_add_key_hash_and_byte_size_to_solid_cache_entries.rb +8 -0
- data/db/migrate/20240110111600_add_key_hash_and_byte_size_indexes_and_null_constraints_to_solid_cache_entries.rb +11 -0
- data/db/migrate/20240110111702_remove_key_index_from_solid_cache_entries.rb +7 -0
- data/lib/solid_cache/cluster/execution.rb +1 -1
- data/lib/solid_cache/cluster/expiry.rb +14 -28
- data/lib/solid_cache/engine.rb +6 -0
- data/lib/solid_cache/store/api.rb +1 -15
- data/lib/solid_cache/store/entries.rb +6 -6
- data/lib/solid_cache/version.rb +3 -1
- data/lib/solid_cache.rb +1 -0
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d61c50ee1ef52b34cbc3d656a65b3f7274ddd21b77963d419787ecb978e01ede
|
4
|
+
data.tar.gz: 612114bb0eade6e73c2d06f2cf342e1245d41ea547bda43b090a5aa6e0d552a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82a744e8cec593d0cdba4acd0d7d55727d0754507664700c25a2750d2bac011f7db965f751791286e7f918802a12f38fe5e1f251cf5daf5285503d4fae664d15
|
7
|
+
data.tar.gz: 4fe8bf0a189e137a6ec53697d2bfb4fc755c29a2f7d3ce1ca207694a5d9ede2d17871b714416b46fac52d405c6d94dad45951c8aae97b36e8be8d66df5eed720
|
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Solid Cache
|
2
2
|
|
3
|
+
**Upgrading from v0.3.0 or earlier? Please see [upgrading to version 0.4.0](upgrading_to_version_0.4.x.md)**
|
4
|
+
|
3
5
|
Solid Cache is a database-backed Active Support cache store implementation.
|
4
6
|
|
5
7
|
Using SQL databases backed by SSDs we can have caches that are much larger and cheaper than traditional memory only Redis or Memcached backed caches.
|
@@ -80,6 +82,7 @@ Solid Cache supports these options in addition to the standard `ActiveSupport::C
|
|
80
82
|
- `error_handler` - a Proc to call to handle any `ActiveRecord::ActiveRecordError`s that are raises (default: log errors as warnings)
|
81
83
|
- `expiry_batch_size` - the batch size to use when deleting old records (default: `100`)
|
82
84
|
- `expiry_method` - what expiry method to use `thread` or `job` (default: `thread`)
|
85
|
+
- `expiry_queue` - which queue to add expiry jobs to (default: `default`)
|
83
86
|
- `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
87
|
- `max_entries` - the maximum number of entries allowed in the cache (default: `nil`, meaning no limit)
|
85
88
|
- `cluster` - a Hash of options for the cache database cluster, e.g `{ shards: [:database1, :database2, :database3] }`
|
@@ -285,7 +288,7 @@ To run a test for a specific version run:
|
|
285
288
|
bundle exec appraisal rails-7-1 bin/rails test
|
286
289
|
```
|
287
290
|
|
288
|
-
After updating the dependencies in
|
291
|
+
After updating the dependencies in the `Gemfile` please run:
|
289
292
|
|
290
293
|
```shell
|
291
294
|
$ bundle
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidCache
|
4
|
+
class Entry
|
5
|
+
module Expiration
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
class_methods do
|
9
|
+
def id_range
|
10
|
+
uncached do
|
11
|
+
pick(Arel.sql("max(id) - min(id) + 1")) || 0
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def expire(count, max_age:, max_entries:)
|
16
|
+
if (ids = expiry_candidate_ids(count, max_age: max_age, max_entries: max_entries)).any?
|
17
|
+
delete(ids)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def expiry_candidate_ids(count, max_age:, max_entries:)
|
23
|
+
cache_full = max_entries && max_entries < id_range
|
24
|
+
return [] unless cache_full || max_age
|
25
|
+
|
26
|
+
# In the case of multiple concurrent expiry operations, it is desirable to
|
27
|
+
# reduce the overlap of entries being addressed by each. For that reason,
|
28
|
+
# retrieve more ids than are being expired, and use random
|
29
|
+
# sampling to reduce that number to the actual intended count.
|
30
|
+
retrieve_count = count * 3
|
31
|
+
|
32
|
+
uncached do
|
33
|
+
candidates = order(:id).limit(retrieve_count)
|
34
|
+
|
35
|
+
candidate_ids = if cache_full
|
36
|
+
candidates.pluck(:id)
|
37
|
+
else
|
38
|
+
min_created_at = max_age.seconds.ago
|
39
|
+
candidates.pluck(:id, :created_at)
|
40
|
+
.filter_map { |id, created_at| id if created_at < min_created_at }
|
41
|
+
end
|
42
|
+
|
43
|
+
candidate_ids.sample(count)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -2,9 +2,16 @@
|
|
2
2
|
|
3
3
|
module SolidCache
|
4
4
|
class Entry < Record
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
include Expiration
|
6
|
+
|
7
|
+
ID_BYTE_SIZE = 8
|
8
|
+
CREATED_AT_BYTE_SIZE = 8
|
9
|
+
KEY_HASH_BYTE_SIZE = 8
|
10
|
+
VALUE_BYTE_SIZE = 4
|
11
|
+
FIXED_SIZE_COLUMNS_BYTE_SIZE = ID_BYTE_SIZE + CREATED_AT_BYTE_SIZE + KEY_HASH_BYTE_SIZE + VALUE_BYTE_SIZE
|
12
|
+
|
13
|
+
self.ignored_columns += [ :key_hash, :byte_size] if SolidCache.key_hash_stage == :ignored
|
14
|
+
|
8
15
|
class << self
|
9
16
|
def write(key, value)
|
10
17
|
upsert_all_no_query_cache([ { key: key, value: value } ])
|
@@ -15,23 +22,23 @@ module SolidCache
|
|
15
22
|
end
|
16
23
|
|
17
24
|
def read(key)
|
18
|
-
select_all_no_query_cache(get_sql,
|
25
|
+
result = select_all_no_query_cache(get_sql, lookup_value(key)).first
|
26
|
+
result[1] if result&.first == key
|
19
27
|
end
|
20
28
|
|
21
29
|
def read_multi(keys)
|
22
|
-
|
23
|
-
select_all_no_query_cache(get_all_sql(
|
30
|
+
key_hashes = keys.map { |key| lookup_value(key) }
|
31
|
+
results = select_all_no_query_cache(get_all_sql(key_hashes), key_hashes).to_h
|
32
|
+
results.except!(results.keys - keys)
|
24
33
|
end
|
25
34
|
|
26
35
|
def delete_by_key(key)
|
27
|
-
delete_no_query_cache(
|
36
|
+
delete_no_query_cache(lookup_column, lookup_value(key))
|
28
37
|
end
|
29
38
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
delete_no_query_cache(:id, entries.map(&:id))
|
34
|
-
end
|
39
|
+
def delete_multi(keys)
|
40
|
+
serialized_keys = keys.map { |key| lookup_value(key) }
|
41
|
+
delete_no_query_cache(lookup_column, serialized_keys)
|
35
42
|
end
|
36
43
|
|
37
44
|
def clear_truncate
|
@@ -45,7 +52,8 @@ module SolidCache
|
|
45
52
|
def increment(key, amount)
|
46
53
|
transaction do
|
47
54
|
uncached do
|
48
|
-
|
55
|
+
result = lock.where(lookup_column => lookup_value(key)).pick(:key, :value)
|
56
|
+
amount += result[1].to_i if result&.first == key
|
49
57
|
write(key, amount)
|
50
58
|
amount
|
51
59
|
end
|
@@ -56,48 +64,86 @@ module SolidCache
|
|
56
64
|
increment(key, -amount)
|
57
65
|
end
|
58
66
|
|
59
|
-
def id_range
|
60
|
-
uncached do
|
61
|
-
pick(Arel.sql("max(id) - min(id) + 1")) || 0
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def expire(count, max_age:, max_entries:)
|
66
|
-
if (ids = expiry_candidate_ids(count, max_age: max_age, max_entries: max_entries)).any?
|
67
|
-
delete(ids)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
67
|
private
|
72
|
-
def upsert_all_no_query_cache(
|
73
|
-
insert_all = ActiveRecord::InsertAll.new(
|
68
|
+
def upsert_all_no_query_cache(payloads)
|
69
|
+
insert_all = ActiveRecord::InsertAll.new(
|
70
|
+
self,
|
71
|
+
add_key_hash_and_byte_size(payloads),
|
72
|
+
unique_by: upsert_unique_by,
|
73
|
+
on_duplicate: :update,
|
74
|
+
update_only: upsert_update_only
|
75
|
+
)
|
74
76
|
sql = connection.build_insert_sql(ActiveRecord::InsertAll::Builder.new(insert_all))
|
75
77
|
|
76
78
|
message = +"#{self} "
|
77
|
-
message << "Bulk " if
|
79
|
+
message << "Bulk " if payloads.many?
|
78
80
|
message << "Upsert"
|
79
81
|
# exec_query_method does not clear the query cache, exec_insert_all does
|
80
82
|
connection.send exec_query_method, sql, message
|
81
83
|
end
|
82
84
|
|
85
|
+
def add_key_hash_and_byte_size(payloads)
|
86
|
+
payloads.map do |payload|
|
87
|
+
payload.dup.tap do |payload|
|
88
|
+
if key_hash?
|
89
|
+
payload[:key_hash] = key_hash_for(payload[:key])
|
90
|
+
payload[:byte_size] = byte_size_for(payload)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def key_hash?
|
97
|
+
@key_hash ||= [ :indexed, :unindexed ].include?(SolidCache.key_hash_stage) &&
|
98
|
+
connection.column_exists?(table_name, :key_hash)
|
99
|
+
end
|
100
|
+
|
101
|
+
def key_hash_indexed?
|
102
|
+
key_hash? && SolidCache.key_hash_stage == :indexed
|
103
|
+
end
|
104
|
+
|
105
|
+
def lookup_column
|
106
|
+
key_hash_indexed? ? :key_hash : :key
|
107
|
+
end
|
108
|
+
|
109
|
+
def lookup_value(key)
|
110
|
+
key_hash_indexed? ? key_hash_for(key) : to_binary(key)
|
111
|
+
end
|
112
|
+
|
113
|
+
def lookup_placeholder
|
114
|
+
key_hash_indexed? ? 1 : "placeholder"
|
115
|
+
end
|
116
|
+
|
83
117
|
def exec_query_method
|
84
118
|
connection.respond_to?(:internal_exec_query) ? :internal_exec_query : :exec_query
|
85
119
|
end
|
86
120
|
|
87
121
|
def upsert_unique_by
|
88
|
-
connection.supports_insert_conflict_target? ?
|
122
|
+
connection.supports_insert_conflict_target? ? lookup_column : nil
|
123
|
+
end
|
124
|
+
|
125
|
+
def upsert_update_only
|
126
|
+
if key_hash_indexed?
|
127
|
+
[ :key, :value, :byte_size ]
|
128
|
+
elsif key_hash?
|
129
|
+
[ :value, :key_hash, :byte_size ]
|
130
|
+
else
|
131
|
+
[ :value ]
|
132
|
+
end
|
89
133
|
end
|
90
134
|
|
91
135
|
def get_sql
|
92
|
-
@get_sql ||=
|
136
|
+
@get_sql ||= {}
|
137
|
+
@get_sql[lookup_column] ||= build_sql(where(lookup_column => lookup_placeholder).select(:key, :value))
|
93
138
|
end
|
94
139
|
|
95
|
-
def get_all_sql(
|
140
|
+
def get_all_sql(key_hashes)
|
96
141
|
if connection.prepared_statements?
|
97
142
|
@get_all_sql_binds ||= {}
|
98
|
-
@get_all_sql_binds[
|
143
|
+
@get_all_sql_binds[[key_hashes.count, lookup_column]] ||= build_sql(where(lookup_column => key_hashes).select(:key, :value))
|
99
144
|
else
|
100
|
-
@get_all_sql_no_binds ||=
|
145
|
+
@get_all_sql_no_binds ||= {}
|
146
|
+
@get_all_sql_no_binds[lookup_column] ||= build_sql(where(lookup_column => [ lookup_placeholder, lookup_placeholder ]).select(:key, :value)).gsub("?, ?", "?")
|
101
147
|
end
|
102
148
|
end
|
103
149
|
|
@@ -140,29 +186,13 @@ module SolidCache
|
|
140
186
|
ActiveModel::Type::Binary.new.serialize(key)
|
141
187
|
end
|
142
188
|
|
143
|
-
def
|
144
|
-
|
145
|
-
|
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
|
152
|
-
|
153
|
-
uncached do
|
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
|
189
|
+
def key_hash_for(key)
|
190
|
+
# Need to unpack this as a signed integer - Postgresql and SQLite don't support unsigned integers
|
191
|
+
Digest::SHA256.digest(key.to_s).unpack("q>").first
|
192
|
+
end
|
163
193
|
|
164
|
-
|
165
|
-
|
194
|
+
def byte_size_for(payload)
|
195
|
+
payload[:key].to_s.bytesize + payload[:value].to_s.bytesize + FIXED_SIZE_COLUMNS_BYTE_SIZE
|
166
196
|
end
|
167
197
|
end
|
168
198
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class AddKeyHashAndByteSizeIndexesAndNullConstraintsToSolidCacheEntries < ActiveRecord::Migration[7.1]
|
2
|
+
def change
|
3
|
+
change_table :solid_cache_entries, bulk: true do |t|
|
4
|
+
t.change_null :key_hash, false
|
5
|
+
t.change_null :byte_size, false
|
6
|
+
t.index :key_hash, unique: true
|
7
|
+
t.index [:key_hash, :byte_size]
|
8
|
+
t.index :byte_size
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -5,7 +5,7 @@ module SolidCache
|
|
5
5
|
module Execution
|
6
6
|
def initialize(options = {})
|
7
7
|
super(options)
|
8
|
-
@background = Concurrent::
|
8
|
+
@background = Concurrent::FixedThreadPool.new(1, max_queue: 100, fallback_policy: :discard)
|
9
9
|
@active_record_instrumentation = options.fetch(:active_record_instrumentation, true)
|
10
10
|
end
|
11
11
|
|
@@ -9,13 +9,14 @@ module SolidCache
|
|
9
9
|
# This ensures there is downward pressure on the cache size while there is valid data to delete
|
10
10
|
EXPIRY_MULTIPLIER = 1.25
|
11
11
|
|
12
|
-
attr_reader :expiry_batch_size, :expiry_method, :
|
12
|
+
attr_reader :expiry_batch_size, :expiry_method, :expiry_queue, :expires_per_write, :max_age, :max_entries
|
13
13
|
|
14
14
|
def initialize(options = {})
|
15
15
|
super(options)
|
16
16
|
@expiry_batch_size = options.fetch(:expiry_batch_size, 100)
|
17
17
|
@expiry_method = options.fetch(:expiry_method, :thread)
|
18
|
-
@
|
18
|
+
@expiry_queue = options.fetch(:expiry_queue, :default)
|
19
|
+
@expires_per_write = (1 / expiry_batch_size.to_f) * EXPIRY_MULTIPLIER
|
19
20
|
@max_age = options.fetch(:max_age, 2.weeks.to_i)
|
20
21
|
@max_entries = options.fetch(:max_entries, nil)
|
21
22
|
|
@@ -23,41 +24,26 @@ module SolidCache
|
|
23
24
|
end
|
24
25
|
|
25
26
|
def track_writes(count)
|
26
|
-
|
27
|
+
expiry_batches(count).times { expire_later }
|
27
28
|
end
|
28
29
|
|
29
30
|
private
|
31
|
+
def expiry_batches(count)
|
32
|
+
batches = (count * expires_per_write).floor
|
33
|
+
overflow_batch_chance = count * expires_per_write - batches
|
34
|
+
batches += 1 if rand < overflow_batch_chance
|
35
|
+
batches
|
36
|
+
end
|
37
|
+
|
30
38
|
def expire_later
|
31
39
|
if expiry_method == :job
|
32
|
-
ExpiryJob
|
40
|
+
ExpiryJob
|
41
|
+
.set(queue: expiry_queue)
|
42
|
+
.perform_later(expiry_batch_size, shard: Entry.current_shard, max_age: max_age, max_entries: max_entries)
|
33
43
|
else
|
34
44
|
async { Entry.expire(expiry_batch_size, max_age: max_age, max_entries: max_entries) }
|
35
45
|
end
|
36
46
|
end
|
37
|
-
|
38
|
-
def expiry_counter
|
39
|
-
@expiry_counters ||= connection_names.index_with { |connection_name| Counter.new(expire_every) }
|
40
|
-
@expiry_counters[Entry.current_shard]
|
41
|
-
end
|
42
|
-
|
43
|
-
class Counter
|
44
|
-
attr_reader :expire_every, :counter
|
45
|
-
|
46
|
-
def initialize(expire_every)
|
47
|
-
@expire_every = expire_every
|
48
|
-
@counter = Concurrent::AtomicFixnum.new(rand(expire_every).to_i)
|
49
|
-
end
|
50
|
-
|
51
|
-
def count(count)
|
52
|
-
value = counter.increment(count)
|
53
|
-
new_multiple_of_expire_every?(value - count, value)
|
54
|
-
end
|
55
|
-
|
56
|
-
private
|
57
|
-
def new_multiple_of_expire_every?(first_value, second_value)
|
58
|
-
first_value / expire_every != second_value / expire_every
|
59
|
-
end
|
60
|
-
end
|
61
47
|
end
|
62
48
|
end
|
63
49
|
end
|
data/lib/solid_cache/engine.rb
CHANGED
@@ -13,6 +13,12 @@ module SolidCache
|
|
13
13
|
|
14
14
|
SolidCache.executor = config.solid_cache.executor
|
15
15
|
SolidCache.connects_to = config.solid_cache.connects_to
|
16
|
+
if config.solid_cache.key_hash_stage
|
17
|
+
unless [:ignored, :unindexed, :indexed].include?(config.solid_cache.key_hash_stage)
|
18
|
+
raise "ArgumentError, :key_hash_stage must be :ignored, :unindexed or :indexed"
|
19
|
+
end
|
20
|
+
SolidCache.key_hash_stage = config.solid_cache.key_hash_stage
|
21
|
+
end
|
16
22
|
end
|
17
23
|
|
18
24
|
config.after_initialize do
|
@@ -14,20 +14,6 @@ module SolidCache
|
|
14
14
|
@max_key_bytesize = options.fetch(:max_key_bytesize, DEFAULT_MAX_KEY_BYTESIZE)
|
15
15
|
end
|
16
16
|
|
17
|
-
def delete_matched(matcher, options = {})
|
18
|
-
instrument :delete_matched, matcher do
|
19
|
-
raise ArgumentError, "Only strings are supported: #{matcher.inspect}" unless String === matcher
|
20
|
-
raise ArgumentError, "Strings cannot start with wildcards" if SQL_WILDCARD_CHARS.include?(matcher[0])
|
21
|
-
|
22
|
-
options ||= {}
|
23
|
-
batch_size = options.fetch(:batch_size, 1000)
|
24
|
-
|
25
|
-
matcher = namespace_key(matcher, options)
|
26
|
-
|
27
|
-
entry_delete_matched(matcher, batch_size)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
17
|
def increment(name, amount = 1, options = nil)
|
32
18
|
options = merged_options(options)
|
33
19
|
key = normalize_key(name, options)
|
@@ -119,7 +105,7 @@ module SolidCache
|
|
119
105
|
end
|
120
106
|
|
121
107
|
def delete_multi_entries(entries, **options)
|
122
|
-
entries.
|
108
|
+
entry_delete_multi(entries).compact.sum
|
123
109
|
end
|
124
110
|
|
125
111
|
def serialize_entry(entry, raw: false, **options)
|
@@ -17,12 +17,6 @@ module SolidCache
|
|
17
17
|
end
|
18
18
|
|
19
19
|
private
|
20
|
-
def entry_delete_matched(matcher, batch_size)
|
21
|
-
writing_all(failsafe: :delete_matched) do
|
22
|
-
Entry.delete_matched(matcher, batch_size: batch_size)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
20
|
def entry_clear
|
27
21
|
writing_all(failsafe: :clear) do
|
28
22
|
if clear_with == :truncate
|
@@ -78,6 +72,12 @@ module SolidCache
|
|
78
72
|
Entry.delete_by_key(key)
|
79
73
|
end
|
80
74
|
end
|
75
|
+
|
76
|
+
def entry_delete_multi(entries)
|
77
|
+
writing_keys(entries, failsafe: :delete_multi_entries, failsafe_returning: false) do
|
78
|
+
Entry.delete_multi(entries)
|
79
|
+
end
|
80
|
+
end
|
81
81
|
end
|
82
82
|
end
|
83
83
|
end
|
data/lib/solid_cache/version.rb
CHANGED
data/lib/solid_cache.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solid_cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Donal McBreen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-01-
|
11
|
+
date: 2024-01-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -92,8 +92,12 @@ files:
|
|
92
92
|
- Rakefile
|
93
93
|
- app/jobs/solid_cache/expiry_job.rb
|
94
94
|
- app/models/solid_cache/entry.rb
|
95
|
+
- app/models/solid_cache/entry/expiration.rb
|
95
96
|
- app/models/solid_cache/record.rb
|
96
97
|
- db/migrate/20230724121448_create_solid_cache_entries.rb
|
98
|
+
- db/migrate/20240108155507_add_key_hash_and_byte_size_to_solid_cache_entries.rb
|
99
|
+
- db/migrate/20240110111600_add_key_hash_and_byte_size_indexes_and_null_constraints_to_solid_cache_entries.rb
|
100
|
+
- db/migrate/20240110111702_remove_key_index_from_solid_cache_entries.rb
|
97
101
|
- lib/active_support/cache/solid_cache_store.rb
|
98
102
|
- lib/generators/solid_cache/install/USAGE
|
99
103
|
- lib/generators/solid_cache/install/install_generator.rb
|
@@ -122,7 +126,9 @@ licenses:
|
|
122
126
|
metadata:
|
123
127
|
homepage_uri: http://github.com/rails/solid_cache
|
124
128
|
source_code_uri: http://github.com/rails/solid_cache
|
125
|
-
post_install_message:
|
129
|
+
post_install_message: |
|
130
|
+
Solid Cache v0.4 contains new database migrations.
|
131
|
+
See https://github.com/rails/solid_cache/blob/main/upgrading_to_version_0.4.x.md for upgrade instructions.
|
126
132
|
rdoc_options: []
|
127
133
|
require_paths:
|
128
134
|
- lib
|
@@ -137,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
137
143
|
- !ruby/object:Gem::Version
|
138
144
|
version: '0'
|
139
145
|
requirements: []
|
140
|
-
rubygems_version: 3.5.
|
146
|
+
rubygems_version: 3.5.4
|
141
147
|
signing_key:
|
142
148
|
specification_version: 4
|
143
149
|
summary: A database backed ActiveSupport::Cache::Store
|