Class: Zache
- Inherits:
-
Object
- Object
- Zache
- Defined in:
- lib/zache.rb
Overview
It is a very simple thread-safe in-memory cache with an ability to expire keys automatically, when their lifetime is over. Use it like this:
require 'zache'
zache = Zache.new
# Expires in 5 minutes
v = zache.get(:count, lifetime: 5 * 60) { expensive() }
For more information read README file.
- Author
-
Yegor Bugayenko ([email protected])
- Copyright
-
Copyright © 2018-2025 Yegor Bugayenko
- License
-
MIT
Defined Under Namespace
Classes: Fake
Instance Method Summary collapse
-
#clean ⇒ Integer
Remove keys that are expired.
-
#empty? ⇒ Boolean
Returns TRUE if the cache is empty, FALSE otherwise.
-
#exists?(key, dirty: false) ⇒ Boolean
Checks whether the value exists in the cache by the provided key.
-
#expired?(key) ⇒ Boolean
Checks whether the key exists in the cache and is expired.
-
#get(key, lifetime: 2**32, dirty: false, placeholder: nil, eager: false) { ... } ⇒ Object
Gets the value from the cache by the provided key.
-
#initialize(sync: true, dirty: false) ⇒ Zache
constructor
Makes a new object of the cache.
-
#locked?(key) ⇒ Boolean
Is key currently locked doing something?.
-
#mtime(key) ⇒ Time
Returns the modification time of the key, if it exists.
-
#put(key, value, lifetime: 2**32) ⇒ Object
Put a value into the cache.
-
#remove(key) { ... } ⇒ Object
Removes the value from the cache, by the provided key.
-
#remove_all ⇒ Hash
Remove all keys from the cache.
-
#remove_by {|key| ... } ⇒ Integer
Remove all keys that match the block.
-
#size ⇒ Integer
Total number of keys currently in cache.
Constructor Details
#initialize(sync: true, dirty: false) ⇒ Zache
Makes a new object of the cache.
“sync” is whether the hash is thread-safe (‘true`) or not (`false`); it is recommended to leave this parameter untouched, unless you really know what you are doing.
If the dirty argument is set to true, a previously calculated result will be returned if it exists, even if it is already expired.
81 82 83 84 85 86 87 |
# File 'lib/zache.rb', line 81 def initialize(sync: true, dirty: false) @hash = {} @sync = sync @dirty = dirty @mutex = Mutex.new @locks = {} end |
Instance Method Details
#clean ⇒ Integer
Remove keys that are expired. This cleans up the cache by removing all keys where the lifetime has been exceeded.
250 251 252 253 254 255 256 |
# File 'lib/zache.rb', line 250 def clean synchronize_all do size_before = @hash.size @hash.delete_if { |key, _value| expired_unsafe?(key) } size_before - @hash.size end end |
#empty? ⇒ Boolean
Returns TRUE if the cache is empty, FALSE otherwise.
261 262 263 |
# File 'lib/zache.rb', line 261 def empty? synchronize_all { @hash.empty? } end |
#exists?(key, dirty: false) ⇒ Boolean
Checks whether the value exists in the cache by the provided key. Returns TRUE if the value is here. If the key is already expired in the cache, it will be removed by this method and the result will be FALSE.
155 156 157 158 159 160 161 162 163 164 |
# File 'lib/zache.rb', line 155 def exists?(key, dirty: false) synchronize_all do rec = @hash[key] if expired_unsafe?(key) && !dirty && !@dirty @hash.delete(key) rec = nil end !rec.nil? end end |
#expired?(key) ⇒ Boolean
Checks whether the key exists in the cache and is expired. If the key is absent FALSE is returned.
171 172 173 |
# File 'lib/zache.rb', line 171 def expired?(key) synchronize_all { expired_unsafe?(key) } end |
#get(key, lifetime: 2**32, dirty: false, placeholder: nil, eager: false) { ... } ⇒ Object
Gets the value from the cache by the provided key.
If the value is not found in the cache, it will be calculated via the provided block. If the block is not given and the key doesn’t exist or is expired, an exception will be raised (unless dirty is set to true). The lifetime must be in seconds. The default lifetime is huge, which means that the key will never be expired.
If the dirty argument is set to true, a previously calculated result will be returned if it exists, even if it is already expired.
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/zache.rb', line 116 def get(key, lifetime: 2**32, dirty: false, placeholder: nil, eager: false, &block) if block_given? if (dirty || @dirty) && locked?(key) synchronize_all do return @hash[key][:value] if expired_unsafe?(key) && @hash.key?(key) end end if eager has_key = synchronize_all { @hash.key?(key) } return synchronize_all { @hash[key][:value] } if has_key put(key, placeholder, lifetime: 0) Thread.new do synchronize_one(key) { calc(key, lifetime, &block) } end placeholder else synchronize_one(key) { calc(key, lifetime, &block) } end else synchronize_all do rec = @hash[key] if expired_unsafe?(key) return rec[:value] if dirty || @dirty @hash.delete(key) rec = nil end raise 'The key is absent in the cache' if rec.nil? rec[:value] end end end |
#locked?(key) ⇒ Boolean
Is key currently locked doing something?
191 192 193 |
# File 'lib/zache.rb', line 191 def locked?(key) synchronize_all { @locks[key]&.locked? } end |
#mtime(key) ⇒ Time
Returns the modification time of the key, if it exists. If not, current time is returned.
180 181 182 183 184 185 |
# File 'lib/zache.rb', line 180 def mtime(key) synchronize_all do rec = @hash[key] rec.nil? ? Time.now : rec[:start] end end |
#put(key, value, lifetime: 2**32) ⇒ Object
Put a value into the cache.
201 202 203 204 205 206 207 208 209 |
# File 'lib/zache.rb', line 201 def put(key, value, lifetime: 2**32) synchronize_one(key) do @hash[key] = { value: value, start: Time.now, lifetime: lifetime } end end |
#remove(key) { ... } ⇒ Object
Removes the value from the cache, by the provided key. If the key is absent and the block is provided, the block will be called.
217 218 219 |
# File 'lib/zache.rb', line 217 def remove(key) synchronize_one(key) { @hash.delete(key) { yield if block_given? } } end |
#remove_all ⇒ Hash
Remove all keys from the cache.
224 225 226 |
# File 'lib/zache.rb', line 224 def remove_all synchronize_all { @hash = {} } end |
#remove_by {|key| ... } ⇒ Integer
Remove all keys that match the block.
233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/zache.rb', line 233 def remove_by synchronize_all do count = 0 @hash.each_key do |k| if yield(k) @hash.delete(k) count += 1 end end count end end |
#size ⇒ Integer
Total number of keys currently in cache.
92 93 94 |
# File 'lib/zache.rb', line 92 def size synchronize_all { @hash.size } end |