Skip to content

JITSU-227: Add Node/PostgreSQL Reverse ETL persistence - #1516

Open
absorbb wants to merge 12 commits into
newjitsufrom
feat/jitsu-227-persistence
Open

absorbb wants to merge 12 commits into
newjitsufrom
feat/jitsu-227-persistence

Conversation

@absorbb

@absorbb absorbb commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Scope

Third implementation slice of JITSU-227, on merged #1509. #1512 was closed by the owner and is deliberately excluded.

Adds server-only Node/PostgreSQL delivery persistence under services/retl-runner; no executable runner, CronJob, provider adapter or live delivery is enabled.

  • Eight canonical reverse_sync_* models in the existing console Prisma schema; no separate retl namespace or standalone migration. Compact encrypted cursor/store/generation state reuses source_state.
  • Ordinary prisma db push manages all tables, enums, keys and indexes. No supplemental CHECK, custom schema-update wrapper, Docker/Helm changes, or extra deployment bundle.
  • Runtime pg transactions use the same configured schema with a transaction-local search path and narrowly scoped runtime-role grants.
  • Database-clock lease/epoch fencing for workspace, sync, task, logical run, target and revision, plus persistent exclusive mirror audience ownership.
  • Encrypted prepared manifests, provider state and recoverable identities; bounded recovery reads and explicit core reconciliation acknowledgements. No blind replay.
  • Acceptance atomically persists receipts, effective membership and reservation/accounting updates. Partial accepted-then-failed outcomes remain recoverable. Per identity, the latest accepted source sequence wins even when staged work resolves later; indexed projected identity hashes on operations preserve ordering for both upserts and removals without blocking multi-batch staging or adding tombstones/tables.
  • Exact batch-receipt retries are read-only whether terminal, staged or mixed, and cannot roll back the run store. Replacements must advance a staged outcome, preserve terminal outcome details and keep original job IDs/checkpoint while any staged outcomes remain. Final terminal metadata may change; recovered transitions require core reconciliation.
  • Pending finish receipts are immutable until accepted. Matching retries preserve the store; changed job IDs/checkpoints are rejected through both facade and core recovery.
  • Interrupted initialization has a core-only reconciled retry path: verify absence or safe cleanup, atomically reset to new with reconciled store and cleared provider state, then release/reacquire the same run before invoking the ordinary lifecycle. Recovery epochs cannot blindly acknowledge/restart initialization; unknown sessions remain blocked.
  • Existing DeliveryJournal facade with no database/keyring/snapshot access. Contiguous checkpoint validation, safe abort boundaries and restartable staged-finish resolution.
  • Snapshot creation is retryable for the current unsealed candidate. Core status reports extraction progress; sequential page appends use an atomic latest-page sequence/content hash on the existing generation table. Exact retries are read-only; changed pages, sequence gaps and duplicate keys in new pages fail closed. No association or page-receipt table.
  • Core-owned desired generations, duplicate source-key validation, shared-identity conflict checks, keyset diff reads, sealed-source removal guards and atomic promotion with final state.
  • Full-snapshot diffs compare deduplicated desired identities with acknowledged membership; no source-to-identity association table is needed. Shared identities remain present until their last source row disappears.
  • Conservative pre-submission storage reservations and bounded, fenced retention.

Billing explicitly excluded

Per owner request, this PR contains no billing-period inputs/validation, activation ledger, usage events/outbox/publisher, billing-service integration, entitlement enforcement, quota reservations, pricing or invoicing. All billing work is deferred to a separate slice.

Receipts remain for delivery recovery, not billing. accepted_at is the database-clock time at which Jitsu acknowledged the verified outcome, not a remote delivery timestamp or billing-period attribution. Recovery no longer requires a remote timestamp or billing proof; only core reconciliation methods can resolve any unresolved recovered operation, whether accepted, rejected or staged. Accepted recovered finish also requires reconciliation even when no rows were staged. Interrupted finish resolution reuses its saved acknowledgement time.

Other boundaries

The caller must already hold the Kubernetes sync lease. Executable runner/syncctl wiring, independent task/log/status observation, lease-renewal loops and cancellation follow in subsequent slices. DB fencing does not promise provider-side fencing or exactly-once remote delivery.

Core projection supplies provider-ready identity/upsert/removal values; no vendor normalization rule is invented here. Mirror planner orchestration, million-member performance validation and the first verified Google adapter remain next. The existing upsert lifecycle still refuses mirror execution. Snapshot page sequences are not warehouse cursors: callers must reproduce an ambiguous page from a stable/replayable source and await acknowledgement before advancing; extraction resumption remains part of runner orchestration.

