Cache.Counter (elixir_cache v0.4.5)
View SourceAtomic integer counter adapter backed by Erlang's :counters module.
Counter values are stored in a lock-free :counters array. The array reference
is stored in :persistent_term so all processes can access it without a
process round-trip.
The slot index for a key is determined as follows:
- Integer key — used directly as the 0-based slot index (
key + 1internally, since:countersis 1-based). Callers control exactly which slot is accessed with no hashing involved. - Atom / string key — index computed deterministically via
:erlang.phash2(key, size) + 1, eliminating any key-to-index bookkeeping and the race conditions that come with it.
Behaviour
put/4accepts only1or-1as values, acting as increment or decrement. Any other value returns an error.get/2accepts only a non-negative integer key and returns the current integer value for that slot. Returns0if the slot has never been incremented.delete/2zeroes the counter slot for the given key. Because multiple keys may hash to the same slot (especially with a smallinitial_size), deleting one key resets the slot shared by all keys that collide with it.increment/1,2anddecrement/1,2are injected into consumer modules viause.
Hash collisions
With a small initial_size, distinct keys may map to the same counter slot.
Operations on colliding keys are summed in that shared slot. Increase
initial_size to reduce collision probability.
Options
:initial_size(pos_integer/0) - Number of counter slots to pre-allocate The default value is1.:write_concurrency(boolean/0) - Enable concurrent writes to different counter slots. When false, all writes serialize through a single process. The default value isfalse.
Example
defmodule MyApp.Cache do
use Cache,
adapter: Cache.Counter,
name: :my_app_counter_cache,
opts: [initial_size: 32]
end
MyApp.Cache.increment(:page_views)
MyApp.Cache.decrement(:active_users)
{:ok, count} = MyApp.Cache.get(:page_views)
Summary
Functions
Returns a specification to start this module under a supervisor.
Functions
Returns a specification to start this module under a supervisor.
arg is passed as the argument to Task.start_link/1 in the :start field
of the spec.
For more information, see the Supervisor module,
the Supervisor.child_spec/2 function and the Supervisor.child_spec/0 type.
@spec decrement(atom(), atom() | String.t() | non_neg_integer(), pos_integer()) :: :ok | ErrorMessage.t()
@spec increment(atom(), atom() | String.t() | non_neg_integer(), pos_integer()) :: :ok | ErrorMessage.t()