# `Tama.Dex.Index`
[🔗](https://github.com/kritama/tama-dex/blob/v0.1.0/lib/tama/dex/index.ex#L1)

The SQLite index of operation documents.

A host starts the index under its own supervisor, with an explicit
database location, and passes it (a pid or registered name) into every
`Tama.Dex` call. tama-dex has no application callback, so nothing is
opened until a host starts an index.

    children = [
      {Tama.Dex.Index, path: "/var/lib/my_host/tama-dex.sqlite3", name: MyHost.Index}
    ]

One index holds every cassette the host installs. Each cassette is a
namespace inside it.

## Options

- `:path` (required): a database file path, or `:memory`. A new file is
  created with mode 0600 and opened in WAL mode. Its directory must exist.
  `:memory` keeps the index in memory for the life of the process, which
  suits tests and short-lived hosts.
- `:name` (optional): a name to register the index under.
- `:limits` (optional): lower limits for documents loaded into this index,
  as a keyword list of `:max_document_bytes`, `:max_operations` and
  `:max_schema_bytes`. They can't exceed the profile's limits.

## Processes

The index process holds state and coordinates two linked workers, each
owning one connection. The reader serves queries and the writer runs loads
and imports one at a time. In a file-backed index, WAL mode lets reads see
the last committed state while a write runs. An in-memory index shares one
`memdb` database between the two connections, so a read issued during a
write may wait for the commit, but never sees a mixture.

The database is derived data. One whose storage schema version this code
doesn't know is recreated empty, emitting `[:tama_dex, :index, :recreated]`,
and the host loads its documents again.

## Readiness

`status/1` reports whether the index can serve. Search must never block or
fail a host's startup, so while nothing is committed and a load or import
runs, `Tama.Dex.search/3` and `Tama.Dex.describe/4` return
`{:error, :not_ready}` and `Tama.Dex.execute/5` a `:not_ready` error,
without waiting. See `status/1`.

# `progress`

```elixir
@type progress() :: %{
  cassette: String.t(),
  done: non_neg_integer(),
  total: non_neg_integer()
}
```

# `status`

```elixir
@type status() :: :ready | {:importing, progress()} | {:failed, atom()} | :empty
```

# `t`

```elixir
@type t() :: GenServer.server()
```

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Starts an index. See the module documentation for the options.

Raises `ArgumentError`, creating nothing, when `:path` is missing or an
option is invalid.

# `status`

```elixir
@spec status(t()) :: status()
```

The index's readiness as a whole:

- `:ready` whenever at least one cassette is committed, even while other
  loads or imports run or after one has failed;
- otherwise `{:importing, %{cassette, done, total}}` while a load or
  import runs or waits, counting sources;
- otherwise `{:failed, reason}` when the last attempt failed, with the
  kind of its error, such as `:deadline`;
- otherwise `:empty`.

`:ready` means at least one cassette is committed, not all of them. Each
cassette's own status is in `Tama.Dex.cassettes/1`. Every change emits
`[:tama_dex, :index, :status]` with the new `status` and the `index` pid
in its metadata.

Reads the index's state only, so it never waits on SQLite.

---

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