wingfoil-next: port the postgres adapter (Phase 4) - #539
Merged
Conversation
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
force-pushed
the
claude/next-postgres-adapter
branch
from
July 25, 2026 21:33
2ae61b5 to
1435c2b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ports the classic
postgresadapter to wingfoil-next on the Op model (port-plan Phase 4, item 4), following thenew-adapter-nextskill. Base branch isnext.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) intoadapters::commonalongside the already-presentWindowFilter/TimeWindow. The slicer is adapter-agnostic and feature-gated onpostgresonly for now; kdb (Wave 3) addsfeature = "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 throughWindowFilter, keeps the classic monotonic-time check, and feeds the finiteResult<(row, time)>set toGraphBuilder::replay_results.postgres_sub— realtimeLISTEN/NOTIFYlive tail onproduce_async(watch-before-get:LISTENbefore the catch-up query). It rejectsRunMode::HistoricalFromat wiring, returningResult(live, unbounded, wall-clock stream with no historical timeline — the etcd/redis invariant).PostgresSinkOps::postgres_write— streaming insert sink overconsume_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 DSNpassword=…token, and it is applied at everyconnect()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 andpassword=***does.Primitives used
GraphBuilder::replay_results(reader),produce_async(live tail),consume_async(sink), plus the portedWindowFilter/TimeWindowand the new time slicer inadapters::common.Deviations from classic (capabilities all preserved)
&Handle(+RunParamsfor the sources), matching next's async convention.replay_resultsrather than streaming viaproduce_async(identical for a bounded historical run; connection/query errors surface at wiring, decode/non-monotonic-time errors still abort the run throughreplay_results).PostgresSinkOpstrait only (classic's free fn + operator trait folded in), burst-only like classic, connects eagerly, and takes aconsume_asyncbuffer_size.Row/ToSql/Typere-exported forimplergonomics, as classic.Tests / example / CI
tests/postgres_adapter.rs(params validation, connection-refused + redaction, historical rejection) — passing.tests/postgres_integration.rs(gatedpostgres-integration-test, testcontainerspostgres: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.examples/postgres_adapter/(registered withrequired-features = ["postgres"])..github/workflows/postgres-next-integration.yml, registered in theintegration-tests.ymlhub.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 postgres— pass (slicer + adapter unit + no-service tests). Full--all-featuressuite compiles./var/run/docker.sockmissing), so they were not executed locally — they run in CI.🤖 Generated with Claude Code
Generated by Claude Code