|
9 | 9 | import org.apache.commons.dbcp2.PoolableConnection; |
10 | 10 | import org.apache.commons.dbcp2.PoolableConnectionFactory; |
11 | 11 | import org.apache.commons.dbcp2.PoolingDataSource; |
12 | | -import org.apache.commons.pool2.impl.AbandonedConfig; |
13 | 12 | import org.apache.commons.pool2.impl.GenericObjectPool; |
14 | 13 | import org.hypertrace.core.documentstore.model.config.ConnectionPoolConfig; |
15 | 14 | import org.hypertrace.core.documentstore.model.config.postgres.PostgresConnectionConfig; |
16 | 15 |
|
17 | 16 | @Slf4j |
18 | 17 | class PostgresConnectionPool { |
19 | 18 |
|
20 | | - private static final String VALIDATION_QUERY = "SELECT 1"; |
21 | | - private static final Duration VALIDATION_QUERY_TIMEOUT = Duration.ofSeconds(5); |
22 | | - |
23 | 19 | // Single pool with autoCommit=true by default. Transactional connections flip autoCommit |
24 | 20 | // to false on borrow; DBCP2 resets it back when the connection is returned to the pool. |
25 | 21 | private final PoolingDataSource<PoolableConnection> dataSource; |
@@ -66,57 +62,90 @@ private PoolingDataSource<PoolableConnection> createPooledDataSource( |
66 | 62 |
|
67 | 63 | final ConnectionPoolConfig poolConfig = config.connectionPoolConfig(); |
68 | 64 | setPoolProperties(connectionPool, poolConfig); |
69 | | - setFactoryProperties(poolableConnectionFactory, connectionPool); |
| 65 | + setFactoryProperties(poolableConnectionFactory, connectionPool, poolConfig); |
70 | 66 |
|
71 | 67 | return new PoolingDataSource<>(connectionPool); |
72 | 68 | } |
73 | 69 |
|
74 | 70 | private void setPoolProperties( |
75 | 71 | final GenericObjectPool<PoolableConnection> connectionPool, |
76 | 72 | final ConnectionPoolConfig poolConfig) { |
77 | | - final AbandonedConfig abandonedConfig = getAbandonedConfig(poolConfig); |
78 | 73 | final int maxConnections = poolConfig.maxConnections(); |
79 | 74 | connectionPool.setMaxTotal(maxConnections); |
80 | 75 | connectionPool.setMaxIdle(getIdleCount(poolConfig.maxIdlePercent(), maxConnections)); |
81 | 76 | connectionPool.setMinIdle(getIdleCount(poolConfig.minIdlePercent(), maxConnections)); |
82 | 77 | connectionPool.setBlockWhenExhausted(true); |
83 | 78 | connectionPool.setMaxWaitMillis(poolConfig.connectionAccessTimeout().toMillis()); |
84 | | - connectionPool.setTestOnBorrow(poolConfig.testOnBorrow()); |
85 | | - connectionPool.setAbandonedConfig(abandonedConfig); |
| 79 | + if (poolConfig.testOnCreate() != null) { |
| 80 | + connectionPool.setTestOnCreate(poolConfig.testOnCreate()); |
| 81 | + } |
| 82 | + if (poolConfig.testOnBorrow() != null) { |
| 83 | + connectionPool.setTestOnBorrow(poolConfig.testOnBorrow()); |
| 84 | + } |
| 85 | + if (poolConfig.testOnReturn() != null) { |
| 86 | + connectionPool.setTestOnReturn(poolConfig.testOnReturn()); |
| 87 | + } |
| 88 | + if (poolConfig.testWhileIdle() != null) { |
| 89 | + connectionPool.setTestWhileIdle(poolConfig.testWhileIdle()); |
| 90 | + } |
| 91 | + if (poolConfig.timeBetweenEvictionRuns() != null) { |
| 92 | + connectionPool.setTimeBetweenEvictionRuns(poolConfig.timeBetweenEvictionRuns()); |
| 93 | + } |
| 94 | + if (poolConfig.numTestsPerEvictionRun() != null) { |
| 95 | + connectionPool.setNumTestsPerEvictionRun(poolConfig.numTestsPerEvictionRun()); |
| 96 | + } |
| 97 | + if (poolConfig.minEvictableIdleTime() != null) { |
| 98 | + connectionPool.setMinEvictableIdleTime(poolConfig.minEvictableIdleTime()); |
| 99 | + } |
| 100 | + if (poolConfig.softMinEvictableIdleTime() != null) { |
| 101 | + connectionPool.setSoftMinEvictableIdleTime(poolConfig.softMinEvictableIdleTime()); |
| 102 | + } |
| 103 | + if (poolConfig.lifo() != null) { |
| 104 | + connectionPool.setLifo(poolConfig.lifo()); |
| 105 | + } |
86 | 106 | log.debug( |
87 | | - "Postgres connection pool properties - maxTotal: {}, maxIdle: {}, minIdle: {}, maxWaitMillis: {}, connectionSurrenderTimeout: {}, testOnBorrow: {}", |
| 107 | + "Postgres connection pool properties - maxTotal: {}, maxIdle: {}, minIdle: {}, maxWaitMillis: {}, testOnBorrow: {}, testWhileIdle: {}, timeBetweenEvictionRunsMillis: {}, minEvictableIdleTimeMillis: {}, maxConnLifetimeMillis: {}, lifo: {}", |
88 | 108 | connectionPool.getMaxTotal(), |
89 | 109 | connectionPool.getMaxIdle(), |
90 | 110 | connectionPool.getMinIdle(), |
91 | 111 | connectionPool.getMaxWaitMillis(), |
92 | | - poolConfig.connectionSurrenderTimeout(), |
93 | | - poolConfig.testOnBorrow()); |
| 112 | + poolConfig.testOnBorrow(), |
| 113 | + poolConfig.testWhileIdle(), |
| 114 | + toMillisOrNull(poolConfig.timeBetweenEvictionRuns()), |
| 115 | + toMillisOrNull(poolConfig.minEvictableIdleTime()), |
| 116 | + toMillisOrNull(poolConfig.maxConnLifetime()), |
| 117 | + poolConfig.lifo()); |
| 118 | + } |
| 119 | + |
| 120 | + private static Long toMillisOrNull(final Duration duration) { |
| 121 | + return duration == null ? null : duration.toMillis(); |
94 | 122 | } |
95 | 123 |
|
96 | 124 | private void setFactoryProperties( |
97 | 125 | PoolableConnectionFactory poolableConnectionFactory, |
98 | | - GenericObjectPool<PoolableConnection> connectionPool) { |
| 126 | + GenericObjectPool<PoolableConnection> connectionPool, |
| 127 | + ConnectionPoolConfig poolConfig) { |
99 | 128 | poolableConnectionFactory.setPool(connectionPool); |
100 | | - poolableConnectionFactory.setValidationQuery(VALIDATION_QUERY); |
101 | | - poolableConnectionFactory.setValidationQueryTimeout((int) VALIDATION_QUERY_TIMEOUT.toSeconds()); |
| 129 | + if (poolConfig.validationQuery() != null) { |
| 130 | + poolableConnectionFactory.setValidationQuery(poolConfig.validationQuery()); |
| 131 | + } |
| 132 | + if (poolConfig.validationQueryTimeout() != null) { |
| 133 | + poolableConnectionFactory.setValidationQueryTimeout( |
| 134 | + (int) poolConfig.validationQueryTimeout().toSeconds()); |
| 135 | + } |
102 | 136 | poolableConnectionFactory.setDefaultReadOnly(false); |
103 | 137 | poolableConnectionFactory.setDefaultAutoCommit(true); |
| 138 | + if (poolConfig.maxConnLifetime() != null) { |
| 139 | + poolableConnectionFactory.setMaxConnLifetimeMillis(poolConfig.maxConnLifetime().toMillis()); |
| 140 | + } |
| 141 | + poolableConnectionFactory.setConnectionInitSql(poolConfig.connectionInitSqls()); |
104 | 142 | // Note: We intentionally do NOT call setDefaultTransactionIsolation() here. |
105 | 143 | // PostgreSQL defaults to READ_COMMITTED, which is what we want. Setting it explicitly |
106 | 144 | // causes DBCP2 to execute "SHOW TRANSACTION ISOLATION LEVEL" on every connection borrow |
107 | 145 | // (via PgConnection.getTransactionIsolation()), adding unnecessary overhead. |
108 | 146 | poolableConnectionFactory.setPoolStatements(false); |
109 | 147 | } |
110 | 148 |
|
111 | | - private AbandonedConfig getAbandonedConfig(final ConnectionPoolConfig poolConfig) { |
112 | | - final AbandonedConfig abandonedConfig = new AbandonedConfig(); |
113 | | - abandonedConfig.setLogAbandoned(true); |
114 | | - abandonedConfig.setRemoveAbandonedOnBorrow(true); |
115 | | - abandonedConfig.setRequireFullStackTrace(true); |
116 | | - abandonedConfig.setRemoveAbandonedTimeout(poolConfig.connectionSurrenderTimeout()); |
117 | | - return abandonedConfig; |
118 | | - } |
119 | | - |
120 | 149 | private int getIdleCount(final int percent, final int maxConnections) { |
121 | 150 | if (percent < 0) { |
122 | 151 | return maxConnections; |
|
0 commit comments