Skip to content

Commit 1843dab

Browse files
authored
Merge pull request #150 from ebean-orm/feature/fix-close-busy-bug
docs: Major update, add configuration-reference.md etc
2 parents 69da13a + e746d82 commit 1843dab

6 files changed

Lines changed: 587 additions & 0 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ This project includes step-by-step guides for common datasource configuration sc
2424
**See [docs/guides/README.md](docs/guides/README.md)** for:
2525
- [Creating DataSource pools](docs/guides/create-datasource-pool.md) — basic pools, read-only pools, Kubernetes deployment, AWS Lambda, and configuration reference
2626
- [Connection Validation Best Practices](docs/guides/connection-validation-best-practices.md) — how to configure heartbeat validation and why explicit heartbeatSql is rarely needed
27+
- [Configuration Reference](docs/guides/configuration-reference.md) — every builder setting, its default, and the equivalent property key for external configuration
28+
- [Troubleshooting Connection Leaks & Pool Exhaustion](docs/guides/troubleshooting-connection-leaks.md) — diagnosing pool exhaustion, leaks, and common warnings
29+
- [Monitoring Pool Metrics](docs/guides/monitoring-pool-metrics.md) — reading PoolStatus metrics, pool state, and outage alerts
2730
- [AWS Aurora with dual DataSources](docs/guides/aws-aurora-read-write-split.md) — read-write endpoint separation with Ebean ORM integration
2831
- Instructions for AI agents to discover and follow these guides
2932

docs/guides/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ Step-by-step guides covering common scenarios for creating and configuring conne
1010
|-------|-------------|
1111
| [Create a DataSource Pool](create-datasource-pool.md) | Basic pool creation, read-only pools, Kubernetes configuration, AWS Lambda setup, and configuration reference |
1212
| [Connection Validation Best Practices](connection-validation-best-practices.md) | How connection validation works; when and how to configure heartbeat validation; why explicit heartbeatSql is rarely needed |
13+
| [Configuration Reference](configuration-reference.md) | Complete reference of every builder setting, its default, and the equivalent property key for file-based / external configuration |
14+
| [Troubleshooting Connection Leaks & Pool Exhaustion](troubleshooting-connection-leaks.md) | Diagnose `ConnectionPoolExhaustedException`, connection leaks, captureStackTrace, leakTimeMinutes, and common warnings |
15+
| [Monitoring Pool Metrics](monitoring-pool-metrics.md) | Read `PoolStatus` metrics, check pool state, and wire up outage alerts and borrow/return listeners |
1316

