CounterEx (CounterEx v0.2.0)
View SourceHigh-performance counter library with pluggable backends and namespace support.
CounterEx provides atomic counter operations with three backend options:
- ETS (default): Dynamic counters, unlimited capacity
- Atomics: Fixed capacity, ultra-fast operations
- Counters: Fixed capacity, write-optimized
Quick Start
# Start the counter server (default ETS backend)
{:ok, _pid} = CounterEx.start_link()
# Increment counters
{:ok, 1} = CounterEx.inc(:my_counter)
{:ok, 2} = CounterEx.inc(:my_counter)
# Get counter value
{:ok, 2} = CounterEx.get(:my_counter)
# Use namespaces to organize counters
{:ok, 1} = CounterEx.inc(:metrics, :http_requests)
{:ok, 1} = CounterEx.inc(:metrics, :db_queries)Backend Selection
# Use Atomics backend for maximum performance
{:ok, _pid} = CounterEx.start_link(
backend: CounterEx.Backend.Atomics,
backend_opts: [capacity: 10_000]
)
# Use Counters backend for write-heavy workloads
{:ok, _pid} = CounterEx.start_link(
backend: CounterEx.Backend.Counters,
backend_opts: [capacity: 5_000]
)Namespaces
Counters can be organized into namespaces for logical grouping:
{:ok, _} = CounterEx.inc(:http, :requests)
{:ok, _} = CounterEx.inc(:http, :errors)
{:ok, _} = CounterEx.inc(:db, :queries)
# Get all counters in a namespace
{:ok, counters} = CounterEx.all(:http)
# => %{requests: 1, errors: 1}
Summary
Functions
Get all counters in a namespace.
Run benchmarks comparing backend performance.
Returns a child spec for use in supervision trees.
Compare-and-swap operation.
Delete a counter.
Delete all counters in a namespace.
Get the current value of a counter.
Increment a counter by the given step (default: 1).
Get information about the backend and counters.
Reset a counter to the initial value (default: 0).
Set a counter to a specific value.
Returns supervisor child spec for backward compatibility.
Returns supervisor child spec with sweep interval for backward compatibility.
Start the CounterEx server.
Functions
@spec all(CounterEx.Backend.namespace()) :: {:ok, %{required(CounterEx.Backend.key()) => integer()}} | {:error, term()}
Get all counters in a namespace.
Returns a map of counter keys to their values.
Examples
{:ok, _} = CounterEx.inc(:counter1)
{:ok, _} = CounterEx.inc(:counter2)
{:ok, counters} = CounterEx.all()
# => %{counter1: 1, counter2: 1}
# With specific namespace
{:ok, counters} = CounterEx.all(:metrics)
Run benchmarks comparing backend performance.
Examples
CounterEx.benchmark()
CounterEx.benchmark(parallel: 4)
Returns a child spec for use in supervision trees.
Examples
children = [
{CounterEx, backend: CounterEx.Backend.ETS},
# ... other children
]
Supervisor.start_link(children, strategy: :one_for_one)
@spec compare_and_swap( CounterEx.Backend.namespace(), CounterEx.Backend.key(), integer(), integer() ) :: {:ok, integer()} | {:error, :mismatch, integer()} | {:error, term()}
Compare-and-swap operation.
Atomically updates the counter to new_value only if its current value equals expected.
Returns {:ok, new_value} on success, or {:error, :mismatch, current_value} if the
current value doesn't match the expected value.
Examples
{:ok, 10} = CounterEx.set(:my_counter, 10)
# CAS succeeds when value matches
{:ok, 20} = CounterEx.compare_and_swap(:my_counter, 10, 20)
# CAS fails when value doesn't match
{:error, :mismatch, 20} = CounterEx.compare_and_swap(:my_counter, 10, 30)
# With namespace
{:ok, 5} = CounterEx.compare_and_swap(:metrics, :requests, 0, 5)
@spec delete(CounterEx.Backend.namespace(), CounterEx.Backend.key()) :: :ok | {:error, term()}
Delete a counter.
Examples
{:ok, _} = CounterEx.inc(:my_counter)
:ok = CounterEx.delete(:my_counter)
{:ok, nil} = CounterEx.get(:my_counter)
# With namespace
:ok = CounterEx.delete(:metrics, :requests)
@spec delete_namespace(CounterEx.Backend.namespace()) :: :ok | {:error, term()}
Delete all counters in a namespace.
Examples
:ok = CounterEx.delete_namespace(:metrics)
@spec get(CounterEx.Backend.namespace(), CounterEx.Backend.key()) :: {:ok, integer() | nil} | {:error, term()}
Get the current value of a counter.
Returns {:ok, value} if the counter exists, or {:ok, nil} if it doesn't.
Examples
{:ok, nil} = CounterEx.get(:my_counter)
{:ok, _} = CounterEx.inc(:my_counter)
{:ok, 1} = CounterEx.get(:my_counter)
# With namespace
{:ok, value} = CounterEx.get(:metrics, :requests)
@spec inc( CounterEx.Backend.namespace(), CounterEx.Backend.key(), integer(), integer() ) :: {:ok, integer()} | {:error, term()}
Increment a counter by the given step (default: 1).
If the counter doesn't exist, it's initialized with default (default: 0) before incrementing.
Examples
{:ok, 1} = CounterEx.inc(:my_counter)
{:ok, 2} = CounterEx.inc(:my_counter)
{:ok, 12} = CounterEx.inc(:my_counter, 10)
# With namespace
{:ok, 1} = CounterEx.inc(:metrics, :requests)
# With default value
{:ok, 105} = CounterEx.inc(:counter, 5, 100)
Get information about the backend and counters.
Returns a map with metadata including:
- Backend type (
:ets,:atomics,:counters) - Number of counters
- Active namespaces
- Backend-specific information
Examples
{:ok, info} = CounterEx.info()
info.type
# => :ets
info.counters_count
# => 42
@spec reset(CounterEx.Backend.namespace(), CounterEx.Backend.key(), integer()) :: {:ok, integer()} | {:error, term()}
Reset a counter to the initial value (default: 0).
Examples
{:ok, _} = CounterEx.inc(:my_counter)
{:ok, 0} = CounterEx.reset(:my_counter)
# Reset to custom value
{:ok, 10} = CounterEx.reset(:my_counter, 10)
# With namespace
{:ok, 0} = CounterEx.reset(:metrics, :requests)
@spec set(CounterEx.Backend.namespace(), CounterEx.Backend.key(), integer()) :: {:ok, integer()} | {:error, term()}
Set a counter to a specific value.
Examples
{:ok, 42} = CounterEx.set(:my_counter, 42)
{:ok, 42} = CounterEx.get(:my_counter)
# With namespace
{:ok, 100} = CounterEx.set(:metrics, :requests, 100)
Returns supervisor child spec for backward compatibility.
Examples
children = [
CounterEx.start_keeper(),
# ... other children
]
@spec start_keeper_with_sweep(integer()) :: {module(), keyword()} | {:error, :interval_is_not_integer}
Returns supervisor child spec with sweep interval for backward compatibility.
Examples
children = [
CounterEx.start_keeper_with_sweep(60_000),
# ... other children
]
@spec start_link(keyword()) :: GenServer.on_start()
Start the CounterEx server.
Options
:backend- Backend module (default:CounterEx.Backend.ETS):backend_opts- Options for backend initialization:interval- Sweep interval in milliseconds (clears all counters periodically):name- GenServer name (default:CounterEx.Keeper)
Examples
# Start with default ETS backend
{:ok, pid} = CounterEx.start_link()
# Start with Atomics backend
{:ok, pid} = CounterEx.start_link(
backend: CounterEx.Backend.Atomics,
backend_opts: [capacity: 10_000]
)