Skip to content

LISTEN wake-up (next_notification) and RowStream::rows_affected - #1

Merged
casibbald merged 3 commits into
masterfrom
feat/listen-wakeup-rows-affected
Oct 27, 2026
Merged

casibbald merged 3 commits into
masterfrom
feat/listen-wakeup-rows-affected

Conversation

@casibbald

Copy link
Copy Markdown

Two independent additions, one commit each.

1. RowStream::rows_affected() — parses the CommandComplete tag of the statement the stream ran and exposes the affected-row count (None until the stream is drained or for tags without a count; Some(0) for an empty query). Lets callers pipeline write statements through query_raw and still learn how many rows changed, without a separate execute round trip.

2. Client::next_notification(timeout) — LISTEN consumers can now block on a notification instead of polling the queue. The connection's notification queue is paired with a semaphore that is posted on every push and once more when the I/O loop exits, so a waiter wakes immediately on delivery, returns queued notifications first, times out with None, and returns promptly (None) when the connection closes. Client::notifications() keeps its existing queue-based behaviour.

Tests (in tests/test/main.rs):

  • row_stream_reports_rows_affected — UPDATE with and without RETURNING, plus two pipelined streams.
  • next_notification_wakes_a_waiting_client_without_polling
  • next_notification_returns_queued_notifications_first_and_times_out_empty
  • next_notification_returns_when_the_connection_closes (uses pg_terminate_backend).

Local: cargo test --all against postgres:16 on 127.0.0.1:5433 (TEST_PG_PORT=5433 for severed_connection) all green; pre-commit (fmt --check, clippy -D warnings) passes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Y3TSRmEZAYU6RBUXhkn7WF

casibbald and others added 3 commits October 27, 2026 18:57
`execute` reports how many rows a statement affected, but a statement
sent with `query_raw` returns a `RowStream`, which dropped the
`CommandComplete` tag. A caller that pipelines several statements with
`query_raw` (send them all, then read the streams in order) therefore had
no way to learn a write's row count without a separate round trip.

`RowStream::rows_affected()` returns the count parsed from the tag
(`UPDATE 5`, `INSERT 0 3`, `SELECT 2`) once the stream has been read to
its end, `Some(0)` for an empty query, and `None` before the end or for a
suspended portal. tokio-postgres has the same method.

Test: counts for an UPDATE without RETURNING, with RETURNING, and for two
pipelined statements read in order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y3TSRmEZAYU6RBUXhkn7WF
Notifications are queued by the connection coroutine as they arrive, but
the queue had no wake-up, so a consumer had to poll it: either burning
CPU on a short interval or adding up to the interval in latency.

The queue now carries a semaphore that the connection posts once per
notification, and once more when the connection closes.
`Client::next_notification(timeout)` returns a queued notification at
once, otherwise parks the coroutine (or thread) until one arrives, the
timeout passes, or the connection closes. `Client::notifications()` is
unchanged; mixing direct pops with waits only costs a spurious wake-up.

Tests: a parked waiter wakes on a notification raised by another session
with no request of its own; queued notifications come first and an empty
wait lasts its timeout; terminating the backend releases a waiter well
before its timeout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y3TSRmEZAYU6RBUXhkn7WF
The test postgres is published on 127.0.0.1:5433, but
tests/severed_connection.rs reads TEST_PG_PORT and defaults to 5432, so
both of its tests failed on every CI run (master included). Set
TEST_PG_PORT=5433 for the test step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y3TSRmEZAYU6RBUXhkn7WF
@casibbald
casibbald merged commit 9c55df0 into master Oct 27, 2026
2 checks passed
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