# `Cache.Counter`
[🔗](https://github.com/mikaak/elixir_cache/blob/main/lib/cache/counter.ex#L1)

Atomic 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 + 1`
  internally, since `:counters` is 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/4` accepts only `1` or `-1` as values, acting as increment or decrement.
  Any other value returns an error.
- `get/2` accepts only a non-negative integer key and returns the current
  integer value for that slot. Returns `0` if the slot has never been incremented.
- `delete/2` zeroes the counter slot for the given key. Because multiple keys
  may hash to the same slot (especially with a small `initial_size`), deleting
  one key resets the slot shared by all keys that collide with it.
- `increment/1,2` and `decrement/1,2` are injected into consumer modules via `use`.

## 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` (`t:pos_integer/0`) - Number of counter slots to pre-allocate The default value is `1`.

* `:write_concurrency` (`t:boolean/0`) - Enable concurrent writes to different counter slots. When false, all writes serialize through a single process. The default value is `false`.

## Example

```elixir
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)
```

# `child_spec`

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 `t:Supervisor.child_spec/0` type.

# `decrement`

```elixir
@spec decrement(atom(), atom() | String.t() | non_neg_integer(), pos_integer()) ::
  ErrorMessage.t_ok_res()
```

# `increment`

```elixir
@spec increment(atom(), atom() | String.t() | non_neg_integer(), pos_integer()) ::
  ErrorMessage.t_ok_res()
```

# `native_term_storage?`

Values are integers in an `:counters` array. `Cache.TermEncoder.encode/2` already
passed integers through untouched, so this only removes a no-op function call.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
