Repository navigation
Conversation
Add an application-level keep-alive mechanism to the SQL connection pool so connections silently dropped while idle (e.g. by a load balancer or the database) are detected and replaced, instead of failing the next query with a connection error. When a pooled connection has been idle for at least a configurable interval, the pool issues a lightweight probe statement against it before handing it to a caller. If the probe fails because the connection is stale, the pool closes it (removing it via the connector onRemove callback, since ConnectionPool.evict only removes unused connections) and transparently retries the command on a fresh connection, bounded by MAX_KEEPALIVE_RETRIES. Core (vertx-sql-client): - PoolOptions.idleKeepAlive / setIdleKeepAlive (default 0 = disabled) - PingCommand carrying the configurable probe statement - Connection.keepAliveQuery() SPI; the pool issues it on checkout and recovers from stale connections in execute() PostgreSQL (vertx-pg-client): - PgConnectOptions.keepAliveQuery (default 'SELECT 1'), implemented by PgSocketConnection and encoded as a simple query on the wire Tests (PgPoolKeepAliveTest): - a healthy idle connection is kept alive (same backend PID) - a connection dropped at the network level via ProxyServer recovers transparently on a fresh backend Signed-off-by: Vasily Pelikh <vasily.pelikh@gmail.com>
|
Hey @vietj @tsegismont. Just pinging you both in case you missed it. |
|
it wasn't set to a milestone, hence out of the radar |
|
Thanks for your proposal @vpelikh So far the problem with stale connections has been solved with |
|
@tsegismont Thanks for the question. Yes, The key point: Problem 1: External idle timeout can fire before Problem 2: Lowering Problem 3: The probe solves all three problems at once: it actively validates the connection right before handing it out and re-establishes it if it's dead. At the same time, This is a well-established pattern used in many database clients and connection pools — for example, Go's |
Summary
Adds an application-level keep-alive mechanism to the SQL connection pool so connections that are silently dropped while idle (for example by an intermediate load balancer or the database itself) are detected and replaced with a fresh connection instead of failing the next user query with a
Connection timed outerror.The mechanism works in two parts:
Changes
Core (
vertx-sql-client)PoolOptions.idleKeepAlive/setIdleKeepAlive(with unit, default0= disabled). Configures how long a pooled connection can be idle before it is probed.spi/protocol/PingCommand— a probe command carrying the lightweight statement used to verify liveness.Connection.keepAliveQuery()SPI — the pool reads the probe statement from the connection, keeping the pool database-agnostic.PoolImpl/SqlConnectionPool):probeIfNeededissues thePingCommandon checkout when the idle threshold is exceeded; on failure it closes the stale connection, which removes it from the pool via the connectoronRemovecallback (ConnectionPool.evictonly removes unused connections, so a leased stale one must be closed instead).executetransparently retries the command on a fresh connection, bounded byMAX_KEEPALIVE_RETRIES.PostgreSQL (
vertx-pg-client)PgConnectOptions.keepAliveQuery— the probe statement is moved to configuration (defaultSELECT 1).PgSocketConnectionimplementskeepAliveQuery()and encodes aPingCommandas a simple query on the wire.Tests
PgPoolKeepAliveTest:ProxyServer, simulating a load-balancer silent close) recovers transparently on a fresh backend.Why an application-level probe
TCP keep-alive alone is often insufficient: load balancers can drop idle connections without sending a reset, and OS keep-alive intervals are long. An application-level probe actively verifies the connection is usable right before it is used, directly addressing idle stale-connection failures.
Design notes
idleKeepAlive = 0), so no behaviour change for existing users.PgConnectOptions.keepAliveQuery), so each database can provide a correct, portable no-op statement.Interaction with the
vertx-coreconnection poolThe stale-connection handling is scoped to the SQL client and deliberately does not change the
vertx-coreconnection pool:ConnectionPool.evict(...)only removes unused connections (usage == 0). That is an intentional contract ofvertx-core, so evicting a stale leased connection there would change public pooling semantics.vertx-coremechanism: when the probe fails, the SQL pool closes the stale connection, which fires the connectoronRemove()callback.vertx-core'sRemoveaction handles a leased connection correctly — it drops it and, if a waiter is queued, immediately connects a replacement.This keeps the fix localized to
vertx-sql-clientand preserves allvertx-corepool guarantees.