segmented_cache (segmented_cache v0.1.1)

segmented_cache is a key/value pairs cache library implemented in rotating segments.

For more information, see the README, and the function documentation.

Link to this section Summary

Link to this section Types

Link to this type

iterative_fun/1

Specs

iterative_fun(Term) :: fun((ets:tid(), Term) -> {continue | stop, not_found | term()}).
Link to this type

merger_fun/1

Specs

merger_fun(Term) :: fun((Term, Term) -> Term).

Specs

name() :: atom().

Specs

opts() ::
    #{strategy => strategy(),
      segment_num => non_neg_integer(),
      ttl => timeout() | {erlang:time_unit(), non_neg_integer()},
      merger_fun => merger_fun(term())}.

Specs

strategy() :: fifo | lru.

Link to this section Functions

Link to this function

assert_parameters(Opts)

Link to this function

delete_entry(Name, Key)

Specs

delete_entry(name(), term()) -> true.
Link to this function

delete_pattern(Name, Pattern)

Specs

delete_pattern(name(), term()) -> true.
Link to this function

get_entry(Name, Key)

Specs

get_entry(name(), term()) -> term() | not_found.

Get the entry for Key in cache

Raises telemetry event name: [?MODULE, request] measurements: #{hit => boolean(), time => microsecond()} metadata: #{name => atom()}
Link to this function

is_member(Name, Key)

Specs

is_member(name(), term()) -> boolean().

Check if Key is cached

Raises telemetry event name: [?MODULE, request] measurements: #{hit => boolean(), time => microsecond()} metadata: #{name => atom()}
Link to this function

iterate_fun_in_tables(Name, Key, IterativeFun)

Specs

iterate_fun_in_tables(name(), Key, IterativeFun) -> term()
                         when Key :: term(), IterativeFun :: iterative_fun(Key).
Link to this function

iterate_fun_in_tables(IterativeFun, Key, Segments, Int, Int, Int)

Specs

iterate_fun_in_tables(iterative_fun(Key), Key, tuple(), Int, Int, Int) ->
                         {not_found | non_neg_integer(), Value}
                         when Key :: term(), Value :: term(), Int :: non_neg_integer().
Link to this function

merge_entry(Name, Key, Value)

Specs

merge_entry(name(), term(), term()) -> boolean().

Merge a new entry into an existing one, or add it at the front if none is found.

Race conditions considerations:
  • Two writers: compare_and_swap will ensure they both succeed sequentially
  • Any writers and the cleaner: under fifo, the writer modifies the record in place and doesn't need to be concerned with rotation. Under lru, the same considerations than for a put_entry_front apply.
  • Link to this function

    post_insert_check_should_retry(_, Index, _)

    Specs

    post_insert_check_should_retry(boolean(), integer(), integer()) -> boolean().
    Link to this function

    put_entry(Name, Key, Value)

    Specs

    put_entry(name(), term(), term()) -> boolean().

    Add an entry to the first table in the segments.

    Possible race conditions:

  • Two writers: another process might attempt to put a record at the same time. It this case, both writers will attempt ets:insert_new, resulting in only one of them succeeding. The one that fails, will retry three times a compare_and_swap, attempting to merge the values and ensuring no data is lost.
  • One worker and the cleaner: there's a chance that by the time we insert in the ets table, this table is not the first anymore because the cleaner has taken action and pushed it behind.
  • Two writers and the cleaner: a mix of the previous, it can happen that two writers can attempt to put a record at the same time, but exactly in-between, the cleaner rotates the tables, resulting in the first writter inserting in the table that immediately becomes the second, and the latter writter inserting in the recently treated as first, shadowing the previous.
  • To treat the data race with the cleaner, after a successful insert, we re-check the index, and if it has changed, we restart the whole operation again: we can be sure that no more rotations will be triggered in a while, so the second round will be final.

    Strategy considerations: under a fifo strategy, no other writes can happen, but under a lru strategy, many other workers might attemp to move a record forward. In this case, the forwarding movement doesn't modify the record, and therefore the compare_and_swap operation should succeed at once; then, once the record is in the front, all other workers shouldn't be attempting to move it.
    Link to this function

    send_to_group(Name, Msg)

    Specs

    start(name()) -> {ok, pid()}.

    Start a cache entity in the local node

    Name must be an atom. Then the cache will be identified by the pair {?MODULE, Name}, and an entry in persistent_term will be created and the worker will join a pg group of the same name. Opts is a map containing the configuration. strategy can be fifo or lru. Default is fifo. segment_num is the number of segments for the cache. Default is 3ttl is the live, in minutes, of _each_ segment. Default is 480, i.e., 8 hours. merger_fun is a function that, given a conflict, takes in order the old and new values and applies a merging strategy. See the merger_fun/1 type
    Link to this function

    start(Name, Opts)

    Specs

    start(name(), opts()) -> {ok, pid()}.
    Link to this function

    start_link(Name)

    Specs

    start_link(name()) -> {ok, pid()}.
    Link to this function

    start_link(Name, Opts)

    Specs

    start_link(name(), opts()) -> {ok, pid()}.