89 lines
2.7 KiB
Crystal
89 lines
2.7 KiB
Crystal
# Common database: only **recently added or requested** entries are kept in memory.
|
|
#
|
|
# Least recently used entries may be removed from the cache in order to keep the amount of memory used reasonable.
|
|
#
|
|
# The number of entries to keep in memory is **configurable**.
|
|
#
|
|
# This database is relevant for high demand applications;
|
|
# which means both a high number of entries (data cannot fit entirely in RAM),
|
|
# and a high number of requests, often to reach the same entries.
|
|
# Typically a retail website.
|
|
# In such applications, the "keep the most recently used data in cache" policy works since new users
|
|
# constantly ask for the same data over and over.
|
|
#
|
|
# ```
|
|
# # Creates a DODB database for common usage (a limited number of cached entries).
|
|
# car_database = DODB::Storage::Common.new "/path/to/db"
|
|
#
|
|
# # Creates a (cached) index.
|
|
# cars_by_name = car_database.new_index "name", &.name
|
|
#
|
|
# # Add a value in the database.
|
|
# car_database << Car.new "Corvet"
|
|
# ```
|
|
# On the file-system:
|
|
# ```plain
|
|
# storage
|
|
# ├── data
|
|
# │ └── 0000000000
|
|
# ├── indices
|
|
# │ └── by_name <- the "name" basic index
|
|
# │ └── Corvet -> ../../data/0000000000
|
|
# ```
|
|
#
|
|
# NOTE: fast for frequently requested data and requires a stable (and configurable) amount of memory.
|
|
class DODB::Storage::Common(V) < DODB::Storage::Cached(V)
|
|
# The *fifo* is an instance of `EfficientFIFO` where the key of the requested data is pushed.
|
|
# In case the number of stored entries exceeds what is allowed, the least recently used entry is removed.
|
|
property fifo : EfficientFIFO(Int32)
|
|
|
|
# Initializes the `DODB::Storage::Common` database with a maximum number of entries in the cache.
|
|
def initialize(@directory_name : String, max_entries : UInt32)
|
|
@fifo = EfficientFIFO(Int32).new max_entries
|
|
Dir.mkdir_p data_path
|
|
Dir.mkdir_p locks_directory
|
|
|
|
@cached_last_key = init_last_key
|
|
end
|
|
|
|
# Verifies that the value is in cache, or read it on disk.
|
|
# Pushes the key in the fifo.
|
|
def [](key : Int32) : V
|
|
val = @data[key]?
|
|
if val.nil?
|
|
raise MissingEntry.new(key) unless ::File.exists? file_path key
|
|
val = read file_path key
|
|
@data[key] = val
|
|
end
|
|
push_fifo key
|
|
val
|
|
end
|
|
|
|
# Assumes new entries are more requested than old ones.
|
|
def []=(key : Int32, value : V)
|
|
super key, value
|
|
push_fifo key
|
|
end
|
|
|
|
# :inherit:
|
|
#
|
|
# Assumes new entries are more requested than old ones.
|
|
def <<(item : V)
|
|
key = super item
|
|
push_fifo key
|
|
end
|
|
|
|
def unsafe_delete(key : Int32)
|
|
@fifo.delete key if super key
|
|
end
|
|
|
|
def delete(key : Int32)
|
|
@fifo.delete key if super key
|
|
end
|
|
|
|
private def push_fifo(key : Int32)
|
|
if entry_to_remove = @fifo << key
|
|
@data.delete entry_to_remove
|
|
end
|
|
end
|
|
end
|