Skip to content

Commit

Permalink
Use query-less datasource validation by default
Browse files Browse the repository at this point in the history
This commit changes DataSourceHealthIndicator to validate the connection
rather than issuing a query to the database. If a custom validation
query is specified, it uses that as before.

Closes spring-projectsgh-17582
  • Loading branch information
snicoll committed Feb 18, 2020
1 parent c53d4f2 commit 16111f1
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 30 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,7 +28,7 @@
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.boot.actuate.health.Status;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.IncorrectResultSetColumnCountException;
import org.springframework.jdbc.core.ConnectionCallback;
Expand All @@ -51,8 +51,6 @@
*/
public class DataSourceHealthIndicator extends AbstractHealthIndicator implements InitializingBean {

private static final String DEFAULT_QUERY = "SELECT 1";

private DataSource dataSource;

private String query;
Expand Down Expand Up @@ -104,17 +102,27 @@ protected void doHealthCheck(Health.Builder builder) throws Exception {
}

private void doDataSourceHealthCheck(Health.Builder builder) throws Exception {
String product = getProduct();
builder.up().withDetail("database", product);
String validationQuery = getValidationQuery(product);
try {
// Avoid calling getObject as it breaks MySQL on Java 7
List<Object> results = this.jdbcTemplate.query(validationQuery, new SingleColumnRowMapper());
Object result = DataAccessUtils.requiredSingleResult(results);
builder.withDetail("result", result);
builder.up().withDetail("database", getProduct());
String validationQuery = this.query;
if (StringUtils.hasText(validationQuery)) {
try {
// Avoid calling getObject as it breaks MySQL on Java 7
List<Object> results = this.jdbcTemplate.query(validationQuery, new SingleColumnRowMapper());
Object result = DataAccessUtils.requiredSingleResult(results);
builder.withDetail("result", result);
}
finally {
builder.withDetail("validationQuery", validationQuery);
}
}
finally {
builder.withDetail("validationQuery", validationQuery);
else {
try {
boolean valid = isConnectionValid();
builder.status((valid) ? Status.UP : Status.DOWN);
}
finally {
builder.withDetail("validationQuery", "isValid()");
}
}
}

Expand All @@ -126,16 +134,12 @@ private String getProduct(Connection connection) throws SQLException {
return connection.getMetaData().getDatabaseProductName();
}

protected String getValidationQuery(String product) {
String query = this.query;
if (!StringUtils.hasText(query)) {
DatabaseDriver specific = DatabaseDriver.fromProductName(product);
query = specific.getValidationQuery();
}
if (!StringUtils.hasText(query)) {
query = DEFAULT_QUERY;
}
return query;
private Boolean isConnectionValid() {
return this.jdbcTemplate.execute((ConnectionCallback<Boolean>) this::isConnectionValid);
}

private Boolean isConnectionValid(Connection connection) throws SQLException {
return connection.isValid(0);
}

/**
Expand All @@ -149,8 +153,8 @@ public void setDataSource(DataSource dataSource) {

/**
* Set a specific validation query to use to validate a connection. If none is set, a
* default validation query is used.
* @param query the query
* validation based on {@link Connection#isValid(int)} is used.
* @param query the validation query to use
*/
public void setQuery(String query) {
this.query = query;
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,6 +17,7 @@
package org.springframework.boot.actuate.jdbc;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

Expand All @@ -26,7 +27,6 @@

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SingleConnectionDataSource;
Expand Down Expand Up @@ -69,8 +69,8 @@ void healthIndicatorWithDefaultSettings() {
this.indicator.setDataSource(this.dataSource);
Health health = this.indicator.health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertThat(health.getDetails()).containsOnly(entry("database", "HSQL Database Engine"), entry("result", 1L),
entry("validationQuery", DatabaseDriver.HSQLDB.getValidationQuery()));
assertThat(health.getDetails()).containsOnly(entry("database", "HSQL Database Engine"),
entry("validationQuery", "isValid()"));
}

@Test
Expand Down Expand Up @@ -109,4 +109,18 @@ void healthIndicatorCloseConnection() throws Exception {
verify(connection, times(2)).close();
}

@Test
void healthIndicatorWithConnectionValidationFailure() throws SQLException {
DataSource dataSource = mock(DataSource.class);
Connection connection = mock(Connection.class);
given(connection.isValid(0)).willReturn(false);
given(connection.getMetaData()).willReturn(this.dataSource.getConnection().getMetaData());
given(dataSource.getConnection()).willReturn(connection);
this.indicator.setDataSource(dataSource);
Health health = this.indicator.health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat(health.getDetails()).containsOnly(entry("database", "HSQL Database Engine"),
entry("validationQuery", "isValid()"));
}

}

0 comments on commit 16111f1

Please sign in to comment.