Class: Litecache
- Inherits:
-
Object
- Object
- Litecache
- Defined in:
- lib/litestack/litecache.rb
Overview
Litecache is a caching library for Ruby applications that is built on top of SQLite. It is designed to be simple to use, very fast, and feature-rich, providing developers with a reliable and efficient way to cache data.
One of the main features of Litecache is automatic key expiry, which allows developers to set an expiration time for each cached item. This ensures that cached data is automatically removed from the cache after a certain amount of time has passed, reducing the risk of stale data being served to users.
In addition, Litecache supports LRU (Least Recently Used) removal, which means that if the cache reaches its capacity limit, the least recently used items will be removed first to make room for new items. This ensures that the most frequently accessed data is always available in the cache.
Litecache also supports integer value increment/decrement, which allows developers to increment or decrement the value of a cached item in a thread-safe manner. This is useful for implementing counters or other types of numerical data that need to be updated frequently.
Overall, Litecache is a powerful and flexible caching library that provides automatic key expiry, LRU removal, and integer value increment/decrement capabilities. Its fast performance and simple API make it an excellent choice for Ruby applications that need a reliable and efficient way to cache data.
Constant Summary collapse
- DEFAULT_OPTIONS =
the default options for the cache can be overridden by passing new options in a hash to Litecache.new
path: "./cache.db" expiry: 60 * 60 * 24 * 30 -> one month default expiry if none is provided size: 128 * 1024 * 1024 -> 128MB mmap_size: 128 * 1024 * 1024 -> 128MB to be held in memory min_size: 32 * 1024 -> 32KB return_full_record: false -> only return the payload sleep_interval: 1 -> 1 second of sleep between cleanup runs
{ path: Litesupport.root.join("cache.sqlite3"), config_path: "./litecache.yml", sync: 0, expiry: 60 * 60 * 24 * 30, # one month size: 128 * 1024 * 1024, # 128MB mmap_size: 128 * 1024 * 1024, # 128MB min_size: 8 * 1024 * 1024, # 16MB return_full_record: false, # only return the payload sleep_interval: 30, # 30 seconds metrics: false }
Instance Method Summary collapse
-
#clear ⇒ Object
delete all key, value pairs in the cache.
-
#close ⇒ Object
close the connection to the cache file.
-
#count ⇒ Object
return the number of key, value pairs in the cache.
-
#decrement(key, amount = 1, expires_in = nil) ⇒ Object
decrement an integer value by amount, optionally add an expiry value (in seconds).
-
#delete(key) ⇒ Object
delete a key, value pair from the cache.
-
#get(key) ⇒ Object
get a value by its key if the key doesn’t exist or it is expired then null will be returned.
-
#get_multi(*keys) ⇒ Object
get multiple values by their keys, a hash with values corresponding to the keys is returned,.
-
#increment(key, amount = 1, expires_in = nil) ⇒ Object
increment an integer value by amount, optionally add an expiry value (in seconds).
-
#initialize(options = {}) ⇒ Litecache
constructor
creates a new instance of Litecache can optionally receive an options hash which will be merged with the DEFAULT_OPTIONS (the new hash overrides any matching keys in the default one).
-
#max_size ⇒ Object
return the maximum size of the cache.
-
#prune(limit = nil) ⇒ Object
delete all entries in the cache up limit (ordered by LRU), if no limit is provided approximately 20% of the entries will be deleted.
-
#set(key, value, expires_in = nil) ⇒ Object
add a key, value pair to the cache, with an optional expiry value (number of seconds).
-
#set_multi(keys_and_values, expires_in = nil) ⇒ Object
set multiple keys and values in one shot set_multi({k1: v1, k2: v2, … }).
-
#set_unless_exists(key, value, expires_in = nil) ⇒ Object
add a key, value pair to the cache, but only if the key doesn’t exist, with an optional expiry value (number of seconds).
- #snapshot ⇒ Object
-
#transaction(mode = :immediate) ⇒ Object
low level access to SQLite transactions, use with caution.
Methods included from Litemetric::Measurable
#capture, #capture_snapshot, #collect_metrics, #create_snapshotter, #measure, #metrics_identifier
Methods included from Litesupport::Liteconnection
#journal_mode, #options, #path, #size, #synchronous
Methods included from Litesupport::Forkable
Constructor Details
#initialize(options = {}) ⇒ Litecache
creates a new instance of Litecache can optionally receive an options hash which will be merged with the DEFAULT_OPTIONS (the new hash overrides any matching keys in the default one).
Example:
litecache = Litecache.new
litecache.set("a", "somevalue")
litecache.get("a") # => "somevalue"
litecache.set("b", "othervalue", 1) # expire aftre 1 second
litecache.get("b") # => "othervalue"
sleep 2
litecache.get("b") # => nil
litecache.clear # nothing remains in the cache
litecache.close # optional, you can safely kill the process
64 65 66 67 68 69 |
# File 'lib/litestack/litecache.rb', line 64 def initialize( = {}) [:size] = DEFAULT_OPTIONS[:min_size] if [:size] && [:size] < DEFAULT_OPTIONS[:min_size] init() @expires_in = @options[:expiry] || 60 * 60 * 24 * 30 collect_metrics if @options[:metrics] end |
Instance Method Details
#clear ⇒ Object
delete all key, value pairs in the cache
197 198 199 |
# File 'lib/litestack/litecache.rb', line 197 def clear run_sql("delete FROM data") end |
#close ⇒ Object
close the connection to the cache file
202 203 204 205 |
# File 'lib/litestack/litecache.rb', line 202 def close @running = false super end |
#count ⇒ Object
return the number of key, value pairs in the cache
187 188 189 |
# File 'lib/litestack/litecache.rb', line 187 def count run_stmt(:counter)[0][0] end |
#decrement(key, amount = 1, expires_in = nil) ⇒ Object
decrement an integer value by amount, optionally add an expiry value (in seconds)
169 170 171 |
# File 'lib/litestack/litecache.rb', line 169 def decrement(key, amount = 1, expires_in = nil) increment(key, -amount, expires_in) end |
#delete(key) ⇒ Object
delete a key, value pair from the cache
153 154 155 156 157 158 159 160 |
# File 'lib/litestack/litecache.rb', line 153 def delete(key) changes = 0 @conn.acquire do |cache| cache.stmts[:deleter].execute!(key) changes = cache.changes end changes > 0 end |
#get(key) ⇒ Object
get a value by its key if the key doesn’t exist or it is expired then null will be returned
124 125 126 127 128 129 130 131 132 |
# File 'lib/litestack/litecache.rb', line 124 def get(key) key = key.to_s if (record = @conn.acquire { |cache| cache.stmts[:getter].execute!(key)[0] }) capture(:get, key, 1) return record[1] end capture(:get, key, 0) nil end |
#get_multi(*keys) ⇒ Object
get multiple values by their keys, a hash with values corresponding to the keys is returned,
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/litestack/litecache.rb', line 136 def get_multi(*keys) results = {} transaction(:deferred) do |conn| keys.length.times do |i| key = keys[i].to_s if (record = conn.stmts[:getter].execute!(key)[0]) results[keys[i]] = record[1] # use the original key format capture(:get, key, 1) else capture(:get, key, 0) end end end results end |
#increment(key, amount = 1, expires_in = nil) ⇒ Object
increment an integer value by amount, optionally add an expiry value (in seconds)
163 164 165 166 |
# File 'lib/litestack/litecache.rb', line 163 def increment(key, amount = 1, expires_in = nil) expires_in ||= @expires_in @conn.acquire { |cache| cache.stmts[:incrementer].execute!(key.to_s, amount, expires_in) } end |
#max_size ⇒ Object
return the maximum size of the cache
208 209 210 |
# File 'lib/litestack/litecache.rb', line 208 def max_size run_sql("SELECT s.page_size * c.max_page_count FROM pragma_page_size() as s, pragma_max_page_count() as c")[0][0].to_f / (1024 * 1024) end |
#prune(limit = nil) ⇒ Object
delete all entries in the cache up limit (ordered by LRU), if no limit is provided approximately 20% of the entries will be deleted
174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/litestack/litecache.rb', line 174 def prune(limit = nil) @conn.acquire do |cache| if limit&.is_a? Integer cache.stmts[:limited_pruner].execute!(limit) elsif limit&.is_a? Float cache.stmts[:extra_pruner].execute!(limit) else cache.stmts[:pruner].execute! end end end |
#set(key, value, expires_in = nil) ⇒ Object
add a key, value pair to the cache, with an optional expiry value (number of seconds)
72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/litestack/litecache.rb', line 72 def set(key, value, expires_in = nil) key = key.to_s expires_in ||= @expires_in @conn.acquire do |cache| cache.stmts[:setter].execute!(key, value, expires_in) capture(:set, key) rescue SQLite3::FullException cache.stmts[:extra_pruner].execute!(0.2) cache.execute("vacuum") retry end true end |
#set_multi(keys_and_values, expires_in = nil) ⇒ Object
set multiple keys and values in one shot set_multi({k1: v1, k2: v2, … })
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/litestack/litecache.rb', line 87 def set_multi(keys_and_values, expires_in = nil) expires_in ||= @expires_in transaction do |conn| keys_and_values.each_pair do |k, v| key = k.to_s conn.stmts[:setter].execute!(key, v, expires_in) capture(:set, key) rescue SQLite3::FullException conn.stmts[:extra_pruner].execute!(0.2) conn.execute("vacuum") retry end end true end |
#set_unless_exists(key, value, expires_in = nil) ⇒ Object
add a key, value pair to the cache, but only if the key doesn’t exist, with an optional expiry value (number of seconds)
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/litestack/litecache.rb', line 104 def set_unless_exists(key, value, expires_in = nil) key = key.to_s expires_in ||= @expires_in changes = 0 @conn.acquire do |cache| cache.transaction(:immediate) do cache.stmts[:inserter].execute!(key, value, expires_in) changes = cache.changes end capture(:set, key) rescue SQLite3::FullException cache.stmts[:extra_pruner].execute!(0.2) cache.execute("vacuum") retry end changes > 0 end |
#snapshot ⇒ Object
212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/litestack/litecache.rb', line 212 def snapshot { summary: { path: path, journal_mode: journal_mode, synchronous: synchronous, size: size, max_size: max_size, entries: count } } end |
#transaction(mode = :immediate) ⇒ Object
low level access to SQLite transactions, use with caution
226 227 228 229 230 231 232 233 234 235 236 |
# File 'lib/litestack/litecache.rb', line 226 def transaction(mode = :immediate) @conn.acquire do |cache| if cache.transaction_active? yield else cache.transaction(mode) do yield cache end end end end |