# `BeamAgent.Store`
[🔗](https://github.com/beardedeagle/beam-agent/blob/main/lib/beam_agent/store.ex#L1)

Store adapter boundary for canonical BeamAgent domains.

This module wraps the Erlang `:beam_agent_store` boundary, providing
idiomatic Elixir access to domain-level persistence configuration.
Domains default to the ETS adapter (`:beam_agent_store_ets`); callers
only need this module when switching a domain to a durable adapter
such as `:beam_agent_store_dets`.

## Quick start

```elixir
# Switch a domain to DETS-backed durable storage
:ok = BeamAgent.Store.configure_domain(:my_domain, %{
  adapter: :beam_agent_store_dets,
  options: %{data_dir: "/var/data", ram_file: false}
})

# With atomic counters for concurrent-safe increments
:ok = BeamAgent.Store.configure_domain(:my_domain, %{
  adapter: :beam_agent_store_dets,
  options: %{data_dir: "/var/data", atomic_counters: true}
})

# Query current config
%{adapter: :beam_agent_store_dets} = BeamAgent.Store.domain_config(:my_domain)

# Reset back to default ETS adapter
:ok = BeamAgent.Store.reset_domain(:my_domain)
```

## Available adapters

| Adapter | Description |
|---------|-------------|
| `:beam_agent_store_ets` | Default. In-memory ETS with hardened-mode write proxying. |
| `:beam_agent_store_dets` | Durable disk-backed DETS. Supports `atomic_counters` option. |

## DETS store options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `data_dir` | binary or string | `"beam_agent_data"` | Directory for `.dets` files |
| `auto_save` | non_neg_integer or `:infinity` | `30000` | Auto-save interval in ms |
| `ram_file` | boolean | `false` | Keep file contents in RAM (useful for tests) |
| `atomic_counters` | boolean | `false` | Use `atomics` CAS loops for lock-free counter increments |

# `adapter_module`

```elixir
@type adapter_module() :: module()
```

A store adapter module implementing the `beam_agent_store` behaviour.

# `domain`

```elixir
@type domain() :: atom()
```

A canonical domain atom (e.g., `:runs`, `:artifacts`, `:journal`).

# `store_config`

```elixir
@type store_config() :: %{
  :adapter =&gt; adapter_module(),
  optional(:options) =&gt; store_options()
}
```

Store configuration map.

Must contain `:adapter` (a module implementing the store behaviour).
May contain `:options` (adapter-specific settings).

# `store_key`

```elixir
@type store_key() :: atom() | binary() | tuple() | pid() | integer()
```

Store key type.

Covers the common BEAM key types used across BeamAgent domains.

# `store_options`

```elixir
@type store_options() :: map()
```

Adapter-specific options passed through to the backing store.

# `table_name`

```elixir
@type table_name() :: atom()
```

ETS/DETS table name.

# `update_op`

```elixir
@type update_op() ::
  integer()
  | {pos_integer(), integer()}
  | {pos_integer(), integer(), integer(), integer()}
```

Counter update operation.

- Integer: increment element at position 2 by the given amount.
- `{pos, incr}`: increment element at `pos` by `incr`.
- `{pos, incr, threshold, set_value}`: bounded increment.

# `adapter_module`

```elixir
@spec adapter_module(domain()) :: adapter_module()
```

Return the adapter module backing a domain.

## Example

    :beam_agent_store_ets = BeamAgent.Store.adapter_module(:runs)

# `clear`

```elixir
@spec clear() :: :ok
```

Clear all domain store configuration and revert every domain to defaults.

# `close_table`

```elixir
@spec close_table(table_name()) :: :ok
```

Close a DETS table file.

Flushes any in-memory atomic counter values to disk before closing.
Safe to call on tables that are not open. No-op for ETS-backed domains.

## Example

    :ok = BeamAgent.Store.close_table(:my_dets_table)

# `configure_domain`

```elixir
@spec configure_domain(domain(), store_config()) ::
  :ok | {:error, :invalid_options | {:invalid_adapter, atom()}}
```

Configure a persistence adapter for a canonical domain.

Domains default to `:beam_agent_store_ets`. Use this to switch a
domain to `:beam_agent_store_dets` or any custom adapter implementing
the `beam_agent_store` behaviour.

Returns `:ok` on success, or `{:error, reason}` for invalid config.

## Example

    :ok = BeamAgent.Store.configure_domain(:my_domain, %{
      adapter: :beam_agent_store_dets,
      options: %{data_dir: "/var/data"}
    })

# `data_dir`

```elixir
@spec data_dir(store_options()) :: String.t()
```

Resolve the data directory from DETS store options.

Returns the directory path where `.dets` files are stored as a binary
string. Defaults to `"beam_agent_data"` when no `data_dir` is configured.

The underlying Erlang function returns a charlist (`file:filename()`);
this wrapper converts it to a binary for idiomatic Elixir usage.

## Examples

    "beam_agent_data" = BeamAgent.Store.data_dir(%{})
    "/var/data" = BeamAgent.Store.data_dir(%{data_dir: "/var/data"})

# `domain_config`

```elixir
@spec domain_config(domain()) :: store_config()
```

Return the normalized store config for a domain, including defaults.

## Example

    %{adapter: :beam_agent_store_ets, options: %{}} =
      BeamAgent.Store.domain_config(:default_domain)

# `ensure_tables`

```elixir
@spec ensure_tables() :: :ok
```

Ensure the store configuration table exists. Idempotent.

# `flush_counters`

```elixir
@spec flush_counters(table_name()) :: :ok
```

Flush in-memory atomic counter values back to their DETS records.

No-op when `atomic_counters` is not enabled or no counters have been
incremented for `table`. After flushing, tracking entries are removed
so the next `update_counter` re-seeds from DETS.

Called automatically by `close_table/1` and `sync_table/1`.

## Example

    :ok = BeamAgent.Store.flush_counters(:my_dets_table)

# `reset_domain`

```elixir
@spec reset_domain(domain()) :: :ok
```

Remove any custom store config for a domain and restore defaults.

## Example

    :ok = BeamAgent.Store.reset_domain(:my_domain)

# `sync_table`

```elixir
@spec sync_table(table_name()) :: :ok
```

Flush pending writes for a DETS table to disk.

Also flushes any in-memory atomic counter values before syncing.

## Example

    :ok = BeamAgent.Store.sync_table(:my_dets_table)

---

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