fangorn/ex_git_objectstore
public
ref:2b54c14bba04f24906f8e2d3318424d8e050cbb8
feat: top-level Graph-aware API (ahead_behind, commits_between, rebuild_graph) (#17)
PR 3 of 4 for fangorn/ex_git_objectstore#26. This is the entry point Anvil will call. Each query tries the persisted commit-graph first and falls back to the existing \`cat_object\` walker when the graph isn't built yet or doesn't cover one of the query SHAs. No behavior change for existing callers of \`ancestor?/3\` — only its speed.
**Stacked on #16** — rebase-clean when that merges.
## New modules
- **\`ExGitObjectstore.Graph.Cache\`** — \`:persistent_term\`-backed in-process cache keyed by \`{storage_module, repo_prefix}\`. Reads are lock-free and zero-copy. \`put/2\` triggers a global GC scan so it's only called on rebuild or first lazy-load.
- **\`ExGitObjectstore.Graph.Fallback\`** — reference walker implementations of \`ahead_behind\`, \`commits_between\`, \`ancestor?\`. Bounded by \`:max_walk\` (default 10k) so a pathological history returns \`{:error, :walk_limit_exceeded}\` instead of running forever.
## New public API
\`\`\`elixir
ahead_behind(repo, base, head) :: {:ok, %{ahead, behind}} | {:error, _}
commits_between(repo, base, head) :: {:ok, [sha]} | {:error, _}
ancestor?(repo, anc, desc) :: {:ok, bool} | {:error, _} # existing, now graph-aware
rebuild_graph(repo) :: :ok | {:error, _}
\`\`\`
All three queries follow the same routing: load-or-fetch-cached graph, verify both SHAs are members, answer from graph. On any failure (missing graph, missing member, corrupt blob), fall back to \`Graph.Fallback\`. Callers see one stable contract.
\`rebuild_graph\` builds from refs, persists to storage, and seeds the cache.
## Tests
23 new tests on top of PR #16:
- 6 Cache (hit/miss, overwrite, delete, per-repo keying)
- 10 Fallback (edge cases, ahead_behind, commits_between ordering, ancestor? semantics, walk-limit ceiling, missing-commit error)
- 7 integration tests exercising all routing paths:
- no graph built — fallback
- after \`rebuild_graph\` — graph path answers + cache seeded
- stale graph (commit pushed after build) — falls back because new SHA isn't in graph
Full suite: **704 tests, 0 failures** (was 681 on #16). Credo: unchanged from main.
## What's next
Anvil PR: swap \`lib/anvil/git/objectstore.ex\` \`ahead_behind\` / \`commits_between\` to call \`ExGitObjectstore\` directly. Add a \`mix anvil.graphs.rebuild\` task so operators can seed graphs in dev / staging / prod (no push-hook wiring yet — tracked separately).
## Deployment notes
- First query after deploy with no graph: walker path, same perf as today.
- After one \`mix anvil.graphs.rebuild\` per repo: graph path on every subsequent query (until a push introduces commits not in the graph, at which point that specific query falls back until the next rebuild).
- No incremental update in this PR — graph staleness after push is handled by the fallback, not by invalidation.
SHA:
2b54c14bba04f24906f8e2d3318424d8e050cbb8
Author:
Anvil <noreply@anvil.fangorn.io>
Date:
2026-04-18 18:31
Parents:
37bb565
8 files changed
+841
-42
| Type | ||
|---|---|---|
|
|
lib/ex_git_objectstore.ex | +138 −6 |
|
||