mybatis/mybatis-3 · critical · SQLException

PooledDataSource: Could not get a good connection to the dat

Error message

PooledDataSource: Could not get a good connection to the database.

What it means

PooledDataSource.popConnection() loops trying to obtain a usable connection. When it repeatedly gets connections that fail pingConnectionDB() validation ('bad connections'), it counts them; once localBadConnectionCount exceeds poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance (default tolerance 3), it throws SQLException 'PooledDataSource: Could not get a good connection to the database.' This means the pool can hand out connections but they are all broken.

Source

Thrown at src/main/java/org/apache/ibatis/datasource/pooled/PooledDataSource.java:532

            conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
            conn.setCheckoutTimestamp(System.currentTimeMillis());
            conn.setLastUsedTimestamp(System.currentTimeMillis());
            state.activeConnections.add(conn);
            state.requestCount++;
            state.accumulatedRequestTime += System.currentTimeMillis() - t;
          } else {
            if (log.isDebugEnabled()) {
              log.debug("A bad connection (" + conn.getRealHashCode()
                  + ") was returned from the pool, getting another connection.");
            }
            state.badConnectionCount++;
            localBadConnectionCount++;
            conn = null;
            if (localBadConnectionCount > poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance) {
              if (log.isDebugEnabled()) {
                log.debug("PooledDataSource: Could not get a good connection to the database.");
              }
              throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
            }
          }
        }
      } finally {
        lock.unlock();
      }

    }

    if (conn == null) {
      if (log.isDebugEnabled()) {
        log.debug("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
      }
      throw new SQLException(
          "PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
    }

    return conn;

View on GitHub (pinned to 008069adb1)

Solutions

  1. Check that the database is actually reachable and up (the pool found connections but every one failed validation)
  2. Enable connection validation: set poolPingEnabled=true, poolPingQuery to a cheap valid query (e.g., SELECT 1), and poolPingConnectionsNotUsedFor under the network/firewall idle timeout
  3. Reduce idle connections: lower poolMaximumIdleConnections so fewer stale sockets are cached, and ensure poolMaximumActiveConnections sizing fits DB limits
  4. If using a custom poolPingQuery, verify it executes on your DB; if the driver supports it, prefer JDBC isValid by leaving the default ping path
  5. Raise poolMaximumLocalBadConnectionTolerance only as a stopgap; the root cause is dead connections in the pool

Example fix

<!-- before: no validation, dead connections stay in pool -->
<dataSource type="POOLED">
  <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
  <property name="url" value="jdbc:mysql://db:3306/app"/>
</dataSource>

<!-- after -->
<dataSource type="POOLED">
  <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
  <property name="url" value="jdbc:mysql://db:3306/app"/>
  <property name="poolPingEnabled" value="true"/>
  <property name="poolPingQuery" value="select 1"/>
  <property name="poolPingConnectionsNotUsedFor" value="60000"/>
  <property name="poolMaximumIdleConnections" value="5"/>
</dataSource>
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight check that the DB is reachable before pool use:
try (Connection c = pooledDataSource.getConnection()) {
  if (!c.isValid(2)) throw new SQLException("DB unreachable");
}

Try / catch

int attempts = 0;
while (true) {
  try (Connection c = pooledDataSource.getConnection()) {
    return c.createStatement().executeQuery(q);
  } catch (SQLException e) {
    if (++attempts >= 3 || !e.getMessage().contains("Could not get a good connection")) throw e;
    // pool is full of dead connections; wait for revalidation cycle
  }
}

Prevention

When it happens

Trigger: poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance consecutive connections fail the PoolState ping query (PoolPingEnabled / poolPingQuery); database restarted or network dropped while pooled connections remained cached; wrong poolPingQuery for the DB dialect; driver incompatibility making isValid()/ping fail; DB max_allowed_packet or firewall idle-killing connections faster than pings detect.

Common situations: Database failover/restart behind a load balancer leaving dead sockets in the pool; aggressive firewall/LB idle timeouts (e.g., 5 min) killing pooled connections; poolPingEnabled=false so dead connections are never validated; custom poolPingQuery that is not valid SQL on the target database; MySQL wait_timeout killing idle connections.

Related errors


AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14). Data as JSON: /api/errors/a7fb8f876a9a5dd4. Report an issue: GitHub.