Controlled config/target/mode changes fail closed until the reset/transfer workflow exists. Retention is explicit and lease-fenced; no background sweep is started by this package.

Validation

  • 80 tests against isolated real PostgreSQL 18 provisioned with the canonical Prisma schema and a restricted runtime role.
  • Covers concurrent acquisition, stale owners, renewal expiry, connection recreation, partial acceptance, atomic batch-receipt rollback, duplicate acknowledgements, cursor barriers, pending/interrupted finish resolution, full refresh, snapshot conflicts/removal guards, retention, encryption/key rotation and maximum protocol envelope sizes.
  • Added takeover regressions for prepared/unknown/staged batches with all outcome types; empty/already-accepted finish in prepared/pending states; unchanged state/reservations after blocked acknowledgements; and safe local resumption of already-accepted finish chunks. Ten bypass cases failed before the fixes and pass now.
  • Terminal-retry regressions cover accepted/rejected results before/after takeover, changed or omitted metadata, reordered outcomes, receipt/counter/timestamp preservation, older retries after a later batch, and staged-to-terminal reconciliation.
  • Four pending-finish regressions cover normal/recovered sessions with/without staged rows, conflicting metadata, read-only duplicates and accepted completion. All four failed before the fix and pass now.
  • Seven snapshot regressions cover repeated creation before/after takeover, ambiguous append commits, exact and conflicting retries, duplicate keys, page gaps, accounting/ciphertext preservation, transaction rollback, concurrent retries, sealed generations and stale-owner/abandoned-generation guards.
  • Fourteen additional tests cover all delayed upsert/remove combinations through finish and takeover reconciliation, reverse-ordered outcomes and multi-identity projections, rejected successors, and unchanged/progressing mixed/staged receipts with metadata/store protection.
  • Four initialization-recovery tests cover absent/created sessions, blocked facade acknowledgement and premature runner calls, read-only reset retries, lost reset response/connection recreation, stale ownership, atomic rollback, lifecycle bounds and normal runner completion after re-admission.
  • Tests explicitly cover recovery without billing inputs/provider timestamps, guarded recovered acceptance, unchanged acknowledgement time across finish chunks, absence of billing/association tables, shared-identity departures across generations, snapshot accounting/cleanup, repeated schema updates and configured-schema isolation.
  • 130 console unit tests pass for this revision; the 81 warehouse-model integration tests passed at the preceding billing-removal checkpoint.
  • Runner/console typechecks, Prisma Client/Zod generation, changed-file lint/formatting and diff checks pass. Deployment scripts and console integration setup match their pre-wrapper versions.
  • Existing 139 destination-suite tests and protocols/destination-functions typechecks passed in the original persistence checkpoint; those sources are unchanged. Credential-dependent provider tests remain disabled.
  • Incremental large/risky local review: independent runtime and schema/test finders plus fresh final review before push.
  • Schema updates executed only inside disposable test containers. No production migration, deployment or advertising writes.

Next: core snapshot-mirror orchestration and recovery/performance tests, followed by runnable Node service and syncctl/CronJobs. Billing will be a separate implementation slice.

jitsu-code-review[bot]
jitsu-code-review Bot previously approved these changes Sep 14, 2026

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the PostgreSQL persistence migration, ownership fencing, encrypted journal/recovery paths, snapshot transitions, retention, and protocol integration. No actionable correctness, security, or user-visible regression found in this change set.

jitsu-code-review[bot]
jitsu-code-review Bot previously approved these changes Sep 14, 2026

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the Reverse ETL persistence lifecycle, schema-update wrapper, Docker/Helm invocation paths, and the added integration coverage. No actionable correctness, security, or user-visible regression was identified.

jitsu-code-review[bot]
jitsu-code-review Bot previously approved these changes Sep 14, 2026

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the PostgreSQL persistence implementation, Prisma schema integration, ownership fencing, encrypted recovery journal, snapshot lifecycle, and retention logic.

No actionable correctness, security, or user-visible regression findings.

jitsu-code-review[bot]
jitsu-code-review Bot previously approved these changes Sep 14, 2026

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new PostgreSQL persistence lifecycle, schema ownership, encryption binding, and recovery paths.

Findings:

  • Recovered batch acknowledgements can still persist non-accepted outcomes through the provider-facing method without reconciliation.
  • A recovered finish with no staged operations can be recorded as accepted without reconciliation.

