Create cache stores
Marten lets you easily create custom cache stores that you can then use as part of your application when it comes to perform caching operations.
Basic store definition
Defining a cache store is as simple as creating a class that inherits from the Marten::Caching::Store::Base
abstract class and that implements the following methods:
#clear
- called when clearing the cache#decrement
- called when decrementing an integer value in the cache#delete_entry
- called when deleting an entry from the cache#increment
- called when incrementing an integer value in the cache#read_entry
- called when reading an entry in the cache#write_entry
- called when writing an entry to the cache
For example, the following snippet implements an in-memory store that persists cache entries in a hash:
class MemoryStore < Marten::Cache::Store::Base
@data = {} of String => String
def initialize(
@namespace : String? = nil,
@expires_in : Time::Span? = nil,
@version : Int32? = nil,
@compress = false,
@compress_threshold = DEFAULT_COMPRESS_THRESHOLD
)
super
end
def clear : Nil
@data.clear
end
def decrement(
key : String,
amount : Int32 = 1,
expires_at : Time? = nil,
expires_in : Time::Span? = nil,
version : Int32? = nil,
race_condition_ttl : Time::Span? = nil,
compress : Bool? = nil,
compress_threshold : Int32? = nil
) : Int
apply_increment(
key,
amount: -amount,
expires_at: expires_at,
expires_in: expires_in,
version: version,
race_condition_ttl: race_condition_ttl,
compress: compress,
compress_threshold: compress_threshold
)
end
def increment(
key : String,
amount : Int32 = 1,
expires_at : Time? = nil,
expires_in : Time::Span? = nil,
version : Int32? = nil,
race_condition_ttl : Time::Span? = nil,
compress : Bool? = nil,
compress_threshold : Int32? = nil
) : Int
apply_increment(
key,
amount: amount,
expires_at: expires_at,
expires_in: expires_in,
version: version,
race_condition_ttl: race_condition_ttl,
compress: compress,
compress_threshold: compress_threshold
)
end
private getter data
private def apply_increment(
key : String,
amount : Int32 = 1,
expires_at : Time? = nil,
expires_in : Time::Span? = nil,
version : Int32? = nil,
race_condition_ttl : Time::Span? = nil,
compress : Bool? = nil,
compress_threshold : Int32? = nil
)
normalized_key = normalize_key(key.to_s)
entry = deserialize_entry(read_entry(normalized_key))
if entry.nil? || entry.expired? || entry.mismatched?(version || self.version)
write(
key: key,
value: amount.to_s,
expires_at: expires_at,
expires_in: expires_in,
version: version,
race_condition_ttl: race_condition_ttl,
compress: compress,
compress_threshold: compress_threshold
)
amount
else
new_amount = entry.value.to_i + amount
entry = Entry.new(new_amount.to_s, expires_at: entry.expires_at, version: entry.version)
write_entry(normalized_key, serialize_entry(entry))
new_amount
end
end
private def delete_entry(key : String) : Bool
deleted_entry = @data.delete(key)
!!deleted_entry
end
private def read_entry(key : String) : String?
data[key]?
end
private def write_entry(
key : String,
value : String,
expires_in : Time::Span? = nil,
race_condition_ttl : Time::Span? = nil
)
data[key] = value
true
end
end
Enabling the use of custom cache stores
Custom cache store can be used by assigning an instance of the corresponding class to the cache_store
setting.
For example:
config.cache_store = MemoryStore.new