CounterEx.Backend.Counters (CounterEx v0.2.0)

View Source

Counters-based backend for CounterEx using Erlang's :counters module.

This backend uses Erlang's :counters for write-optimized atomic counter operations:

  • Fixed capacity: Pre-allocated array of atomic counters
  • Write-optimized: Optimized for heavy concurrent writes
  • No GC pressure: Counters live outside the process heap
  • Key mapping: Uses ETS to map keys to array indices

Characteristics

  • Performance: Optimized for write-heavy workloads
  • Memory: Fixed at initialization
  • Limitations: Fixed number of counters per namespace (default: 1000)

Configuration

Options:

  • :capacity - Max counters per namespace (default: 1000)
  • :write_concurrency - Enable write concurrency optimization (default: true)

When to Use

Use this backend when:

  • You have write-heavy workloads with many concurrent writers
  • You have a bounded, known set of counters
  • You can pre-allocate capacity
  • You need predictable memory usage

Atomics vs Counters

  • Atomics: Better for read-heavy or mixed workloads
  • Counters: Better for write-heavy workloads with many concurrent writers

Limitations

  • Cannot create more counters than the configured capacity
  • Deleting a counter doesn't free its slot (reusable after explicit delete)
  • Each namespace requires separate counter array
  • Counters do not support compare-and-swap natively (emulated with get+put)

Example

# Initialize with capacity for 5,000 counters
{:ok, state} = CounterEx.Backend.Counters.init(capacity: 5_000)
{:ok, 1} = CounterEx.Backend.Counters.inc(state, :default, :requests, 1, 0)
{:ok, 1} = CounterEx.Backend.Counters.get(state, :default, :requests)

Summary

Types

t()

@type t() :: %CounterEx.Backend.Counters{
  capacity: pos_integer(),
  metadata: :ets.tid(),
  registry: :ets.tid(),
  write_concurrency: boolean()
}