zache 0.15.1 → 0.15.2
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/Gemfile.lock +1 -1
- data/Rakefile +4 -1
- data/lib/zache.rb +100 -35
- data/zache.gemspec +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e9cc0b6c8fece285753b4a7bc12bc296f9cab08b7adacee09dd888f556263e4c
|
|
4
|
+
data.tar.gz: 722f973b74565b70225b44f1de8f6c63ddccf18bbb534ba31a9b1c4c24bbb60d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9ff5b92b44b011b8cb689538e7d4eade3d7d216a89a94b0b85da769bece6610ba9aa27e143f766bbac0b736de65bd7d7faba803b1af4c93876e02913843b819a
|
|
7
|
+
data.tar.gz: dfb50539962ba04cf80db2ceff76dc01abfa3596fd3dc46a21e7d23cc03219e52098a2c77525d0e1ed35b4ea6b31fed6a5f548c228f1105639d66852c4594ac8
|
data/Gemfile.lock
CHANGED
data/Rakefile
CHANGED
|
@@ -10,7 +10,7 @@ require 'rake'
|
|
|
10
10
|
require 'rake/clean'
|
|
11
11
|
require 'shellwords'
|
|
12
12
|
|
|
13
|
-
CLEAN
|
|
13
|
+
CLEAN.include('coverage')
|
|
14
14
|
|
|
15
15
|
def name
|
|
16
16
|
@name ||= File.basename(Dir['*.gemspec'].first, '.*')
|
|
@@ -28,6 +28,9 @@ Rake::TestTask.new(:test) do |test|
|
|
|
28
28
|
test.libs << 'lib' << 'test'
|
|
29
29
|
test.pattern = 'test/**/test_*.rb'
|
|
30
30
|
test.verbose = false
|
|
31
|
+
test.options = '--verbose' if ENV['VERBOSE']
|
|
32
|
+
# Disable minitest plugins on Windows to avoid gem conflicts
|
|
33
|
+
ENV['MT_NO_PLUGINS'] = '1' if OS.windows?
|
|
31
34
|
end
|
|
32
35
|
|
|
33
36
|
desc 'Run them via Ruby, one by one'
|
data/lib/zache.rb
CHANGED
|
@@ -115,33 +115,11 @@ class Zache
|
|
|
115
115
|
# @return [Object] The cached value
|
|
116
116
|
def get(key, lifetime: 2**32, dirty: false, placeholder: nil, eager: false, &block)
|
|
117
117
|
if block_given?
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
if eager
|
|
124
|
-
has_key = synchronize_all { @hash.key?(key) }
|
|
125
|
-
return synchronize_all { @hash[key][:value] } if has_key
|
|
126
|
-
put(key, placeholder, lifetime: 0)
|
|
127
|
-
Thread.new do
|
|
128
|
-
synchronize_one(key) { calc(key, lifetime, &block) }
|
|
129
|
-
end
|
|
130
|
-
placeholder
|
|
131
|
-
else
|
|
132
|
-
synchronize_one(key) { calc(key, lifetime, &block) }
|
|
133
|
-
end
|
|
118
|
+
return get_dirty_value(key) if should_return_dirty?(key, dirty)
|
|
119
|
+
return get_eager(key, lifetime, placeholder, &block) if eager
|
|
120
|
+
synchronize_one(key) { calc(key, lifetime, &block) }
|
|
134
121
|
else
|
|
135
|
-
|
|
136
|
-
rec = @hash[key]
|
|
137
|
-
if expired_unsafe?(key)
|
|
138
|
-
return rec[:value] if dirty || @dirty
|
|
139
|
-
@hash.delete(key)
|
|
140
|
-
rec = nil
|
|
141
|
-
end
|
|
142
|
-
raise 'The key is absent in the cache' if rec.nil?
|
|
143
|
-
rec[:value]
|
|
144
|
-
end
|
|
122
|
+
get_without_block(key, dirty)
|
|
145
123
|
end
|
|
146
124
|
end
|
|
147
125
|
|
|
@@ -215,14 +193,19 @@ class Zache
|
|
|
215
193
|
# @yield Block to call if the key is not found
|
|
216
194
|
# @return [Object] The removed value or the result of the block
|
|
217
195
|
def remove(key)
|
|
218
|
-
synchronize_one(key) { @hash.delete(key) { yield if block_given? } }
|
|
196
|
+
result = synchronize_one(key) { @hash.delete(key) { yield if block_given? } }
|
|
197
|
+
synchronize_all { @locks.delete(key) }
|
|
198
|
+
result
|
|
219
199
|
end
|
|
220
200
|
|
|
221
201
|
# Remove all keys from the cache.
|
|
222
202
|
#
|
|
223
203
|
# @return [Hash] Empty hash
|
|
224
204
|
def remove_all
|
|
225
|
-
synchronize_all
|
|
205
|
+
synchronize_all do
|
|
206
|
+
@hash = {}
|
|
207
|
+
@locks = {}
|
|
208
|
+
end
|
|
226
209
|
end
|
|
227
210
|
|
|
228
211
|
# Remove all keys that match the block.
|
|
@@ -234,10 +217,10 @@ class Zache
|
|
|
234
217
|
synchronize_all do
|
|
235
218
|
count = 0
|
|
236
219
|
@hash.each_key do |k|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
220
|
+
next unless yield(k)
|
|
221
|
+
@hash.delete(k)
|
|
222
|
+
@locks.delete(k)
|
|
223
|
+
count += 1
|
|
241
224
|
end
|
|
242
225
|
count
|
|
243
226
|
end
|
|
@@ -250,7 +233,11 @@ class Zache
|
|
|
250
233
|
def clean
|
|
251
234
|
synchronize_all do
|
|
252
235
|
size_before = @hash.size
|
|
253
|
-
@hash.delete_if
|
|
236
|
+
@hash.delete_if do |key, _value|
|
|
237
|
+
expired = expired_unsafe?(key)
|
|
238
|
+
@locks.delete(key) if expired
|
|
239
|
+
expired
|
|
240
|
+
end
|
|
254
241
|
size_before - @hash.size
|
|
255
242
|
end
|
|
256
243
|
end
|
|
@@ -264,6 +251,84 @@ class Zache
|
|
|
264
251
|
|
|
265
252
|
private
|
|
266
253
|
|
|
254
|
+
# Checks if dirty value should be returned for a locked key
|
|
255
|
+
# @param key [Object] The key to check
|
|
256
|
+
# @param dirty [Boolean] Whether dirty reads are allowed
|
|
257
|
+
# @return [Boolean] True if dirty value should be returned
|
|
258
|
+
def should_return_dirty?(key, dirty)
|
|
259
|
+
(dirty || @dirty) && locked?(key) && expired_value?(key)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Checks if key has an expired value in cache
|
|
263
|
+
# @param key [Object] The key to check
|
|
264
|
+
# @return [Boolean] True if key exists and is expired
|
|
265
|
+
def expired_value?(key)
|
|
266
|
+
synchronize_all do
|
|
267
|
+
rec = @hash[key]
|
|
268
|
+
!rec.nil? && expired_unsafe?(key)
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Gets the dirty cached value without recalculation
|
|
273
|
+
# @param key [Object] The key to retrieve
|
|
274
|
+
# @return [Object] The cached value
|
|
275
|
+
def get_dirty_value(key)
|
|
276
|
+
synchronize_all { @hash[key][:value] }
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Handles eager mode get operation
|
|
280
|
+
# @param key [Object] The key to retrieve
|
|
281
|
+
# @param lifetime [Integer] Time in seconds until the key expires
|
|
282
|
+
# @param placeholder [Object] The placeholder to return immediately
|
|
283
|
+
# @yield Block that provides the value
|
|
284
|
+
# @return [Object] The placeholder value
|
|
285
|
+
def get_eager(key, lifetime, placeholder, &block)
|
|
286
|
+
return synchronize_all { @hash[key][:value] } if synchronize_all { @hash.key?(key) }
|
|
287
|
+
|
|
288
|
+
put(key, placeholder, lifetime: 0)
|
|
289
|
+
spawn_calculation_thread(key, lifetime, &block)
|
|
290
|
+
placeholder
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Spawns a background thread to calculate the value
|
|
294
|
+
# @param key [Object] The key to calculate for
|
|
295
|
+
# @param lifetime [Integer] Time in seconds until the key expires
|
|
296
|
+
# @yield Block that provides the value
|
|
297
|
+
def spawn_calculation_thread(key, lifetime, &block)
|
|
298
|
+
Thread.new do
|
|
299
|
+
synchronize_one(key) { calc(key, lifetime, &block) }
|
|
300
|
+
rescue StandardError => e
|
|
301
|
+
cleanup_failed_key(key)
|
|
302
|
+
raise e
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Cleans up a key after calculation failure
|
|
307
|
+
# @param key [Object] The key to clean up
|
|
308
|
+
def cleanup_failed_key(key)
|
|
309
|
+
synchronize_all do
|
|
310
|
+
@hash.delete(key)
|
|
311
|
+
@locks.delete(key)
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Gets value without a block (retrieval only mode)
|
|
316
|
+
# @param key [Object] The key to retrieve
|
|
317
|
+
# @param dirty [Boolean] Whether to return expired values
|
|
318
|
+
# @return [Object] The cached value
|
|
319
|
+
def get_without_block(key, dirty)
|
|
320
|
+
synchronize_all do
|
|
321
|
+
rec = @hash[key]
|
|
322
|
+
if expired_unsafe?(key)
|
|
323
|
+
return rec[:value] if dirty || @dirty
|
|
324
|
+
@hash.delete(key)
|
|
325
|
+
rec = nil
|
|
326
|
+
end
|
|
327
|
+
raise 'The key is absent in the cache' if rec.nil?
|
|
328
|
+
rec[:value]
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
267
332
|
# Calculates or retrieves a cached value for the given key.
|
|
268
333
|
# @param key [Object] The key to store the value under
|
|
269
334
|
# @param lifetime [Integer] Time in seconds until the key expires
|
|
@@ -308,9 +373,9 @@ class Zache
|
|
|
308
373
|
# @return [Object] The result of the block
|
|
309
374
|
def synchronize_one(key, &block)
|
|
310
375
|
return yield unless @sync
|
|
311
|
-
@mutex.synchronize do
|
|
376
|
+
mtx = @mutex.synchronize do
|
|
312
377
|
@locks[key] ||= Mutex.new
|
|
313
378
|
end
|
|
314
|
-
|
|
379
|
+
mtx.synchronize(&block)
|
|
315
380
|
end
|
|
316
381
|
end
|
data/zache.gemspec
CHANGED
|
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
|
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
|
|
9
9
|
s.required_ruby_version = '>= 2.5'
|
|
10
10
|
s.name = 'zache'
|
|
11
|
-
s.version = '0.15.
|
|
11
|
+
s.version = '0.15.2' # Version should be updated before release
|
|
12
12
|
s.license = 'MIT'
|
|
13
13
|
s.summary = 'In-memory Cache'
|
|
14
14
|
s.description = 'Zero-footprint in-memory thread-safe cache'
|