apache/seatunnel · warning

Replaced an unusable pooled connection for queue index {}. {

Error message

Replaced an unusable pooled connection for queue index {}. {} replacement(s) since the last such message. Repeated messages indicate the connection is not surviving between writes.

What it means

This is a throttled WARN from the JDBC sink's connection pool manager (ConnectionPoolManager.logReplacement). It fires when a pooled connection for a given queue index was found unusable (dead/stale) and had to be discarded and replaced. The message is rate-limited to once per REPLACEMENT_WARN_INTERVAL_NANOS and reports how many replacements occurred since the last warning; repeated warnings mean connections keep dying between writes rather than being a one-off stale connection.

Source

Thrown at seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/sink/ConnectionPoolManager.java:134

    /**
     * Reports a replaced connection at WARN, rate limited to one message per minute per manager.
     *
     * <p>Replacing an occasionally idle connection is the expected case and is not worth a warning
     * on every occurrence. A validation that fails systematically is not: a misconfigured {@code
     * connectionTestQuery}, a persistent network fault or a server-side connection limit makes
     * every call replace the connection, which silently turns the cache off and churns connections
     * continuously. Logging only at DEBUG would leave that indistinguishable from healthy operation
     * in a default deployment, so the first occurrence and a periodic summary are surfaced.
     */
    private void logReplacement(int index) {
        long replacements = replacementsSinceLastWarn.incrementAndGet();
        long now = System.nanoTime();
        long last = lastReplacementWarnNanos.get();

        if (now - last >= REPLACEMENT_WARN_INTERVAL_NANOS
                && lastReplacementWarnNanos.compareAndSet(last, now)) {
            replacementsSinceLastWarn.addAndGet(-replacements);
            log.warn(
                    "Replaced an unusable pooled connection for queue index {}. "
                            + "{} replacement(s) since the last such message. Repeated messages "
                            + "indicate the connection is not surviving between writes.",
                    index,
                    replacements);

            return;
        }

        log.debug("Cached connection for queue index {} is no longer usable, replacing it", index);
    }

    private long getValidationTimeoutMillis() {
        long configured = connectionPool.getValidationTimeout();
        return configured > 0 ? configured : DEFAULT_VALIDATION_TIMEOUT_MILLIS;
    }

    private void closeQuietly(Connection connection) {

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Check DB server idle-connection kill settings (MySQL wait_timeout, PG idle_session_timeout) and set the pool's max idle/ maxLifetime below them
  2. Enable TCP keepalive and connection validation/test-on-borrow in the JDBC URL and pool config (e.g. connectTimeout, socketTimeout, keepalive properties)
  3. Reduce the interval between writes or configure the connector's connection pool keepalive so connections stay fresh
  4. Verify network middleboxes (LB/firewall/NAT) idle TCP timeouts and raise them or enable keepalives
  5. If only occasional, treat as benign — the pool already recovered; repeated warnings indicate a systematic idle-timeout mismatch

Example fix

// before: jdbc url without keepalive
url=jdbc:mysql://db:3306/mydb
// after
url=jdbc:mysql://db:3306/mydb?connectTimeout=10000&socketTimeout=60000&tcpKeepAlive=true&autoReconnect=false
Defensive patterns

Strategy: validation

Validate before calling

// before submitting the job, probe and compare idle limits
try (Connection c = DriverManager.getConnection(url, user, pass)) {
    if (c.isValid(5)) { System.out.println("connection ok, enable keepalive + pool test-on-borrow"); }
}
// keep pool maxIdleTime < DB wait_timeout (e.g. wait_timeout=600 -> maxIdleTime=300)

Prevention

When it happens

Trigger: Calling getConnection when the pool hands out a connection that fails a validity/ usability check, so it is replaced. Happens repeatedly when the database or a network middlebox (firewall, LB, NAT) kills idle connections faster than they are reused, or when pool idle timeouts exceed server-side timeouts (e.g. MySQL wait_timeout, TCP keepalive off).

Common situations: Long idle periods between checkpointed write batches; cloud databases (RDS/Aurora/ProxySQL) dropping idle TCP connections; MySQL 'server has gone away'; PostgreSQL 'terminating connection due to administrator command'; misconfigured pool maxIdleTime larger than DB idle kill threshold.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/5dc529b561977429. Report an issue: GitHub.