1417
## AWS Aurora
1518

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# Guide: Configuration Reference
2+
3+
## Purpose
4+
5+
A complete reference of every `DataSourceBuilder` setting, its default value, and the equivalent
6+
property key for file-based / external configuration. Use this alongside the task-focused guides
7+
(pool creation, read-only pools, validation, Aurora) when you need to know exactly what a setting
8+
does or what it defaults to.
9+
10+
---
11+
12+
## Two ways to configure
13+
14+
### 1. Programmatic (builder)
15+
16+
```java
17+
DataSourcePool pool = DataSourcePool.builder()
18+
.name("mypool")
19+
.url("jdbc:postgresql://localhost:5432/myapp")
20+
.username("app_user")
21+
.password("password")
22+
.minConnections(5)
23+
.maxConnections(50)
24+
.build();
25+
```
26+
27+
### 2. Properties (external configuration)
28+
29+
The builder can load settings from `java.util.Properties`. There are three entry points:
30+
31+
```java
32+
Properties props = ...; // loaded from file, env, avaje-config, Spring, etc.
33+
34+
// (a) no prefix: keys are "username", "url", ...
35+
DataSourcePool pool = DataSourcePool.builder().load(props).build();
36+
37+
// (b) custom prefix: keys are "my-db.username", "my-db.url", ...
38+
DataSourcePool pool = DataSourcePool.builder().load(props, "my-db").build();
39+
40+
// (c) "datasource.<poolName>." prefix: keys are "datasource.hr.username", ...
41+
DataSourcePool pool = DataSourcePool.builder().loadSettings(props, "hr").build();
42+
```
43+
44+
Example properties file using the `loadSettings` convention with pool name `hr`:
45+
46+
```properties
47+
datasource.hr.username=app_user
48+
datasource.hr.password=password
49+
datasource.hr.url=jdbc:postgresql://localhost:5432/myapp
50+
datasource.hr.minConnections=5
51+
datasource.hr.maxConnections=50
52+
datasource.hr.leakTimeMinutes=30
53+
```
54+
55+
> When used through **Ebean ORM**, these `datasource.<name>.*` properties are typically placed in
56+
> `application.yaml` / `application.properties` and loaded for you via avaje-config.
57+
58+
Property keys are matched case-insensitively.
59+
60+
---
61+
62+
## Connection settings
63+
64+
| Builder method | Property key | Default | Description |
65+
|----------------|--------------|---------|-------------|
66+
| `url(String)` | `url` (or `databaseUrl`) || JDBC URL. |
67+
| `username(String)` | `username` || Database username. |
68+
| `password(String)` | `password` || Database password. |
69+
| `readOnlyUrl(String)` | `readOnlyUrl` || Optional separate URL for read-only connections. |
70+
| `driver(...)` / `driver(String)` | `driver` (or `databaseDriver`) | auto from URL | JDBC driver class / instance. |
71+
| `schema(String)` | `schema` | driver default | Default schema applied to connections. |
72+
| `catalog(String)` | `catalog` | driver default | Default catalog applied to connections. |
73+
| `isolationLevel(int)` | `isolationLevel` | `READ_COMMITTED` | Transaction isolation level. Property accepts names e.g. `READ_COMMITTED`. |
74+
| `autoCommit(boolean)` | `autoCommit` | `false` | Auto-commit mode for pooled connections. |
75+
| `readOnly(boolean)` | `readOnly` | `false` | Mark connections read-only (optimises read workloads). |
76+
| `applicationName(String)` | `applicationName` || Application name reported to the driver where supported. |
77+
| `clientInfo(Properties)` | `clientInfo` || Client info properties (semicolon separated `key=value` in properties form). |
78+
| `customProperties(Map)` / `addProperty(...)` | `customProperties` || Extra JDBC driver connection properties (semicolon separated `key=value` in properties form). |
79+
| `initSql(List<String>)` | `initSql` || SQL run on each new connection (semicolon separated statements in properties form). See per-connection init below. |
80+
81+
## Pool sizing
82+
83+
| Builder method | Property key | Default | Description |
84+
|----------------|--------------|---------|-------------|
85+
| `minConnections(int)` | `minConnections` | `2` | Minimum connections maintained in the pool. |
86+
| `initialConnections(int)` | `initialConnections` | = `minConnections` | Connections created on startup. Set higher than min for smooth warm-up (Kubernetes). |
87+
| `maxConnections(int)` | `maxConnections` | `200` | Maximum connections. Threads block (up to `waitTimeout`) when this is reached. |
88+
89+
## Timeouts, trimming and ageing
90+
91+
| Builder method | Property key | Default | Description |
92+
|----------------|--------------|---------|-------------|
93+
| `waitTimeoutMillis(int)` | `waitTimeout` | `1000` | Millis a thread waits for a free connection once the pool is at max before throwing `ConnectionPoolExhaustedException`. |
94+
| `maxInactiveTimeSecs(int)` | `maxInactiveTimeSecs` | `300` | Idle seconds after which a free connection can be trimmed back towards `minConnections`. |
95+
| `maxAgeMinutes(int)` | `maxAgeMinutes` | `0` (unlimited) | Maximum age of a connection before it is trimmed regardless of activity. |
96+
| `trimPoolFreqSecs(int)` | `trimPoolFreqSecs` | `59` | How often the background trim check runs. |
97+
98+
## Health checks / heartbeat
99+
100+
| Builder method | Property key | Default | Description |
101+
|----------------|--------------|---------|-------------|
102+
| `validateOnHeartbeat(boolean)` | `validateOnHeartbeat` | `true` (`false` in AWS Lambda) | Enable the background heartbeat that validates the pool. |
103+
| `heartbeatFreqSecs(int)` | *(builder only)* | `30` | How often the heartbeat runs. |
104+
| `heartbeatTimeoutSeconds(int)` | `heartbeatTimeoutSeconds` | `30` | Query timeout for the heartbeat validation. |
105+
| `heartbeatSql(String)` | `heartbeatSql` | `Connection.isValid()` / platform default | Explicit validation SQL. Rarely needed — see the validation guide. |
106+
| `heartbeatMaxPoolExhaustedCount(int)` | *(builder only)* | `10` | Consecutive heartbeat pool-exhaustion detections before the pool is reset (leak recovery). |
107+
108+
See [Connection Validation Best Practices](connection-validation-best-practices.md) for details.
109+
110+
## Leak detection / diagnostics
111+
112+
| Builder method | Property key | Default | Description |
113+
|----------------|--------------|---------|-------------|
114+
| `leakTimeMinutes(int)` | `leakTimeMinutes` | `30` | A busy (checked-out) connection older than this is treated as a leak and force-closed during a pool reset. |
115+
| `captureStackTrace(boolean)` | `captureStackTrace` | `false` | Capture the stack trace when a connection is obtained, to locate leaks. Has a performance cost. |
116+
| `maxStackTraceSize(int)` | `maxStackTraceSize` | `5` | Number of stack frames reported for busy connections. |
117+
118+
See [Troubleshooting Connection Leaks & Pool Exhaustion](troubleshooting-connection-leaks.md).
119+
120+
## Statement caching
121+
122+
| Builder method | Property key | Default | Description |
123+
|----------------|--------------|---------|-------------|
124+
| `pstmtCacheSize(int)` | `pstmtCacheSize` | `300` | `PreparedStatement` cache size, per connection. |
125+
| `cstmtCacheSize(int)` | `cstmtCacheSize` | `20` | `CallableStatement` cache size, per connection. |
126+
127+
## Lifecycle / startup
128+
129+
| Builder method | Property key | Default | Description |
130+
|----------------|--------------|---------|-------------|
131+
| `failOnStart(boolean)` | `failOnStart` | `true` | When `false`, the pool starts even if the database is unavailable (it recovers later via heartbeat). |
132+
| `offline(boolean)` | `offline` | `false` | Start the pool offline (no connections created until `online()`). |
133+
| `shutdownOnJvmExit(boolean)` | `shutdownOnJvmExit` | `false` | Register a JVM shutdown hook to close the pool on exit. |
134+
| `enforceCleanClose(boolean)` | `enforceCleanClose` | `false` | Throw if a dirty (uncommitted) connection is closed. Recommended in tests. See [issue #116](https://github.com/ebean-orm/ebean-datasource/issues/116). |
135+
136+
## Hooks and extension points
137+
138+
| Builder method | Property key | Description |
139+
|----------------|--------------|-------------|
140+
| `connectionInitializer(NewConnectionInitializer)` | *(builder only)* | Hook called when each new connection is created (`preInitialize` / `postInitialize`). |
141+
| `defaultConnectionInitializer(NewConnectionInitializer)` | *(builder only)* | Fallback initializer used only if one is not otherwise set. |
142+
| `listener(DataSourcePoolListener)` | *(builder only)* | Callbacks on borrow (`onAfterBorrowConnection`) and return (`onBeforeReturnConnection`). |
143+
| `poolListener(String)` | `poolListener` | Class name of a `DataSourcePoolListener` to instantiate. |
144+
| `alert(DataSourceAlert)` | *(builder only)* | Callbacks for `dataSourceUp` / `dataSourceDown` (outage alerting). See [Monitoring](monitoring-pool-metrics.md). |
145+
146+
## Per-connection initialization
147+
148+
Use `initSql` for simple statements, or a `NewConnectionInitializer` for programmatic control. This
149+
is the right place to set things like a Postgres `search_path` or a per-session `statement_timeout`.
150+
151+
```java
152+
DataSourcePool pool = DataSourcePool.builder()
153+
.name("mypool")
154+
.url("jdbc:postgresql://localhost:5432/myapp")
155+
.username("app_user")
156+
.password("password")
157+
.initSql(List.of("set search_path to app, public"))
158+
.connectionInitializer(new NewConnectionInitializer() {
159+
@Override
160+
public void postInitialize(Connection connection) {
161+
try (Statement st = connection.createStatement()) {
162+
st.execute("set statement_timeout to '30s'");
163+
} catch (SQLException e) {
164+
throw new IllegalStateException(e);
165+
}
166+
}
167+
})
168+
.build();
169+
```
170+
171+
---
172+
173+
## Deprecated `setXxx` methods
174+
175+
Many settings historically used a `setXxx` name (e.g. `setMinConnections`). These remain for
176+
backwards compatibility but are deprecated — prefer the fluent forms shown above
177+
(`minConnections`, `maxConnections`, `heartbeatFreqSecs`, etc.).
178+
179+
---
180+
181+
## Next Steps
182+
183+
- [Create a DataSource Pool](create-datasource-pool.md)
184+
- [Connection Validation Best Practices](connection-validation-best-practices.md)
185+
- [Troubleshooting Connection Leaks & Pool Exhaustion](troubleshooting-connection-leaks.md)
186+
- [Monitoring Pool Metrics](monitoring-pool-metrics.md)
187+
- Full Javadoc: [io.ebean:ebean-datasource](https://javadoc.io/doc/io.ebean/ebean-datasource)

docs/guides/create-datasource-pool.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ DataSourcePool pool = DataSourcePool.builder()
164164

165165
## Configuration Reference
166166

167+
> For the **complete** list of settings, defaults, and property keys see the
168+
> [Configuration Reference](configuration-reference.md). The table below covers the most common
169+
> settings.
170+
167171
### Common Settings
168172

169173
| Setting | Default | Purpose |
@@ -217,6 +221,9 @@ DataSourcePool pool = DataSourcePool.builder()
217221

218222
## Next Steps
219223

224+
- Read the [Configuration Reference](configuration-reference.md) for all available settings and property keys
225+
- [Troubleshooting Connection Leaks & Pool Exhaustion](troubleshooting-connection-leaks.md)
226+
- [Monitoring Pool Metrics](monitoring-pool-metrics.md)
220227
- Read the [README](../../README.md) for more information about the connection pool
221228
- Check the [ebean-datasource GitHub repository](https://github.com/ebean-orm/ebean-datasource) for latest updates
222229
- Consult the [DataSourceBuilder API documentation](https://javadoc.io/doc/io.ebean/ebean-datasource) for all available configuration options
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# Guide: Monitoring Pool Metrics
2+
3+
## Purpose
4+
5+
This guide explains how to observe the health of a `DataSourcePool` at runtime: reading pool metrics
6+
via `PoolStatus`, checking online/up state, and receiving outage notifications via `DataSourceAlert`.
7+
8+
---
9+
10+
## Reading pool metrics with `PoolStatus`
11+
12+
Call `status(reset)` on the pool to get a point-in-time snapshot:
13+
14+
```java
15+
PoolStatus status = pool.status(false); // false = read without resetting counters
16+
17+
System.out.printf(
18+
"min=%d max=%d free=%d busy=%d waiting=%d highWaterMark=%d hitCount=%d waitCount=%d%n",
19+
status.minSize(), status.maxSize(), status.free(), status.busy(),
20+
status.waiting(), status.highWaterMark(), status.hitCount(), status.waitCount());
21+
```
22+
23+
### Metric meanings
24+
25+
| Metric | Meaning |
26+
|--------|---------|
27+
| `minSize()` | Configured minimum connections. |
28+
| `maxSize()` | Configured maximum connections. |
29+
| `free()` | Connections currently idle and available. |
30+
| `busy()` | Connections currently checked out (in use). |
31+
| `size()` | `free() + busy()` — total connections in the pool. |
32+
| `waiting()` | Threads currently blocked waiting for a connection. |
33+
| `highWaterMark()` | Peak `busy()` value seen since the last reset. |
34+
| `hitCount()` | Number of times a connection was requested from the pool. |
35+
| `waitCount()` | Number of times a thread had to wait (pool was full). |
36+
| `totalAcquireMicros()` | Total time spent acquiring connections (micros). |
37+
| `totalWaitMicros()` | Total time threads spent waiting when the pool was full (micros). |
38+
| `maxAcquireMicros()` | Slowest single acquire (micros). |
39+
| `meanAcquireNanos()` | Mean acquire time (nanos) — typically in the ballpark of ~150 nanos. |
40+
41+
### What to watch for
42+
43+
- **`highWaterMark()` approaching `maxSize()`** — the pool is close to exhaustion; consider raising
44+
`maxConnections` or investigate a leak (see
45+
[Troubleshooting Connection Leaks & Pool Exhaustion](troubleshooting-connection-leaks.md)).
46+
- **Non-zero / rising `waitCount()` and `waiting()`** — threads are blocking on the pool; demand
47+
exceeds capacity.
48+
- **`busy()` not dropping when traffic falls** — a likely connection leak.
49+
- **High `maxAcquireMicros()` / `meanAcquireNanos()`** — acquiring is slow, often a sign of
50+
contention or repeated validation/recreation.
51+
52+
### Resetting counters for periodic sampling
53+
54+
Pass `true` to reset the cumulative counters (`highWaterMark`, `hitCount`, `waitCount`, acquire/wait
55+
times) after reading. This is ideal for emitting metrics on a fixed interval:
56+
57+
```java
58+
// every 60s, e.g. from a scheduled task
59+
PoolStatus status = pool.status(true); // read and reset, so the next window starts clean
60+
meterRegistry.gauge("db.pool.busy", status.busy());
61+
meterRegistry.gauge("db.pool.free", status.free());
62+
meterRegistry.gauge("db.pool.highWaterMark", status.highWaterMark());
63+
meterRegistry.gauge("db.pool.waitCount", status.waitCount());
64+
```
65+
66+
---
67+
68+
## Checking pool state
69+
70+
```java
71+
pool.isOnline(); // true once the pool has been brought online (connections created)
72+
pool.isDataSourceUp(); // false if the pool has detected the database is down
73+
pool.size(); // current number of connections (free + busy)
74+
75+
SQLException reason = pool.dataSourceDownReason(); // non-null when the pool is down
76+
```
77+
78+
- `isOnline()` reflects the lifecycle state (`online()` / `offline()`).
79+
- `isDataSourceUp()` reflects connectivity health detected by the heartbeat.
80+
81+
---
82+
83+
## Outage notifications with `DataSourceAlert`
84+
85+
Register a `DataSourceAlert` to be notified when the pool detects the database has gone down and when
86+
it recovers. This is useful for wiring pool health into your alerting/paging system.
87+
88+
```java
89+
DataSourceAlert alert = new DataSourceAlert() {
90+
@Override
91+
public void dataSourceDown(DataSource dataSource, SQLException reason) {
92+
// e.g. send to your alerting system / increment a metric
93+
log.error("Database pool is DOWN", reason);
94+
}
95+
96+
@Override
97+
public void dataSourceUp(DataSource dataSource) {
98+
log.warn("Database pool is back UP");
99+
}
100+
};
101+
102+
DataSourcePool pool = DataSourcePool.builder()
103+
.name("mypool")
104+
// ... url/username/password ...
105+
.alert(alert)
106+
.build();
107+
```
108+
109+
The pool also logs these transitions itself:
110+
111+
```
112+
FATAL: DataSource [mypool] is down or has network error!!!
113+
RESOLVED FATAL: DataSource [mypool] is back up!
114+
```
115+
116+
---
117+
118+
## Borrow/return hooks with `DataSourcePoolListener`
119+
120+
For per-connection observation (e.g. timing, tracing, auditing), register a listener:
121+
122+
```java
123+
DataSourcePoolListener listener = new DataSourcePoolListener() {
124+
@Override
125+
public void onAfterBorrowConnection(Connection connection) {
126+
// called when a connection is handed to the application
127+
}
128+
129+
@Override
130+
public void onBeforeReturnConnection(Connection connection) {
131+
// called just before a connection is returned to the pool
132+
}
133+
};
134+
135+
DataSourcePool pool = DataSourcePool.builder()
136+
.name("mypool")
137+
// ...
138+
.listener(listener)
139+
.build();
140+
```
141+
142+
> Keep listener callbacks fast — they run on the borrow/return path of every connection.
143+
144+
---
145+
146+
## Putting it together: a simple health check
147+
148+
```java
149+
PoolStatus status = pool.status(false);
150+
boolean healthy =
151+
pool.isDataSourceUp()
152+
&& status.waiting() == 0
153+
&& status.busy() < status.maxSize();
154+
155+
if (!healthy) {
156+
log.warn("DB pool degraded: up={} busy={}/{} waiting={}",
157+
pool.isDataSourceUp(), status.busy(), status.maxSize(), status.waiting());
158+
}
159+
```
160+
161+
---
162+
163+
## Next Steps
164+
165+
- [Troubleshooting Connection Leaks & Pool Exhaustion](troubleshooting-connection-leaks.md)
166+
- [Configuration Reference](configuration-reference.md)
167+
- [Connection Validation Best Practices](connection-validation-best-practices.md)

0 commit comments

Comments
 (0)