AuroraMeter.Counter (Aurora Meter v0.1.0)

View Source

The hot path: atomic ETS counter operations.

Increments use :ets.update_counter/3 (lock-free, concurrency-safe). A cold key is seeded once from the last flushed database value (ensure_seeded/4) so the in-ETS value is always absolute; after that first touch the path is pure ETS. Every mutation marks the key dirty for the flusher.

Summary

Types

A counter key: {tenant_key, feature, period_start}.

Functions

Returns a map of feature => value for a tenant's warm counters in a period.

Removes a single key from the dirty set.

Snapshots the current set of dirty keys.

Increments a counter by qty and returns the new value.

Marks a counter key dirty so the flusher persists it.

Releases a previously reserved qty (rollback on a raised function).

Atomically reserves qty against an optional hard limit.

Returns the current value for a counter (rehydrating from the database if cold).

Types

key()

@type key() :: {String.t(), atom(), DateTime.t()}

A counter key: {tenant_key, feature, period_start}.

Functions

all_for(tenant_key, period_start)

@spec all_for(String.t(), DateTime.t()) :: %{required(atom()) => integer()}

Returns a map of feature => value for a tenant's warm counters in a period.

clear_dirty(key)

@spec clear_dirty(key()) :: :ok

Removes a single key from the dirty set.

dirty_keys()

@spec dirty_keys() :: [key()]

Snapshots the current set of dirty keys.

incr(tenant_key, feature, qty, period_start)

@spec incr(String.t(), atom(), integer(), DateTime.t()) :: integer()

Increments a counter by qty and returns the new value.

mark_dirty(key)

@spec mark_dirty(key()) :: :ok

Marks a counter key dirty so the flusher persists it.

release(tenant_key, feature, qty, period_start)

@spec release(String.t(), atom(), integer(), DateTime.t()) :: :ok

Releases a previously reserved qty (rollback on a raised function).

reserve(tenant_key, feature, qty, period_start, limit)

@spec reserve(String.t(), atom(), integer(), DateTime.t(), non_neg_integer() | nil) ::
  :ok | {:error, :limit_exceeded}

Atomically reserves qty against an optional hard limit.

Increments first; if the new value exceeds limit it rolls the increment back and returns {:error, :limit_exceeded}. A nil limit always succeeds.

value(tenant_key, feature, period_start)

@spec value(String.t(), atom(), DateTime.t()) :: integer()

Returns the current value for a counter (rehydrating from the database if cold).