# `Hyper.Img.Db.Gc`
[🔗](https://github.com/harmont-dev/hyper/blob/main/lib/hyper/img/db/gc.ex#L1)

Cluster-singleton garbage collector that reconciles the `blobs` table against
the shared medium: a `:present` blob whose backing file is gone is a stale row
and is pruned. Runs continuously, one keyset page at a time.

Deletes are irreversible, so a blob is pruned only when all of these agree it is
really gone:

  1. **Errno discrimination** - `Hyper.Node.Layer.Repo.find_layer/1` reports
     `:enoent` only for a true absence; any other I/O error (NFS `ESTALE`/`EIO`,
     a vanished mount) is treated as `:unknown` and never pruned.
  2. **Mount re-check** - `test_system/0` is re-checked after a page's probes and
     before its deletes, so a mount that drops mid-page can't trigger a mass
     delete.
  3. **Grace period** - rows younger than `grace_period` are never deleted, so
     a blob mid-publish (row present, file not finished) is safe.

The `DELETE` is also guarded by `NOT EXISTS` against `image_layers`, so it can
never violate the FK. A missing-file blob still referenced by an image is a
*dangling reference* (data loss the GC can't fix) - left in place and reported,
never deleted.

**Publish contract:** write a layer's file to the medium before inserting its
`blobs` row (ideally write-temp then atomic rename); the grace period is
insurance, not a substitute.

The delete step itself lives in `Hyper.Img.Db.Gc.Prune`.

# `t`

```elixir
@type t() :: %Hyper.Img.Db.Gc{
  config: Hyper.Cfg.Gc.t(),
  last_sweep: Hyper.Img.Db.Gc.Sweep.State.t() | nil,
  role: :active | :standby,
  sweep: Hyper.Img.Db.Gc.Sweep.State.t() | nil
}
```

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `start_link`

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

# `status`

```elixir
@spec status() :: %{
  role: :active | :standby,
  last_sweep: Hyper.Img.Db.Gc.Sweep.State.t() | nil
}
```

Local role + last completed sweep, for introspection on this node.

---

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