Skip to content

wingfoil-next: port the postgres adapter (Phase 4) - #539

Merged
0-jake-0 merged 1 commit into
nextfrom
claude/next-postgres-adapter
Jul 25, 2026
Merged

wingfoil-next: port the postgres adapter (Phase 4)#539
0-jake-0 merged 1 commit into
nextfrom
claude/next-postgres-adapter

Conversation

@0-jake-0

Copy link
Copy Markdown
Contributor

Ports the classic postgres adapter to wingfoil-next on the Op model (port-plan Phase 4, item 4), following the new-adapter-next skill. Base branch is next.

What landed

  • postgres_read — time-partitioned historical replay. As the first time-partitioned adapter to port, it lands the shared time slicer (compute_time_slices / compute_validated_time_slices) into adapters::common alongside the already-present WindowFilter/TimeWindow. The slicer is adapter-agnostic and feature-gated on postgres only for now; kdb (Wave 3) adds feature = "kdb" to the same #[cfg] gate later (noted in the port-plan). The reader queries every midnight-aligned slice at wiring time (Handle::block_on), clamps each row through WindowFilter, keeps the classic monotonic-time check, and feeds the finite Result<(row, time)> set to GraphBuilder::replay_results.
  • postgres_sub — realtime LISTEN/NOTIFY live tail on produce_async (watch-before-get: LISTEN before the catch-up query). It rejects RunMode::HistoricalFrom at wiring, returning Result (live, unbounded, wall-clock stream with no historical timeline — the etcd/redis invariant).
  • PostgresSinkOps::postgres_write — streaming insert sink over consume_async, connecting eagerly at wiring (Result), per-burst pipelined (~1 round trip/burst), off the graph thread.

Password redaction (classic PR #433)

Reproduced: PostgresConnection::redacted() masks the DSN password=… token, and it is applied at every connect() error site — postgres_read, postgres_sub, postgres_write. Covered by no-service tests (read_connection_refused_redacts_password, write_connection_refused_redacts_password) that assert the raw password never appears and password=*** does.

Primitives used

GraphBuilder::replay_results (reader), produce_async (live tail), consume_async (sink), plus the ported WindowFilter/TimeWindow and the new time slicer in adapters::common.

Deviations from classic (capabilities all preserved)

  1. Caller-owned tokio runtime — &Handle (+ RunParams for the sources), matching next's async convention.
  2. The reader queries at wiring onto replay_results rather than streaming via produce_async (identical for a bounded historical run; connection/query errors surface at wiring, decode/non-monotonic-time errors still abort the run through replay_results).
  3. The sink is the PostgresSinkOps trait only (classic's free fn + operator trait folded in), burst-only like classic, connects eagerly, and takes a consume_async buffer_size.
  4. Row / ToSql / Type re-exported for impl ergonomics, as classic.

Tests / example / CI

  • No-service tests/postgres_adapter.rs (params validation, connection-refused + redaction, historical rejection) — passing.
  • Container-backed tests/postgres_integration.rs (gated postgres-integration-test, testcontainers postgres:16-alpine) — parity port of the classic integration tests (time-sliced read, timestamptz, drop-before-start, empty table, write round-trip, multi-row burst, catch-up + live NOTIFY); asserts values and tick times.
  • Classic example ported to examples/postgres_adapter/ (registered with required-features = ["postgres"]).
  • New workflow .github/workflows/postgres-next-integration.yml, registered in the integration-tests.yml hub.
  • No per-adapter Python bindings — those arrive with the facade/cutover phase (next/docs/port-plan.md, Phase 6), per the skill.

Checks

  • cargo fmt --all — clean.
  • cargo lint (default features) — pass.
  • cargo lint-all (all features, workspace) — pass (the sandbox aeron/CMake issue did not trigger, so no clippy substitution was needed).
  • cargo test -p wingfoil-next --features postgrespass (slicer + adapter unit + no-service tests). Full --all-features suite compiles.
  • Integration tests require Docker, which is unavailable in the build sandbox (/var/run/docker.sock missing), so they were not executed locally — they run in CI.

🤖 Generated with Claude Code


Generated by Claude Code

Port the classic `postgres` adapter to wingfoil-next on the Op model:

- `postgres_read` — time-partitioned historical replay. First time-
  partitioned adapter to port, so it lands the shared time slicer
  (`compute_time_slices` / `compute_validated_time_slices`) in
  `adapters::common` (feature-gated `postgres`; kdb adds its feature
  later) alongside the existing `WindowFilter`/`TimeWindow`. Queries every
  midnight-aligned slice at wiring time (`Handle::block_on`), clamps each
  row through `WindowFilter`, and feeds the finite rows to
  `replay_results`.
- `postgres_sub` — realtime `LISTEN`/`NOTIFY` live tail on `produce_async`;
  rejects `RunMode::HistoricalFrom` at wiring (live, unbounded stream).
- `PostgresSinkOps::postgres_write` — streaming insert sink over
  `consume_async`, connecting eagerly at wiring, per-burst pipelined.

Password redaction (classic PR #433) is reproduced:
`PostgresConnection::redacted()` masks the DSN `password=...` token at
every `connect()` error site.

Parity tests ported: no-service `tests/postgres_adapter.rs` (validation,
connection-refused + redaction, historical rejection) and container-backed
`tests/postgres_integration.rs` (gated `postgres-integration-test`,
testcontainers). Classic example ported to `examples/postgres_adapter/`.
CI wired into the integration-tests hub; port-plan updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SEQvysabaSKTnYyQDgPDTt
@0-jake-0
0-jake-0 force-pushed the claude/next-postgres-adapter branch from 2ae61b5 to 1435c2b Compare July 25, 2026 21:33
@0-jake-0
0-jake-0 merged commit 1b6b0c3 into next Jul 25, 2026
9 checks passed
@0-jake-0
0-jake-0 deleted the claude/next-postgres-adapter branch July 25, 2026 21:50
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.

2 participants