Comment thread services/retl-runner/src/persistence/journal.ts
Comment thread services/retl-runner/src/persistence/journal.ts
jitsu-code-review[bot]
jitsu-code-review Bot previously approved these changes Sep 14, 2026

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the PostgreSQL persistence, recovery fences, journal accounting, snapshot handling, and Prisma schema changes.\n\nFinding: acknowledged batch receipt metadata is mutable on retry.

Comment thread services/retl-runner/src/persistence/journal.ts
jitsu-code-review[bot]
jitsu-code-review Bot previously approved these changes Sep 14, 2026

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the PostgreSQL persistence layer, fencing/recovery flow, encrypted journal handling, and Prisma schema changes.\n\nFinding:\n- A repeated pending finish acknowledgement can overwrite the previously persisted remote job IDs.

Comment thread services/retl-runner/src/persistence/journal.ts
jitsu-code-review[bot]
jitsu-code-review Bot previously approved these changes Sep 14, 2026

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the fenced ownership, encrypted delivery journal, lifecycle transitions, retention, and mirror snapshot persistence. I found two mirror recovery paths that can strand a run after a transaction commits but the worker loses its response.

Comment thread services/retl-runner/src/persistence/snapshots.ts
Comment thread services/retl-runner/src/persistence/snapshots.ts
jitsu-code-review[bot]
jitsu-code-review Bot previously approved these changes Sep 14, 2026

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the PostgreSQL persistence lifecycle, recovery behavior, snapshot membership updates, and schema changes.

Findings:

  • A staged operation can be finalized after a later accepted operation and revert the effective membership.
  • A partially staged batch can have its recovery metadata replaced without any outcome transition.

Comment thread services/retl-runner/src/persistence/journal.ts
Comment thread services/retl-runner/src/persistence/journal.ts
jitsu-code-review[bot]
jitsu-code-review Bot previously approved these changes Sep 14, 2026

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new PostgreSQL reverse-ETL persistence layer, recovery paths, snapshot storage, and Prisma schema.

Finding:

  • Initialization can become unrecoverable after an interruption before writer creation.

Comment thread services/retl-runner/src/persistence/journal.ts
jitsu-code-review[bot]
jitsu-code-review Bot previously approved these changes Sep 15, 2026

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the PostgreSQL persistence schema, fencing/ownership, encrypted journal recovery, and snapshot paths. No new actionable correctness or security issues found.\n\nValidation: corepack pnpm --filter @jitsu-internal/retl-runner typecheck passed. The package test command could not run in this checkout because the local vitest executable is unavailable.

## Summary

JITSU-227. Stacked on #1516 (`feat/jitsu-227-persistence`); merge
persistence first. This PR adds the server-side snapshot-mirror library,
not an executable runner or production enablement.

- Collect and validate the complete source, seal its desired snapshot,
then deliver bounded additions before planning removals.
- Keep snapshots and diff planning in core. Adapters provide pure
identity/payload projection and ordinary writers, with explicit
per-batch acceptance and removal support; no snapshot API is added to
writer context.
- Persist exact normalized request manifests and accepted membership
changes. Permanent row rejection stops the run; ambiguous calls preserve
recovery evidence.
- Recover sealed snapshots through explicit provider reconciliation and
session attachment, without re-reading changed SQL or re-normalizing
identities. Promote the generation only after accepted finalization,
including empty/unchanged snapshots.
- Document target-baseline admission, incomplete-source restart,
provider contracts, and caller-owned lease renewal/cancellation.

Billing, schema changes, source-to-identity association tables, live
destination adapters, UI, native replacement, and syncctl/CronJobs are
intentionally excluded. The next slice wires the executable Node runner
into syncctl/CronJobs.

## Validation

- PostgreSQL integration tests use disposable containers and restricted
runtime roles, including failure/recovery, stale ownership, removal
ordering, and request bounds.
- All 105 tests pass with `RETL_MIRROR_SCALE_TEST=1` (80 persistence +
25 mirror).
- The opt-in scale test seeds one million synthetic SQL identities to
verify bounded diff pagination; it is not a full
extraction/encryption/provider-throughput benchmark.
- Runner TypeScript check and targeted ESLint pass.

No live advertising APIs or production databases were accessed.

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the PostgreSQL persistence, fencing/recovery, encrypted journal, Prisma schema, and snapshot-mirror lifecycle changes.

No new actionable correctness or security findings. Existing review threads are resolved and were not duplicated.

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