Description
When using tokio-postgres with Hyperdrive via workers-rs, issuing multiple query_typed calls on the same Client connection intermittently fails with "unexpected message from server". The error is non-deterministic — the same query succeeds sometimes and fails others, with roughly 30-50% failure rate on repeated requests.
Environment
workers-rs 0.8.x on wasm32-unknown-unknown
tokio-postgres 0.7.x with query_typed (unnamed statement / extended query protocol)
- Hyperdrive with caching enabled, transaction-mode pooling
- Supabase Postgres origin (PostgreSQL 15)
Reproduction
- Open a single TCP socket to Hyperdrive via
Socket::builder().secure_transport(SecureTransport::StartTls)
connect_raw with tokio_postgres::Config from hyperdrive.connection_string()
- Execute a
query_typed call — succeeds
- Execute a second
query_typed call on the same Client — intermittently fails with "unexpected message from server"
The error rate increases with query complexity (larger SQL strings fail more often) but even simple queries fail sometimes.
Key finding: protocol mixing
Using simple_query (simple query protocol) followed by query_typed (extended query protocol) on the same connection reliably produces the error. This suggests Hyperdrive's message buffering/replay logic desynchronizes when it sees different protocol message types on the same client socket.
Workaround
Opening a fresh TCP socket to Hyperdrive for each query operation eliminates the issue. This is consistent with the docs' guidance that "creating a new client on each invocation is fast and recommended" — but that guidance appears to be about JS clients where each operation naturally creates a new socket. For Rust/tokio-postgres, a Client is designed to be reused across multiple queries on the same connection, and the extended query protocol expects consistent wire state.
The workaround works but adds ~1-2ms per query (edge-local socket setup) and defeats Hyperdrive's connection multiplexing within a single request handler that needs multiple queries.
Expected behavior
Multiple query_typed calls on the same Hyperdrive connection should work reliably, as they do with a direct connection to PostgreSQL. The extended query protocol (Parse → Bind → Describe → Execute → Sync) should maintain consistent state across queries on the same socket.
Hypothesis
Hyperdrive's transaction-mode pooling routes individual queries within a single client socket to different backend PostgreSQL connections. When tokio-postgres sends the second query's Parse message, the backend connection may have been swapped, and the response doesn't match what the client's protocol state machine expects. The prepared statements blog post describes Hyperdrive buffering and replaying messages — the replay logic may not correctly handle the state transitions of the unnamed-statement extended query protocol used by query_typed.
Docs gap
The Connect to PostgreSQL docs list rust-postgres v0.19.8 as supported and say "Use the query_typed method for best performance" but don't mention:
- Whether a single
Client can issue multiple queries (answer: apparently not reliably)
- That mixing simple and extended query protocols is unsupported
- That a fresh socket per query is required for reliable operation
Adding this guidance would save Rust/Workers developers significant debugging time.
Description
When using
tokio-postgreswith Hyperdrive viaworkers-rs, issuing multiplequery_typedcalls on the sameClientconnection intermittently fails with"unexpected message from server". The error is non-deterministic — the same query succeeds sometimes and fails others, with roughly 30-50% failure rate on repeated requests.Environment
workers-rs0.8.x onwasm32-unknown-unknowntokio-postgres0.7.x withquery_typed(unnamed statement / extended query protocol)Reproduction
Socket::builder().secure_transport(SecureTransport::StartTls)connect_rawwithtokio_postgres::Configfromhyperdrive.connection_string()query_typedcall — succeedsquery_typedcall on the sameClient— intermittently fails with"unexpected message from server"The error rate increases with query complexity (larger SQL strings fail more often) but even simple queries fail sometimes.
Key finding: protocol mixing
Using
simple_query(simple query protocol) followed byquery_typed(extended query protocol) on the same connection reliably produces the error. This suggests Hyperdrive's message buffering/replay logic desynchronizes when it sees different protocol message types on the same client socket.Workaround
Opening a fresh TCP socket to Hyperdrive for each query operation eliminates the issue. This is consistent with the docs' guidance that "creating a new client on each invocation is fast and recommended" — but that guidance appears to be about JS clients where each operation naturally creates a new socket. For Rust/tokio-postgres, a
Clientis designed to be reused across multiple queries on the same connection, and the extended query protocol expects consistent wire state.The workaround works but adds ~1-2ms per query (edge-local socket setup) and defeats Hyperdrive's connection multiplexing within a single request handler that needs multiple queries.
Expected behavior
Multiple
query_typedcalls on the same Hyperdrive connection should work reliably, as they do with a direct connection to PostgreSQL. The extended query protocol (Parse → Bind → Describe → Execute → Sync) should maintain consistent state across queries on the same socket.Hypothesis
Hyperdrive's transaction-mode pooling routes individual queries within a single client socket to different backend PostgreSQL connections. When tokio-postgres sends the second query's Parse message, the backend connection may have been swapped, and the response doesn't match what the client's protocol state machine expects. The prepared statements blog post describes Hyperdrive buffering and replaying messages — the replay logic may not correctly handle the state transitions of the unnamed-statement extended query protocol used by
query_typed.Docs gap
The Connect to PostgreSQL docs list
rust-postgres v0.19.8as supported and say "Use thequery_typedmethod for best performance" but don't mention:Clientcan issue multiple queries (answer: apparently not reliably)Adding this guidance would save Rust/Workers developers significant debugging time.