feat(indexer/common): create core ledger cursor table#41
Conversation
…col#24) - Add ledger_cursor SQL migration (0001_create_ledger_cursor.sql) - BIGSERIAL primary key, UNIQUE(source, domain) constraint - CHECK constraints: non-empty source/domain, seq >= 0 - Index on last_ledger_seq for monitoring queries - schema_migrations bookkeeping table for idempotent runs - Add LedgerCursor type and SqlClient type alias (ledger-cursor.ts) - findCursor(sql, source, domain): returns cursor or null - upsertCursor(sql, source, domain, lastLedgerSeq): insert-or-advance with stale-write guard (only advances if new seq > existing) - Add migration runner (migrate.ts) - Reads *.sql files from migrations/ sorted lexicographically - Executes each file idempotently via sql.unsafe() - INDEXER_DATABASE_URL env var; db:migrate npm script - Add @types/node devDependency and node types in tsconfig - Export all public API from index.ts - 16 tests covering findCursor, upsertCursor (in-memory mock) Closes Fundable-Protocol#24
|
@playmaker410 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! 🚀 |
📝 WalkthroughWalkthroughAdds ledger cursor storage, migration execution wiring, and a cursor repository with lookup, upsert, tests, and exports. ChangesLedger cursor persistence and migration setup
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@indexer/common/src/db/migrate.ts`:
- Around line 45-48: The exported runMigrations helper currently looks for the
migrations directory next to the compiled module, so the SQL files must be
available in the built package. Update the build/package flow for the db
migration path so the src/db/migrations/*.sql assets are copied or otherwise
shipped alongside the emitted JS used by runMigrations, or revert the API to
source-only if that packaging is not intended. Use runMigrations,
listMigrationFiles, and the migrationsDir resolution logic as the main places to
adjust.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 736d7011-eca3-4f7d-81ba-47772b98fe5e
⛔ Files ignored due to path filters (1)
indexer/bun.lockis excluded by!**/*.lock
📒 Files selected for processing (8)
indexer/common/package.jsonindexer/common/src/cursor/ledger-cursor.test.tsindexer/common/src/cursor/ledger-cursor.tsindexer/common/src/db/migrate.tsindexer/common/src/db/migrations/0001_create_ledger_cursor.sqlindexer/common/src/index.tsindexer/common/tsconfig.jsonpackage.json
|
Thank you for your awesome contribution, however after analyzing your implementation, there is a minor fixes to be done, which is related to the migration files. Kindly fix it to merge your PR asap. Also do not forget to use fundable.finance to offramp. |
|
Checking In |
|
I just saw your comment |
…rce and dist The original resolveMigrationsDir() used join(fileURLToPath(import.meta.url), '..', 'migrations') which only works when the module is loaded from src/db/migrate.ts. When loaded from dist/db/migrate.js the relative path points to dist/db/migrations/ which does not exist because tsc does not copy .sql assets. Replace the naive sibling-relative resolution with a package-root walk: starting from the current file's directory, walk upward until a directory containing package.json is found, then return <root>/src/db/migrations. This ensures the SQL assets — which always live in src/ — are found regardless of whether the runner is executed from source or a compiled build. Also fix import order (dirname, join) and collapse error string concatenation into a single template literal to satisfy biome lint rules. All checks pass: indexer:type-check, indexer:lint, indexer:test (16 tests).
Summary
Implements issue #24 — durable progress tracking for the Soroban event poller.
This PR adds the
ledger_cursortable, a migration runner, and a cursor repository so the poller can resume from the last processed ledger after restarts or failures.Changes
Migration —
0001_create_ledger_cursor.sqlschema_migrationsbookkeeping table (idempotent, guarded run)ledger_cursortable withBIGSERIALsurrogate PK,UNIQUE(source, domain),CHECKconstraints (non-empty source/domain,last_ledger_seq >= 0), and a partial index onlast_ledger_seqfor monitoring and backfill queriesCursor repository —
ledger-cursor.tsLedgerCursorinterface:id,source,domain,last_ledger_seq,created_at,updated_atSqlClienttype alias: minimal tagged-template signature (no directpostgresimport needed in callers or tests)findCursor(sql, source, domain)— SELECT by(source, domain), returnsLedgerCursor | nullupsertCursor(sql, source, domain, lastLedgerSeq)— INSERT … ON CONFLICT DO UPDATE with stale-write guard; falls back tofindCursorwhen the WHERE clause suppresses the updateMigration runner —
migrate.ts*.sqlfiles fromsrc/db/migrations/lexicographicallysql.unsafe()(idempotency enforced inside SQL)resolveMigrationsDir()walks fromimport.meta.urlup to the package root (package.json), then returns<root>/src/db/migrations— works correctly from both source (bun run src/db/migrate.ts) and a compiled dist build (SQL assets always live undersrc/and are not copied todist/)INDEXER_DATABASE_URLenv var;db:migratescript in package.jsonindexer:db:migrateconvenience script addedPackage config
@types/nodedevDependency added toindexer/commontsconfig.jsonupdated withtypes: ["node", "vitest/globals"]index.tsTests — 16 tests covering
findCursorandupsertCursorusing an in-memorySqlClientmock (no database required): null for missing cursor, insert on first call, advances on higher seq, ignores stale/same seq (idempotent), separate cursors per source and domain, input validation (empty strings, negative seq), and timestamps on returned cursor.Area
src/)indexer/common/)indexer/streams/)indexer/distributions/)Scope
Verification
bun run type-checkbun run testbun run lintbun run indexer:type-checkif indexer files changedbun run indexer:testif indexer files changedbun run indexer:lintif indexer files changedIndexer Safety
Notes
Closes #24
Summary by CodeRabbit