Skip to content

feat(indexer): test SQLite + Postgres backend matrix and document selection - #452

Open
Topmatrixmor2014 wants to merge 3 commits into
ToluLabs:mainfrom
Topmatrixmor2014:fix/350-indexer-db-test-matrix
Open

feat(indexer): test SQLite + Postgres backend matrix and document selection#452
Topmatrixmor2014 wants to merge 3 commits into
ToluLabs:mainfrom
Topmatrixmor2014:fix/350-indexer-db-test-matrix

Conversation

@Topmatrixmor2014

@Topmatrixmor2014 Topmatrixmor2014 commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #350

The indexer (services/indexer) advertises two storage backends — SQLite via better-sqlite3 and Postgres via pg — selected by DB_DRIVER. But the DB layer was never exercised by CI, and (as this work surfaced) the whole package did not even compile or pass its own tests. Only one backend being run means the other silently rots, and the two use different SQL dialects (INSERT OR IGNORE vs ON CONFLICT, INTEGER vs BIGINT) exactly where they diverge.

This PR:

  1. Parameterizes the DB test suite over BOTH backends.
  2. Adds a CI job with a Postgres 16 service container so the matrix actually runs on every PR.
  3. Documents backend selection and tradeoffs.
  4. Fixes the real bugs the matrix surfaced (BIGINT-as-string rows, a corrupted fetchEvents, reorg detection that could never fire, and non-compiling tests).

What changed

New parameterized DB test matrix — services/indexer/src/db.test.ts

A single test suite registered for both sqlite and postgres, covering everything issue #350 calls out:

  • schema migrations are idempotent
  • ledger cursor updates (getLastLedger/setLastLedger round-trip, default 0)
  • claim upserts (insert + update, threshold present/null, revoked reset on re-verify)
  • revokes (revoked = 1, and cleared again on re-verification)
  • claimsByWallet, stats (total/active/revoked), paginated recent
  • deleteClaimsAfter rollback and getMaxClaimLedger

The Postgres leg reads TEST_POSTGRES_URL (falling back to DATABASE_URL): it runs in CI and locally when a Postgres is reachable, and skips gracefully when it isn't (so local npm test never fails for lack of a DB).

CI — .github/workflows/ci.yml

Adds an indexer job with a postgres:16 service container (POSTGRES_USER/PASSWORD/DB=indexer) that:

  • installs deps (npm ci) and builds (npm run build),
  • runs the full indexer suite with TEST_POSTGRES_URL pointing at the container — so db.test.ts runs both the SQLite and Postgres legs on every PR. This is the first time the indexer has ever had CI coverage.

Documentation

  • services/indexer/README.md — new "Database Backend Selection & Tradeoffs" section: how a deploy selects the backend (DB_DRIVER), what DATABASE_URL/SQLITE_PATH control, when to pick each engine (dev/single-instance vs production multi-instance), concurrency/writing constraints, operational tooling, and how to run the DB matrix locally.
  • services/indexer/.env.example — documents DB_DRIVER, SQLITE_PATH, DATABASE_URL, and TEST_POSTGRES_URL.

Real bugs fixed along the way (surfaced by the matrix)

  • db.tsnode-postgres returns BIGINT/INTEGER columns as strings, while SQLite returns numbers, so claimsByWallet/stats/recent exposed different row shapes per backend. Registered pg numeric type parsers (types.setTypeParser) so Postgres matches SQLite exactly.
  • ingester.ts — repaired a corrupted fetchEvents (a bad merge left redeclared page/records, an undefined lastLedger, and a duplicate implementation) and moved reorg detection ahead of the finality-ceiling early return so a reorged cursor (paused > head) is actually reconciled instead of silently treated as "ahead".
  • ingester.tsdecodeScVal now falls back to the literal string when input isn't valid base64 XDR, so plain-string event topics ("proof", "verified") parse correctly instead of collapsing to null.
  • api.test.ts / ingester.test.ts — fixed pre-existing mismatches: the Ingester stub was missing the reconcile member, and test config factories were missing the newer Config fields (corsOrigins, rateLimit*), which is why the package didn't compile. The reconcile test was also corrected to match the documented "deletes and re-indexes" behavior.

Validation

  • services/indexer:
    • tsc --noEmit — clean
    • npm run build — succeeds
    • npm test (SQLite leg) — 55 passed / 1 skipped (Postgres skipped without a URL)
    • npm test with a real Postgres 16 (docker run -p 5432:5432 postgres:16) — 66 passed (SQLite + Postgres legs both green)
  • .github/workflows/ci.yml — parsed and validated (jobs: commitlint, contracts, circuits, frontend, indexer, sdk-integration, a11y)

Notes

  • The Postgres numeric-type normalization and the fetchEvents/reorg fixes are changes to production behavior, but they only make the two backends behave identically and restore the finality/reorg design intent — they were unreachable/broken before.
  • The ingester test suite was previously never run (the module didn't compile), so the corrections in ingester.test.ts reflect the intended, documented behavior rather than weakening any previously-passing assertions.

closes #350

)

The indexer supports both SQLite and Postgres via DB_DRIVER, but the
DB layer was never exercised on any backend by CI — the whole package
had no workflow job and, as it turns out, did not even compile. Add a
parameterized DB test matrix that runs the SAME suite (migrations,
cursor updates, upserts, revokes, claimsByWallet, stats, recent,
deleteClaimsAfter, getMaxClaimLedger) against SQLite and a real
Postgres, plus CI wiring and backend-selection documentation.

Fixes surfaced by the matrix:
- db.ts: pg returns BIGINT/INTEGER as strings; register numeric type
  parsers so Postgres rows match SQLite's number shapes.
- ingester.ts: repair a corrupted fetchEvents (redeclared vars,
  undefined lastLedger) from a bad merge, and move reorg detection
  ahead of the finality-ceiling early return so a reorged cursor is
  actually reconciled.
- ingester.ts: decodeScVal now falls back to the literal string for
  non-XDR values so plain-string event topics parse correctly.
- api.test/ingester.test: fix pre-existing Config/stub mismatches
  (missing reconcile in the Ingester stub, newer Config fields).

CI: add an `indexer` job (SQLite + Postgres service container) that
builds and runs the full indexer suite. Docs: add a "Database Backend
Selection & Tradeoffs" section covering DB_DRIVER, when to pick each
engine, and how to exercise both backends in the test matrix.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@drips-wave

drips-wave Bot commented Aug 30, 2026

Copy link
Copy Markdown

@Topmatrixmor2014 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Psalmuel01

Copy link
Copy Markdown
Collaborator

Good work on the dual-backend test matrix. It now conflicts with main though: #448 (keyset cursor pagination) just merged and also edits services/indexer/src/db.ts, ingester.ts, and api.ts. Please rebase onto latest main. The pagination change altered the db query shape, so make sure the Postgres and SQLite matrix tests cover the new keyset queries after the rebase.

Topmatrixmor2014 and others added 2 commits August 31, 2026 05:58
PR ToluLabs#448 switched recent() to keyset (cursor) pagination returning a
RecentPage, but db.test.ts still called the old offset-based API, so the
SQLite + Postgres matrix failed to build/run. Update the pagination test
to the new { claims, nextCursor } shape and add a fourth claim so a
second page genuinely exists — exercising the keyset cursor and proving
revoked claims stay excluded across pages.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
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.

Indexer supports SQLite and Postgres — add a test matrix and document backend selection

2 participants