Hyper.Img.Db.Gc.Sweep (Hyper v0.1.0)

Copy Markdown View Source

Pure accounting core for the layer GC.

A Hyper.Img.Db.Gc.Sweep.State accumulates a single full pass over the blobs table. absorb/3 classifies a keyset page into present (file on the medium) vs missing (file gone) and returns the missing blobs for the GC to prune; record_prune/4 folds the prune outcome back into the tally once the GC has executed it against the database.

Summary

Types

A blob page row: content-addressed id and its DB-recorded byte size.

Injected shared-medium probe. :present = file is there; :missing = file is genuinely gone (safe to prune); :unknown = could not determine (I/O error), so the blob is counted but never pruned.

Functions

Fold one keyset page into the sweep. Advances cursor to the last blob's id (so the next page starts after it), counts present / missing / unknown, and returns only the genuinely-missing blobs (file confirmed absent) as {id, size} for the GC to prune. :unknown rows (probe I/O error) are counted but never returned, so an unreachable medium can never drive a delete.

Full page (length == limit) means more rows may remain; page again.

A fresh sweep state: nil cursor (start of table), zeroed counters.

Fold the result of pruning one page back into the sweep: pruned rows deleted totalling pruned_bytes, and dangling rows left in place (file gone but the blob is still referenced by an image - a data-loss condition the GC reports but must not delete).

Types

blob()

@type blob() :: {String.t(), non_neg_integer()}

A blob page row: content-addressed id and its DB-recorded byte size.

check_fun()

@type check_fun() :: (String.t() -> presence())

presence()

@type presence() :: :present | :missing | :unknown

Injected shared-medium probe. :present = file is there; :missing = file is genuinely gone (safe to prune); :unknown = could not determine (I/O error), so the blob is counted but never pruned.

Functions

absorb(sweep, batch, check_fun)

@spec absorb(Hyper.Img.Db.Gc.Sweep.State.t(), [blob()], check_fun()) ::
  {Hyper.Img.Db.Gc.Sweep.State.t(), [blob()]}

Fold one keyset page into the sweep. Advances cursor to the last blob's id (so the next page starts after it), counts present / missing / unknown, and returns only the genuinely-missing blobs (file confirmed absent) as {id, size} for the GC to prune. :unknown rows (probe I/O error) are counted but never returned, so an unreachable medium can never drive a delete.

continue?(batch, limit)

@spec continue?([term()], pos_integer()) :: boolean()

Full page (length == limit) means more rows may remain; page again.

new()

@spec new() :: Hyper.Img.Db.Gc.Sweep.State.t()

A fresh sweep state: nil cursor (start of table), zeroed counters.

record_prune(sweep, pruned, pruned_bytes, dangling)

@spec record_prune(
  Hyper.Img.Db.Gc.Sweep.State.t(),
  non_neg_integer(),
  non_neg_integer(),
  non_neg_integer()
) :: Hyper.Img.Db.Gc.Sweep.State.t()

Fold the result of pruning one page back into the sweep: pruned rows deleted totalling pruned_bytes, and dangling rows left in place (file gone but the blob is still referenced by an image - a data-loss condition the GC reports but must not delete).