ExPirate v0.1.0 ExPirate
Yo-ho-ho! ExPirate
is a layer on top of the AgentMap
library that provides
TTL and statistics.
Supported attributes
Each stored value is wrapped in a map ("item"). This item may contain the following keys ("attributes"):
value: term
;ttl: pos_integer
— the period (in ms) counted from the insertion or the last update during which this item can be used. A default TTL value can be given as option tostart/1
orstart_link/1
and updated viaset_prop/3
;expired?: (item -> boolean)
— indicator of item to be expired (used instead of TTL).
— this attributes are user customized. Also, ExPirate
recognizes and
automatically updates the following properties of item:
inserted_at: integer
— is stamped withSystem.monotonic_time(:millisecond)
the first time item appears;updated: pos_integer
(times) — is an update count;updated_at: integer
— is stamped every time when item is changed;:used(_at)
— is stamped every time when value is requested;:missed(_at)
— is stamped every time when value is missing.
By default, ExPirate
stores only :inserted_at
(and later :updated_at
)
attribute for items with TTL. To turn on other statistics provide attrs: [:updated | :updated_at | :inserted_at | :used | …]
option on
start.
iex> {:ok, ep} = ExPirate.start() # ttl: ∞
...>
iex> ep
...> |> put(:key, 42)
...> |> get(:key)
42
iex> get(ep, :key, raw: true)
%{value: 42}
#
iex> ep
...> |> put(:key, 24, ttl: 5000)
...> |> get(:key, raw: true)
...> |> Map.keys()
[:ttl, :updated_at, :value]
Expired items
To decide which item is expired, either "time to live" attribute (:ttl
) is
used or a custom indicator (:expired?
).
TTL is a period of time in milliseconds, counted from insert (:inserted_at
attribute) or from the last update (:updated_at
). ExPirate
will never
return or use an expired item in calculations:
iex> {:ok, ep} = ExPirate.start()
...>
iex> ep
...> |> put(:key, 42, ttl: 20)
...> |> fetch(:key)
{:ok, 42}
#
iex> sleep(30)
...>
iex> fetch(ep, :key)
{:error, :expired}
#
iex> put(ep, :key, 43) # no TTL is given
...> #
iex> sleep(30)
iex> fetch(ep, :key)
{:ok, 43}
Also, can be used a custom indicator — an unary function that takes item as an argument:
iex> {:ok, ep} =
...> ExPirate.start(ttl: 20, attrs: [:used])
...>
iex> ep
...> |> put(:key, 42, expired?: & &1[:used] > 1)
...> |> fetch(:key)
{:ok, 42} # used: 1
#
iex> sleep(50)
iex> fetch(ep, :key)
{:ok, 42} # used: 2, … still there!
#
iex> sleep(50) # !
iex> fetch(ep, :key)
{:error, :expired} # used: 2
Link to this section Summary
Functions
Deletes value for key
while keeps statistics
Fetches the value for a specific key
Fetches the value for a specific key
, erroring out if value is missing or
expired
Returns the value for a specific key
Gets a value via the given fun
Puts value
under the key
. Statistics is updated
Start as an unlinked process
Starts instance that is linked to the current process
Synchronously stops the ExPirate
instance with the given reason
Link to this section Types
attr()
attr() ::
:inserted_at
| :used
| :used_at
| :updated
| :updated_at
| :missed
| :missed_at
| :expired?
| :ttl
| custom()
attr() :: :inserted_at | :used | :used_at | :updated | :updated_at | :missed | :missed_at | :expired? | :ttl | custom()
custom()
custom() :: atom()
custom() :: atom()
Custom attribute.
default()
default() :: term()
default() :: term()
ep()
ep() :: ExPirate.t()
ep() :: ExPirate.t()
item()
item() :: %{
optional(:value) => term(),
optional(:used_at | :missed_at | :updated_at | :inserted_at) => integer(),
optional(:used | :updated | :missed) => non_neg_integer(),
optional(:expired?) => (item() -> boolean()),
optional(:ttl) => pos_integer(),
optional(custom()) => term()
}
item() :: %{ optional(:value) => term(), optional(:used_at | :missed_at | :updated_at | :inserted_at) => integer(), optional(:used | :updated | :missed) => non_neg_integer(), optional(:expired?) => (item() -> boolean()), optional(:ttl) => pos_integer(), optional(custom()) => term() }
Wrapper for a value.
key()
key() :: any()
key() :: any()
reason()
reason() :: term()
reason() :: term()
t()
t() :: AgentMap.t()
t() :: AgentMap.t()
value()
value() :: any()
value() :: any()
Link to this section Functions
delete(ep, key, opts \\ [cast: true, !: :max, raw: false])
Deletes value for key
while keeps statistics.
Returns without waiting for the actual deletion to occur.
Default priority for this call is :max
.
Options
cast: false
— to return only after the actual delete;raw: true
— to delete item viaAgentMap.delete/3
;!: priority
,:max
;:timeout
,5000
.
Examples
iex> {:ok, ep} =
...> ExPirate.start(attrs: [:missed])
...>
iex> ep
...> |> put(:key, 42)
...> |> delete(:key)
...> |> fetch(:key)
{:error, :missing}
#
iex> get(ep, :key, raw: true) # statistics
%{missed: 1}
#
iex> ep
...> |> delete(:key, raw: true, cast: false)
...> |> get(:key, raw: true)
%{}
fetch(ep, key, opts \\ [!: :now])
Fetches the value for a specific key
.
Returns:
{:ok, value}
;{:error, :missing}
if item with suchkey
is missing or:value
attribute is not present;{:error, :expired}
if value is present, but is expired. You can still retrive an expired value viaget(ep, key, raw: true).value
.
Options
raw: true
— to return value with statistics, wrapped in an item map;!: priority
,:now
— to return only when calls with priorities higher than given are finished for thiskey
;:timeout
,5000
.
Examples
iex> {:ok, ep} =
...> ExPirate.start(attrs: [:missed, :used, :updated])
...>
iex> fetch(ep, :a)
{:error, :missing}
#
iex> ep
...> |> put(:a, 42, ttl: 20, cast: false)
...> |> fetch(:a)
{:ok, 42}
#
iex> sleep(20)
iex> fetch(ep, :a)
{:error, :expired}
#
iex> fetch(ep, :a, raw: true)
{:error, :expired}
#
iex> ep
...> |> get(:a, raw: true)
...> |> Map.delete(:inserted_at)
%{used: 1, missed: 3, ttl: 20}
fetch!(ep, key, opts \\ [!: :now])
Fetches the value for a specific key
, erroring out if value is missing or
expired.
Returns current value or raises a KeyError
.
See fetch/3
.
Options
!: priority
,:now
— to return only when calls with higher priorities are finished to execute for thiskey
;:timeout
,5000
.
Examples
iex> {:ok, ep} =
...> ExPirate.start()
iex> ep
...> |> put(:a, 1)
...> |> fetch!(:a)
1
iex> fetch!(ep, :b)
** (KeyError) key :b not found
iex> {:ok, ep} =
...> ExPirate.start(ttl: 20)
iex> ep
...> |> put(:a, 42)
...> |> fetch!(:a)
42
#
iex> sleep(30) # !
iex> fetch!(ep, :a)
** (ValueError) value for the key :a is expired
iex> {:ok, ep} =
...> ExPirate.start(attrs: [:inserted_at])
iex> ep
...> |> put(:a, 1)
...> |> delete(:a)
...> |> fetch!(:a)
** (KeyError) key :a not found
get(ep, key, opts \\ [!: :min, raw: false])
Returns the value for a specific key
.
This call has the :min
priority. As so, the value is retrived only after all
other calls for key
are completed.
See get/4
.
Options
:default
,nil
— value to return ifkey
is missing or expired;raw: true
— to return item instead of just value;!: priority
,:min
;:timeout
,5000
.
Examples
iex> {:ok, ep} =
...> ExPirate.start(attrs: [:used])
...>
iex> get(ep, :key)
nil # used: 0
#
iex> ep
...> |> put(:key, 42, expired?: & &1[:used] > 0)
...> |> get(:key)
42 # used: 1
#
iex> sleep(30) # !
iex> get(ep, :key)
nil # used: 1
iex> get(ep, :key, default: 0)
0 # used: 1
iex> get(ep, :key, raw: true, default: 0).value
0 # used: 1
#
#
iex> ep
...> |> get(:key, raw: true)
...> |> Map.take([:value, :used])
%{used: 1} # no value is returned
# as it's expired
iex> ep
...> |> AgentMap.get(:key)
...> |> Map.take([:value, :used])
%{value: 42, used: 1} # but it's still there
get(ep, key, fun, opts)
Gets a value via the given fun
.
A callback fun
is sent to an instance that invokes it, passing as an
argument the value associated with key
. The result of an invocation is
returned from this function. This call does not change value, and so, workers
execute a series of get
-calls as a parallel Task
s.
Options
:default
,nil
— value forkey
if it's missing or expired;!: priority
,:avg
;!: :now
— to execute call in a separateTask
spawned from server, passing current value (seeAgentMap.get/4
);:timeout
,5000
.
Examples
iex> {:ok, ep} = ExPirate.start()
iex> get(ep, :a, & &1)
nil
iex> ep
...> |> put(:a, 42)
...> |> get(:a, & &1 + 1)
43
iex> get(ep, :b, & &1 + 1, default: 0)
1
put(ep, key, value, opts \\ [cast: true, !: :max])
Puts value
under the key
. Statistics is updated.
Returns without waiting for the actual put.
Default priority for this call is :max
.
Options
expired?: (item -> boolean)
,get_prop(ep, :expired?)
— a custom indicator that item is expired;ttl: pos_integer
,get_prop(ep, :ttl)
— period (in ms) counted from:inserted_at
or:updated_at
, during which this item considered as not expired;attrs: [attr] | nil
,get_prop(ep, :attrs)
— custom attribute list for thekey
;cast: false
— to return after the actual put;!: priority
,:max
;:timeout
,5000
.
Examples
iex> {:ok, ep} =
...> ExPirate.start(attrs: [:used])
...>
iex> ep
...> |> put(:key, 42, expired?: & &1[:used] > 1) # used: 0
...> |> get(:key)
42 # used: 1
iex> fetch(ep, :key)
{:ok, 42} # used: 2
#
iex> sleep(50) # !
iex> fetch(ep, :key)
{:error, :expired}
#
iex> ep
...> |> put(:key, 43)
...> |> get(:key, raw: true)
%{value: 43, used: 2} # used: 3
start(opts \\ [])
start(keyword()) :: GenServer.on_start()
start(keyword()) :: GenServer.on_start()
Start as an unlinked process.
See start_link/1
for details.
start_link(opts \\ [])
start_link(keyword()) :: GenServer.on_start()
start_link(keyword()) :: GenServer.on_start()
Starts instance that is linked to the current process.
Options
name: term
— is used for registration as described in the module documentation;:debug
— is used to invoke the corresponding function in:sys
module;:spawn_opt
— is passed as options to the underlying process as inProcess.spawn/4
;:timeout
,5000
—ExPirate
is allowed to spend at most the given number of milliseconds on the whole process of initialization or it will be terminated;attrs: [:updated, :updated_at, :used, :used_at, :missed, :missed_at]
,[]
— attributes that are stored and automatically updated;expired?: (item -> boolean)
— custom indicator of expired item;ttl: pos_integer
— default period (in ms) counted from:updated_at
during which item is considered fresh.
Return values
If an instance is successfully created and initialized, the function returns
{:ok, pid}
, where pid
is the PID of the server. If a server with the
specified name already exists, the function returns {:error, {:already_started, pid}}
with the PID of that process.
stop(ep, reason \\ :normal, timeout \\ :infinity)
Synchronously stops the ExPirate
instance with the given reason
.
Returns :ok
if terminated with the given reason. If it terminates with
another reason, the call will exit.
This function keeps OTP semantics regarding error reporting. If the reason is
any other than :normal
, :shutdown
or {:shutdown, _}
, an error report
will be logged.
Examples
iex> {:ok, pid} = ExPirate.start_link()
iex> ExPirate.stop(pid)
:ok