Packages
An in-memory caching library using tuples as the core data type, with support for structs, key/value pairs, queues, and stacks. Includes experimental distributed caching.
Current section
Files
Jump to
Current section
Files
lib/storage/storage_api.ex
defmodule SuperCache.Storage do
@moduledoc false
require Logger
alias SuperCache.{EtsHolder, Config}
alias :ets, as: Ets
## Lifecycle ##
@doc "Create all ETS partitions."
@spec start(pos_integer) :: :ok
def start(num) when is_integer(num) and num > 0 do
prefix = Config.get_config(:table_prefix)
for order <- 0..(num - 1) do
name = table_name(prefix, order)
EtsHolder.new_table(EtsHolder, name)
end
:ok
end
@doc "Delete all ETS partitions."
@spec stop(pos_integer) :: :ok
def stop(num) when is_integer(num) and num > 0 do
Logger.debug(fn -> "super_cache, storage, stopping #{num} partition(s)" end)
prefix = Config.get_config(:table_prefix)
for order <- 0..(num - 1) do
name = table_name(prefix, order)
Logger.debug(fn -> "super_cache, storage, deleting #{inspect(name)}" end)
EtsHolder.delete_table(EtsHolder, name)
end
:ok
end
## Write ##
@doc "Insert one or more tuples into a partition."
@spec put([tuple] | tuple, atom | :ets.tid()) :: true
def put(term, partition), do: Ets.insert(partition, term)
@doc """
Insert a tuple only if no entry with the same key exists.
Returns `true` on success, `false` if the key is already present.
Works only with `:set` / `:ordered_set` table types.
"""
@spec insert_new(tuple, atom | :ets.tid()) :: boolean
def insert_new(term, partition), do: Ets.insert_new(partition, term)
@doc "Update specific element(s) of an existing record. Optional default for missing keys."
def update_element(key, partition, element_spec, default),
do: Ets.update_element(partition, key, element_spec, default)
def update_element(key, partition, element_spec),
do: Ets.update_element(partition, key, element_spec)
@doc "Increment/decrement a counter field. Optional default for missing keys."
def update_counter(key, partition, counter_spec, default),
do: Ets.update_counter(partition, key, counter_spec, default)
def update_counter(key, partition, counter_spec),
do: Ets.update_counter(partition, key, counter_spec)
## Read ##
@doc "Look up records by key."
@spec get(any, atom | :ets.tid()) :: [tuple]
def get(key, partition), do: Ets.lookup(partition, key)
@doc "Pattern match — returns list of bound variable lists (`:ets.match/2`)."
@spec get_by_match(atom | tuple, atom | :ets.tid()) :: [[any]]
def get_by_match(pattern, partition) do
Logger.debug(fn -> "super_cache, storage, match pattern=#{inspect(pattern)} partition=#{partition}" end)
Ets.match(partition, pattern)
end
@doc "Pattern match — returns full matching objects (`:ets.match_object/2`)."
@spec get_by_match_object(atom | tuple, atom | :ets.tid()) :: [tuple]
def get_by_match_object(pattern, partition) do
Logger.debug(fn -> "super_cache, storage, match_object pattern=#{inspect(pattern)} partition=#{partition}" end)
Ets.match_object(partition, pattern)
end
@doc "Fold over all records in a partition."
@spec scan((any, any -> any), any, atom | :ets.tid()) :: any
def scan(fun, acc, partition), do: Ets.foldl(fun, acc, partition)
@doc "Remove and return a record by key (atomic take)."
@spec take(any, atom | :ets.tid()) :: [tuple]
def take(key, partition), do: Ets.take(partition, key)
## Delete ##
@doc "Delete a record by key."
@spec delete(any, atom | :ets.tid()) :: true
def delete(key, partition), do: Ets.delete(partition, key)
@doc "Delete all records in a partition."
@spec delete_all(atom | :ets.tid()) :: true
def delete_all(partition), do: Ets.delete_all_objects(partition)
@doc "Delete all records matching `pattern`."
@spec delete_match(atom | tuple, atom | :ets.tid()) :: true
def delete_match(pattern, partition) do
Logger.debug(fn -> "super_cache, storage, delete_match pattern=#{inspect(pattern)} partition=#{partition}" end)
Ets.match_delete(partition, pattern)
end
## Stats ##
@doc "Return `{partition, record_count}` for a partition."
@spec stats(atom | :ets.tid()) :: {atom | :ets.tid(), non_neg_integer}
def stats(partition), do: {partition, Ets.info(partition, :size)}
## Private ##
defp table_name(prefix, order), do: String.to_atom("#{prefix}_#{order}")
end