# 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