How does DataSourceHealthIndicator actually verify the database, and what happens with multiple DataSources?
answer
- borrow Connection + validation query
- DatabaseDriver enum → SELECT 1 / DUAL
- fallback Connection.isValid()
- multiple DS → CompositeHealthContributor
- consumes pool conn; blocks up to connection-timeout
basics
~20 sIt borrows a connection from the pool and runs a lightweight validation query (a driver-specific query like SELECT 1, or Connection.isValid() if none is known). Success is UP, an exception is DOWN. With several DataSources it groups them into a composite.
solid answer
~50 sDataSourceHealthIndicator (registered as 'db') obtains a Connection from the pooled DataSource and executes a validation query to confirm the database is reachable and responsive. Spring Boot picks the query from the DatabaseDriver enum based on the JDBC product (e.g. SELECT 1, or SELECT 1 FROM DUAL for Oracle); if it can't determine one it falls back to JDBC's Connection.isValid(). A returned row → UP with details like the database product and the query used; any SQLException → DOWN with the error. With multiple DataSource beans, auto-configuration creates a CompositeHealthContributor so each datasource reports under its own name. Key gotchas: the check consumes a pool connection each scrape (frequent scrapes + tiny pools can cause contention), and it depends on pool timeouts — a hung DB can make the health check block up to the connection-acquire timeout, so tune those.
code
java · 16 lines// Conceptually, what the indicator does each scrape (simplified):
try (Connection conn = dataSource.getConnection()) {
// Query chosen from DatabaseDriver, e.g. "SELECT 1"; else conn.isValid(timeout)
String query = databaseDriver.getValidationQuery(); // may be null
if (query != null) {
try (Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(query)) {
rs.next(); // success -> UP
}
} else {
boolean ok = conn.isValid(1); // JDBC 4 fallback
}
} // SQLException -> Status.DOWN
// Keep the check fast-failing so a dead DB doesn't hang the probe:
// spring.datasource.hikari.connection-timeout=2000 (ms)go deeper
Know it runs a cheap query like SELECT 1 and reports UP/DOWN.
Explain the DatabaseDriver-derived query, the isValid() fallback, and that it uses the pool.
Discuss connection consumption, blocking on connection-timeout, and CompositeHealthContributor for multiple DataSources.
Design pool/timeout/probe-group interplay so the check fails fast and DB blips don't cause restart storms or LB flapping.
## What it does **DataSourceHealthIndicator** verifies that the application's relational database is reachable and answering. It's auto-configured whenever a `javax.sql.DataSource` bean is present, and it registers under the fixed name **`db`**. ## The verification mechanism 1. It **borrows a `Connection`** from the pooled `DataSource` (HikariCP by default). 2. It runs a **validation query** — a trivially cheap statement whose success proves the round-trip works. Spring Boot selects the query from the internal **`DatabaseDriver`** enum by matching the JDBC URL / product name. Examples: - Most databases: `SELECT 1` - Oracle: `SELECT 1 FROM DUAL` - HSQLDB / Informix have their own variants. 3. If no query is known for the driver, it falls back to **`Connection.isValid(timeout)`** (JDBC 4's built-in validity check). 4. Outcome: - Query/isValid succeeds → **`Status.UP`**, with details such as `database` (product name) and `validationQuery`. - A `SQLException` (or any failure) → **`Status.DOWN`**, with the exception surfaced in details (subject to show-details). ## Multiple DataSources When more than one `DataSource` bean exists, `DataSourceHealthContributorAutoConfiguration` wraps them in a **`CompositeHealthContributor`**. Each datasource is checked and reported under its bean name, so `/health` shows a nested structure. This is common in multi-tenant or read/write-split setups. ## Important edge cases and gotchas - **Connection consumption.** Every health scrape borrows and returns a pool connection. With aggressive scrape intervals and a small pool, health checks can compete with real traffic. Consider a dedicated small pool or a longer scrape interval. - **Blocking on a hung DB.** If the database is unreachable, acquiring a connection can block up to the pool's connection-timeout (Hikari default 30s). A slow health endpoint can itself trip orchestrator probe timeouts. Tune `spring.datasource.hikari.connection-timeout` and probe timeouts together. - **DOWN ejects the instance.** A `DOWN` db makes the aggregate `DOWN` → HTTP 503 → removal from the load balancer/readiness. That's usually desired for readiness but can cause thundering-herd failovers if the DB blips. - **Custom validation query.** You can supply your own via configuration on some setups, but the default driver-derived query is preferred for portability. - **routing datasources / lazy proxies** can behave oddly — an `AbstractRoutingDataSource` may resolve to a target that isn't valid outside a request context. ## When to use / tune Keep it in the **readiness** group (an app with a dead DB shouldn't receive traffic), but usually keep it out of **liveness** (a DB blip shouldn't restart the pod). Size pools and timeouts so the check fails fast rather than hanging.
- Your /actuator/health takes 30 seconds to return when the DB is down and Kubernetes keeps killing the pod. What's happening and how do you fix it?The db indicator blocks acquiring a pool connection up to Hikari's connection-timeout (default 30s), so the probe times out. Lower spring.datasource.hikari.connection-timeout, and consider keeping the db check in readiness (not liveness) so a DB outage stops traffic without restarting the pod.
- How does the health check know to use 'SELECT 1 FROM DUAL' for Oracle?Spring Boot matches the JDBC driver/product to its internal DatabaseDriver enum, which carries a per-database validation query. If none is defined it falls back to Connection.isValid().
saying these in an interview costs you the question
- Claiming it runs a heavy query or SELECT COUNT(*) against a real table
- Saying it opens a brand-new TCP connection outside the pool (it borrows from the pool)
- Not realizing a hung DB can make the health endpoint block up to the pool connection-timeout