Repository navigation
Validate idle sessions with buffered non-blocking reads - #14
Merged
Merged
Conversation
`isopen(conn)` reflects only the local socket. When the server terminates a session that is sitting idle (`pg_terminate_backend`, a restart, failover, `idle_session_timeout`), it sends a FATAL ErrorResponse and closes, and those bytes wait unread in the socket. The pool validated idle connections with `isopen`, so it handed the dead connection to the next borrower, who absorbed the failure (#13). Add `isvalid(conn::Connection)`: read whatever the server has already sent without blocking and without sending anything. Asynchronous notices, notifications, and parameter changes are dispatched to the style callbacks; a FATAL, an unexpected message, a read failure, or a closed stream closes the socket and reports the connection dead; nothing pending means alive. The connection lock is held, so a statement in flight is never disturbed. The pool checks with `isvalid` on acquire and release, so a terminated idle connection is replaced rather than reused. Nothing is ever retried. The non-blocking peek behind this is `Reseau.TCP.pending_input` and `Reseau.TLS.pending_input` (Reseau 1.5.0); the GSS transport gets the same contract on top of its plaintext buffer. Version 2.2.0. The CI steps that install the unreleased Reseau branch are temporary until Reseau 1.5.0 is registered. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`read(socket, len)` returns short when the peer closes mid-message (the Base contract), so a message whose body never fully arrived parsed as a valid one: an ErrorResponse or NoticeResponse with missing fields, a DataRow decoded from a partial buffer, a partial COPY chunk. Three parsers already guarded this by hand; the others did not, and `isvalid` reading a truncated notice dispatched an empty notice before noticing the EOF. One helper, `readbody`, now does every body read: it closes the socket, whose stream position is unknowable after a short read, and throws. The three hand-written guards are replaced by it. The SASL authentication exchange keeps its plain reads: a truncated token fails the handshake regardless. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A live PostgreSQL cannot be made to send, on demand, the message shapes `isvalid` has branches for: a notice or parameter change while idle, a warning followed by a bare close (what an immediate shutdown or a crash of another backend produces), a message that cannot occur on an idle session, a truncated message, or a close with no bytes at all. Drive them from the fake server used by the GSSAPI tests, on plain TCP, and check that live connections stay valid with their callbacks fed, and that every dead shape reports false and closes the socket. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The four temporary CI steps that installed Reseau from the nonblocking-peek branch are no longer needed: Reseau 1.5.0 carries the non-blocking tryread! APIs and is registered, and the compat bound already requires it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
3 of 10 tasks
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.
Closes #13. Detects idle-terminated sessions before pool reuse, without sending a probe query or retrying any user operation.
isopenreports local socket state. A server FATAL and EOF can already be waiting in the receive queue while that state still says open. Checking for one pending byte does not solve this safely: the rest of the PostgreSQL message or encrypted record may not have arrived.Postgres.isvalid(conn)now collects available input through Reseau's non-blockingtryread!APIs. Validation and all normal post-authentication reads share one buffered stream, including startup session settings and reconnects. Only complete PostgreSQL messages are parsed. Partial messages remain available to a later validation, query, or notification read. GSS-encrypted frames also preserve partial headers and bodies.Postgres.isvalidis qualified public API, not exported and not an extension ofBase.isvalid.isopenkeeps its existing local-state meaning.Validation:
3013885, including cross-platform tests, PostgreSQL-version tests, documentation, and coverage checks.Depends on JuliaServices/Reseau.jl#163. Version remains 2.2.0. The four temporary CI installs of Reseau's
nonblocking-peekbranch must be removed after Reseau 1.5.0 is registered.Co-authored by Codex