Merge pull request #27294 from meistermeier

* gh-27294:
  Polish "Use server version from database in Neo4j health details"
  Use server version from database in Neo4j health details

Closes gh-27294
This commit is contained in:
Andy Wilkinson
2021-07-14 17:53:46 +01:00
7 changed files with 90 additions and 42 deletions

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2012-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.neo4j;
import org.neo4j.driver.Record;
import org.neo4j.driver.summary.ResultSummary;
/**
* Health details for a Neo4j server.
*
* @author Andy Wilkinson
*/
class Neo4jHealthDetails {
private final Record record;
private final ResultSummary summary;
Neo4jHealthDetails(Record record, ResultSummary summary) {
this.record = record;
this.summary = summary;
}
String getVersion() {
return this.record.get("version").asString();
}
String getEdition() {
return this.record.get("edition").asString();
}
ResultSummary getSummary() {
return this.summary;
}
}

View File

@@ -33,15 +33,14 @@ class Neo4jHealthDetailsHandler {
/**
* Add health details for the specified {@link ResultSummary} and {@code edition}.
* @param builder the {@link Builder} to use
* @param edition the edition of the server
* @param resultSummary server information
* @param healthDetails the health details of the server
*/
@SuppressWarnings("deprecation")
void addHealthDetails(Builder builder, String edition, ResultSummary resultSummary) {
ServerInfo serverInfo = resultSummary.server();
builder.up().withDetail("server", serverInfo.version() + "@" + serverInfo.address()).withDetail("edition",
edition);
DatabaseInfo databaseInfo = resultSummary.database();
void addHealthDetails(Builder builder, Neo4jHealthDetails healthDetails) {
ResultSummary summary = healthDetails.getSummary();
ServerInfo serverInfo = summary.server();
builder.up().withDetail("server", healthDetails.getVersion() + "@" + serverInfo.address()).withDetail("edition",
healthDetails.getEdition());
DatabaseInfo databaseInfo = summary.database();
if (StringUtils.hasText(databaseInfo.name())) {
builder.withDetail("database", databaseInfo.name());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@@ -20,6 +20,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.neo4j.driver.AccessMode;
import org.neo4j.driver.Driver;
import org.neo4j.driver.Record;
import org.neo4j.driver.Result;
import org.neo4j.driver.Session;
import org.neo4j.driver.SessionConfig;
@@ -46,7 +47,7 @@ public class Neo4jHealthIndicator extends AbstractHealthIndicator {
/**
* The Cypher statement used to verify Neo4j is up.
*/
static final String CYPHER = "CALL dbms.components() YIELD name, edition WHERE name = 'Neo4j Kernel' RETURN edition";
static final String CYPHER = "CALL dbms.components() YIELD versions, name, edition WHERE name = 'Neo4j Kernel' RETURN edition, versions[0] as version";
/**
* Message logged before retrying a health check.
@@ -91,9 +92,9 @@ public class Neo4jHealthIndicator extends AbstractHealthIndicator {
// all possible workloads
try (Session session = this.driver.session(DEFAULT_SESSION_CONFIG)) {
Result result = session.run(CYPHER);
String edition = result.single().get("edition").asString();
Record record = result.single();
ResultSummary resultSummary = result.consume();
this.healthDetailsHandler.addHealthDetails(builder, edition, resultSummary);
this.healthDetailsHandler.addHealthDetails(builder, new Neo4jHealthDetails(record, resultSummary));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@@ -22,9 +22,7 @@ import org.neo4j.driver.Driver;
import org.neo4j.driver.exceptions.SessionExpiredException;
import org.neo4j.driver.reactive.RxResult;
import org.neo4j.driver.reactive.RxSession;
import org.neo4j.driver.summary.ResultSummary;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
import reactor.util.retry.Retry;
import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator;
@@ -57,19 +55,19 @@ public final class Neo4jReactiveHealthIndicator extends AbstractReactiveHealthIn
return runHealthCheckQuery()
.doOnError(SessionExpiredException.class,
(e) -> logger.warn(Neo4jHealthIndicator.MESSAGE_SESSION_EXPIRED))
.retryWhen(Retry.max(1).filter(SessionExpiredException.class::isInstance)).map((result) -> {
this.healthDetailsHandler.addHealthDetails(builder, result.getT1(), result.getT2());
.retryWhen(Retry.max(1).filter(SessionExpiredException.class::isInstance)).map((healthDetails) -> {
this.healthDetailsHandler.addHealthDetails(builder, healthDetails);
return builder.build();
});
}
Mono<Tuple2<String, ResultSummary>> runHealthCheckQuery() {
Mono<Neo4jHealthDetails> runHealthCheckQuery() {
// We use WRITE here to make sure UP is returned for a server that supports
// all possible workloads
return Mono.using(() -> this.driver.rxSession(Neo4jHealthIndicator.DEFAULT_SESSION_CONFIG), (session) -> {
RxResult result = session.run(Neo4jHealthIndicator.CYPHER);
return Mono.from(result.records()).map((record) -> record.get("edition").asString())
.zipWhen((edition) -> Mono.from(result.consume()));
return Mono.from(result.records()).zipWhen((record) -> Mono.from(result.consume()))
.map((tuple) -> new Neo4jHealthDetails(tuple.getT1(), tuple.getT2()));
}, RxSession::close);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@@ -51,8 +51,8 @@ class Neo4jHealthIndicatorTests {
@Test
void neo4jIsUp() {
ResultSummary resultSummary = ResultSummaryMock.createResultSummary("4711", "My Home", "test");
Driver driver = mockDriver(resultSummary, "ultimate collectors edition");
ResultSummary resultSummary = ResultSummaryMock.createResultSummary("My Home", "test");
Driver driver = mockDriver(resultSummary, "4711", "ultimate collectors edition");
Health health = new Neo4jHealthIndicator(driver).health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertThat(health.getDetails()).containsEntry("server", "4711@My Home");
@@ -62,8 +62,8 @@ class Neo4jHealthIndicatorTests {
@Test
void neo4jIsUpWithoutDatabaseName() {
ResultSummary resultSummary = ResultSummaryMock.createResultSummary("4711", "My Home", null);
Driver driver = mockDriver(resultSummary, "some edition");
ResultSummary resultSummary = ResultSummaryMock.createResultSummary("My Home", null);
Driver driver = mockDriver(resultSummary, "4711", "some edition");
Health health = new Neo4jHealthIndicator(driver).health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertThat(health.getDetails()).containsEntry("server", "4711@My Home");
@@ -73,8 +73,8 @@ class Neo4jHealthIndicatorTests {
@Test
void neo4jIsUpWithEmptyDatabaseName() {
ResultSummary resultSummary = ResultSummaryMock.createResultSummary("4711", "My Home", "");
Driver driver = mockDriver(resultSummary, "some edition");
ResultSummary resultSummary = ResultSummaryMock.createResultSummary("My Home", "");
Driver driver = mockDriver(resultSummary, "4711", "some edition");
Health health = new Neo4jHealthIndicator(driver).health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertThat(health.getDetails()).containsEntry("server", "4711@My Home");
@@ -84,9 +84,9 @@ class Neo4jHealthIndicatorTests {
@Test
void neo4jIsUpWithOneSessionExpiredException() {
ResultSummary resultSummary = ResultSummaryMock.createResultSummary("4711", "My Home", "");
ResultSummary resultSummary = ResultSummaryMock.createResultSummary("My Home", "");
Session session = mock(Session.class);
Result statementResult = mockStatementResult(resultSummary, "some edition");
Result statementResult = mockStatementResult(resultSummary, "4711", "some edition");
AtomicInteger count = new AtomicInteger();
given(session.run(anyString())).will((invocation) -> {
if (count.compareAndSet(0, 1)) {
@@ -112,17 +112,18 @@ class Neo4jHealthIndicatorTests {
assertThat(health.getDetails()).containsKeys("error");
}
private Result mockStatementResult(ResultSummary resultSummary, String edition) {
private Result mockStatementResult(ResultSummary resultSummary, String version, String edition) {
Record record = mock(Record.class);
given(record.get("edition")).willReturn(Values.value(edition));
given(record.get("version")).willReturn(Values.value(version));
Result statementResult = mock(Result.class);
given(statementResult.single()).willReturn(record);
given(statementResult.consume()).willReturn(resultSummary);
return statementResult;
}
private Driver mockDriver(ResultSummary resultSummary, String edition) {
Result statementResult = mockStatementResult(resultSummary, edition);
private Driver mockDriver(ResultSummary resultSummary, String version, String edition) {
Result statementResult = mockStatementResult(resultSummary, version, edition);
Session session = mock(Session.class);
given(session.run(anyString())).willReturn(statementResult);
Driver driver = mock(Driver.class);

View File

@@ -51,8 +51,8 @@ class Neo4jReactiveHealthIndicatorTests {
@Test
void neo4jIsUp() {
ResultSummary resultSummary = ResultSummaryMock.createResultSummary("4711", "My Home", "test");
Driver driver = mockDriver(resultSummary, "ultimate collectors edition");
ResultSummary resultSummary = ResultSummaryMock.createResultSummary("My Home", "test");
Driver driver = mockDriver(resultSummary, "4711", "ultimate collectors edition");
Neo4jReactiveHealthIndicator healthIndicator = new Neo4jReactiveHealthIndicator(driver);
healthIndicator.health().as(StepVerifier::create).consumeNextWith((health) -> {
assertThat(health.getStatus()).isEqualTo(Status.UP);
@@ -63,9 +63,9 @@ class Neo4jReactiveHealthIndicatorTests {
@Test
void neo4jIsUpWithOneSessionExpiredException() {
ResultSummary resultSummary = ResultSummaryMock.createResultSummary("4711", "My Home", "");
ResultSummary resultSummary = ResultSummaryMock.createResultSummary("My Home", "");
RxSession session = mock(RxSession.class);
RxResult statementResult = mockStatementResult(resultSummary, "some edition");
RxResult statementResult = mockStatementResult(resultSummary, "4711", "some edition");
AtomicInteger count = new AtomicInteger();
given(session.run(anyString())).will((invocation) -> {
if (count.compareAndSet(0, 1)) {
@@ -95,17 +95,18 @@ class Neo4jReactiveHealthIndicatorTests {
}).verifyComplete();
}
private RxResult mockStatementResult(ResultSummary resultSummary, String edition) {
private RxResult mockStatementResult(ResultSummary resultSummary, String version, String edition) {
Record record = mock(Record.class);
given(record.get("edition")).willReturn(Values.value(edition));
given(record.get("version")).willReturn(Values.value(version));
RxResult statementResult = mock(RxResult.class);
given(statementResult.records()).willReturn(Mono.just(record));
given(statementResult.consume()).willReturn(Mono.just(resultSummary));
return statementResult;
}
private Driver mockDriver(ResultSummary resultSummary, String edition) {
RxResult statementResult = mockStatementResult(resultSummary, edition);
private Driver mockDriver(ResultSummary resultSummary, String version, String edition) {
RxResult statementResult = mockStatementResult(resultSummary, version, edition);
RxSession session = mock(RxSession.class);
given(session.run(anyString())).willReturn(statementResult);
Driver driver = mock(Driver.class);

View File

@@ -33,10 +33,8 @@ final class ResultSummaryMock {
private ResultSummaryMock() {
}
@SuppressWarnings("deprecation")
static ResultSummary createResultSummary(String serverVersion, String serverAddress, String databaseName) {
static ResultSummary createResultSummary(String serverAddress, String databaseName) {
ServerInfo serverInfo = mock(ServerInfo.class);
given(serverInfo.version()).willReturn(serverVersion);
given(serverInfo.address()).willReturn(serverAddress);
DatabaseInfo databaseInfo = mock(DatabaseInfo.class);
given(databaseInfo.name()).willReturn(databaseName);