Skip to content

Add opt-in query pipelining#3652

Open
mcollina wants to merge 4 commits intobrianc:masterfrom
mcollina:pipelining-v2
Open

Add opt-in query pipelining#3652
mcollina wants to merge 4 commits intobrianc:masterfrom
mcollina:pipelining-v2

Conversation

@mcollina
Copy link
Copy Markdown

Adds opt-in query pipelining via client.pipelining = true (or new Pool({ pipelining: true })).

Pipelined queries are sent to the server without waiting for previous responses, using PostgreSQL's extended query protocol with per-query Sync for error isolation. ~2-3x throughput on batches of simple queries locally.

  • pg — three-queue state machine (_queryQueue_sentQueryQueue_activeQuery), named-statement dedup across in-flight Parse, graceful end(), query_timeout unblocks the pipeline
  • pg-poolpipelining option sets the flag on every client the pool creates
  • Unit + integration tests, benchmark, and docs (features + API reference)

Allow multiple queries to be sent on the wire before waiting for
responses, reducing round-trip latency. Enabled via client.pipelining = true.
Each query gets its own Sync boundary so errors are isolated.

Tracks in-flight named statements (submittedNamedStatements) to prevent
duplicate Parse messages when pipelining queries with the same prepared
statement name. Handles error/disconnect cleanup for the sent queue.
- Clean up submittedNamedStatements on error in _handleErrorMessage to
  prevent stale entries from blocking future re-preparation of the same
  named statement after a parse failure
- Guard _pulsePipelinedQueryQueue against non-queryable connections
- Fix cancel() and readTimeout for sent queries: removing an already-sent
  query from _sentQueryQueue corrupts the pipeline response mapping since
  the server will still respond to it; no-op the callback instead
- Add bench-pipelining.js comparing serial vs pipelined throughput
Gate _sentQueryQueue activation on readyForQuery=true inside
_pulsePipelinedQueryQueue (and remove the redundant promotion block
from _handleReadyForQuery) to eliminate the microtask/macrotask race
where the next query could be activated as _activeQuery before the
error's ReadyForQuery arrived, causing that RFQ to be handled by the
wrong query.

Also adds the error-listener fix for the query_timeout integration test
so the expected stream-destroy doesn't leak as an unhandled 'error'.
- New features/pipelining.mdx documenting the opt-in flag
- Client and Pool API reference updated
- Pool accepts `pipelining: true` and sets it on every client it creates
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