Skip to content

feat(img-db): runtime-selectable SQLite backend for the image graph (single-node)#17

Draft
markovejnovic wants to merge 12 commits into
mainfrom
feat/sqlite-image-graph-backend
Draft

feat(img-db): runtime-selectable SQLite backend for the image graph (single-node)#17
markovejnovic wants to merge 12 commits into
mainfrom
feat/sqlite-image-graph-backend

Conversation

@markovejnovic

@markovejnovic markovejnovic commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

No description provided.

- Add Hyper.Img.Db.Repo.Postgres (concrete, priv: priv/repo keeps existing migrations)
- Add Hyper.Img.Db.Repo.Sqlite (concrete, same priv path)
- Add Hyper.Img.Db.Backend (config-driven selector; selected/0, repo/0, sqlite?/0)
- Rewrite Hyper.Img.Db.Repo as a thin runtime facade forwarding Ecto callbacks to Backend.repo()
- Add with_low_priority/2 to facade (postgres: statement_timeout tx; sqlite: passthrough)
- Rename config block from Repo to Repo.Postgres; add priv: "priv/repo" to both repo configs
- Point ecto_repos at Hyper.Img.Db.Repo.Postgres; start Backend.repo() in application tree
Adds SingleNodeGuard GenServer that refuses to boot when peers are already
connected and halts the node if a peer joins while SQLite is active. Started
conditionally in the application tree only when Backend.sqlite?/0 is true.
Adds a real-SQLite integration test that starts Repo.Sqlite against a
temp file, runs the shared migration, and exercises the three constructs
that could behave differently on SQLite vs Postgres:
- Lease.bump/3 ON CONFLICT upsert (via new bump_with_repo/4 that takes
  the repo explicitly, used by tests; bump/3 delegates to it)
- GC prune query: parent_as correlated subquery in delete_all with
  RETURNING - confirmed {count, sizes} populates correctly on SQLite
- Image.resolve_chain/1 ordered join (refactored to return a query
  instead of executing it, so callers choose the repo)

ecto_sqlite3 note: on conflict update, the returned struct carries a
freshly-generated UUID rather than the existing row's id (ecto_sqlite3
does not RETURNING the stored row after a conflict update). The test
asserts what actually matters: one row persists and the stored expiry
was advanced, verified by Repo.aggregate + Repo.one!.
…tidy pipe

- Fix 1: Clarify lease upsert test assertions with precise comments: returned struct
  check notes it's from changeset (not DB re-read); count==1 assertion proves
  upsert didn't insert; stored-row re-read proves persistence.
- Fix 2: Add log: false to Ecto.Migrator.run to suppress "redefining module" noise.
- Fix 3: Replace |> then(&Repo.all/1) with idiomatic |> Repo.all() in chain_sizes/1.
Adds an Image-graph storage backends section to the architecture doc
covering both backends, the config snippet to enable SQLite, the
SingleNodeGuard boot-refusal and runtime-halt enforcement, per-backend
migration commands, and the known upsert-id limitation under SQLite.
Updates the README Features bullet to mention the SQLite option for
single-node deployments.
@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 32 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
lib/hyper/single_node_guard.ex 0.00% 11 Missing ⚠️
lib/hyper/img/db/gc.ex 0.00% 10 Missing ⚠️
lib/hyper/img/db/repo.ex 0.00% 6 Missing ⚠️
lib/hyper/img/db/config.ex 0.00% 3 Missing ⚠️
lib/hyper/application.ex 0.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

…ode guard

Simplify the SQLite backend per review feedback:

- Collapse Repo.Postgres/Repo.Sqlite/Backend into one Hyper.Img.Db.Repo
  whose adapter is chosen by a single Hyper.Img.Db.Config module
  (config :hyper, Hyper.Img.Db, backend: :postgres | :sqlite). Restores the
  default priv/telemetry_prefix, so ecto_repos/CI/migrations use the plain
  repo name again.
- Move the single-node guard out of the DB namespace into a generic,
  DB-agnostic Hyper.SingleNodeGuard; application.ex decides whether to attach
  it based on Hyper.Img.Db.Config.sqlite?/0.
- Revert test-only scaffolding: Image.resolve_chain/1 executes again and
  Lease.bump/3 is a single function (drop bump_with_repo/4).
- Delete the backend/guard/portability tests.
- Move storage-backend docs from architecture.md into the intro cookbook page.
@markovejnovic markovejnovic marked this pull request as draft June 24, 2026 00:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant