From dc8d4a58278bb9ab6b8cd501d96668c174be5e3b Mon Sep 17 00:00:00 2001 From: Serafeim Chatzopoulos Date: Wed, 5 Jun 2024 23:58:15 +0300 Subject: [PATCH 1/3] Add health check with actuator --- pom.xml | 6 +++ .../api/config/health/HealthCheck.java | 45 +++++++++++++++++++ .../config/health/SolrStartupHealthCheck.java | 2 + .../api/repositories/SolrRepository.java | 5 +++ .../api/solr/SolrConnectionManager.java | 9 +++- src/main/resources/application.properties | 10 +++++ 6 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 src/main/java/eu/openaire/api/config/health/HealthCheck.java create mode 100644 src/main/java/eu/openaire/api/config/health/SolrStartupHealthCheck.java diff --git a/pom.xml b/pom.xml index cadbd81..724943b 100644 --- a/pom.xml +++ b/pom.xml @@ -164,6 +164,12 @@ 2.0.1.Final + + + org.springframework.boot + spring-boot-starter-actuator + + eu.dnetlib.dhp diff --git a/src/main/java/eu/openaire/api/config/health/HealthCheck.java b/src/main/java/eu/openaire/api/config/health/HealthCheck.java new file mode 100644 index 0000000..e790c7e --- /dev/null +++ b/src/main/java/eu/openaire/api/config/health/HealthCheck.java @@ -0,0 +1,45 @@ +package eu.openaire.api.config; + +import eu.openaire.api.repositories.SolrRepository; +import org.apache.solr.client.solrj.SolrServerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class HealthCheck implements HealthIndicator { + + @Autowired + private SolrRepository solrRepository; + + @Override + public Health health() { + + System.out.println("HEALTH CHECK"); + + // Custom health check logic + boolean isHealthy = checkHealth(); + if (isHealthy) { + return Health.up().withDetail("message", "Successfully pinged Solr instances").build(); + } else { + return Health.down().withDetail("message", "Could not ping Solr instances; please check the logs for details").build(); + } + } + + private boolean checkHealth() { + + // Ping Solr cluster to check health + try { + if (solrRepository.ping().getStatus() != 0) { + return false; + } + } catch (SolrServerException | IOException e) { + return false; + } + + return true; + } +} diff --git a/src/main/java/eu/openaire/api/config/health/SolrStartupHealthCheck.java b/src/main/java/eu/openaire/api/config/health/SolrStartupHealthCheck.java new file mode 100644 index 0000000..6f5c7d5 --- /dev/null +++ b/src/main/java/eu/openaire/api/config/health/SolrStartupHealthCheck.java @@ -0,0 +1,2 @@ +package eu.openaire.api.config.health;public class SolrStartupHealthCheck { +} diff --git a/src/main/java/eu/openaire/api/repositories/SolrRepository.java b/src/main/java/eu/openaire/api/repositories/SolrRepository.java index df1bc31..10ad7a2 100644 --- a/src/main/java/eu/openaire/api/repositories/SolrRepository.java +++ b/src/main/java/eu/openaire/api/repositories/SolrRepository.java @@ -10,6 +10,7 @@ import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrException; import org.springframework.stereotype.Repository; import java.io.IOException; @@ -62,4 +63,8 @@ public class SolrRepository { return solrConnectionManager.getSolrClient().query(query); } + + public void ping() throws SolrServerException, SolrException, IOException { + solrConnectionManager.ping(); + } } diff --git a/src/main/java/eu/openaire/api/solr/SolrConnectionManager.java b/src/main/java/eu/openaire/api/solr/SolrConnectionManager.java index d2db084..92efa3d 100644 --- a/src/main/java/eu/openaire/api/solr/SolrConnectionManager.java +++ b/src/main/java/eu/openaire/api/solr/SolrConnectionManager.java @@ -8,6 +8,7 @@ import org.apache.logging.log4j.Logger; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.CloudSolrClient; +import org.apache.solr.common.SolrException; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -46,10 +47,15 @@ public class SolrConnectionManager { // set the appropriate Solr collection ((CloudSolrClient) solrClient).setDefaultCollection(solrProperties.getCollection()); - log.info("Creating Solr client - Ping response: " + solrClient.ping().toString()); + // uncomment ping below, to crash app on startup if Solr is not available +// this.ping(); } + public void ping() throws IOException, SolrException, SolrServerException { + log.info("Solr ping response: " + solrClient.ping().toString()); + } + @PreDestroy private void closeClient() throws IOException { @@ -58,6 +64,5 @@ public class SolrConnectionManager { if (solrClient != null) { solrClient.close(); } - } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f898942..228d23e 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -12,3 +12,13 @@ logging.level.org.springframework.web=DEBUG #removes 'trace' field from error responses; alternatively remove 'spring-boot-devtools' dependency server.error.include-stacktrace=never +# Enable the health endpoint +management.endpoint.health.enabled=true + +# Expose the health endpoint +management.endpoints.web.exposure.include=health,info + +# Customize health endpoint settings +management.endpoint.health.show-details=always +management.endpoint.health.show-components=always + -- 2.17.1 From 061954cc47233c7cb6992371319c27b8eaf845c3 Mon Sep 17 00:00:00 2001 From: Serafeim Chatzopoulos Date: Thu, 6 Jun 2024 00:59:39 +0300 Subject: [PATCH 2/3] Add Solr health check on startup --- .../api/config/health/HealthCheck.java | 20 ++++++++--- .../config/health/SolrStartupHealthCheck.java | 34 ++++++++++++++++++- .../api/repositories/SolrRepository.java | 6 ++-- .../api/solr/SolrConnectionManager.java | 11 +++--- 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/src/main/java/eu/openaire/api/config/health/HealthCheck.java b/src/main/java/eu/openaire/api/config/health/HealthCheck.java index e790c7e..1b6fd08 100644 --- a/src/main/java/eu/openaire/api/config/health/HealthCheck.java +++ b/src/main/java/eu/openaire/api/config/health/HealthCheck.java @@ -1,7 +1,10 @@ -package eu.openaire.api.config; +package eu.openaire.api.config.health; import eu.openaire.api.repositories.SolrRepository; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.common.SolrException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; @@ -15,17 +18,24 @@ public class HealthCheck implements HealthIndicator { @Autowired private SolrRepository solrRepository; + private final Logger log = LogManager.getLogger(this.getClass()); + + @Override public Health health() { - System.out.println("HEALTH CHECK"); + log.info("Health check triggered"); // Custom health check logic boolean isHealthy = checkHealth(); if (isHealthy) { - return Health.up().withDetail("message", "Successfully pinged Solr instances").build(); + String healthMsg = "Successfully pinged Solr cluster"; + log.info(healthMsg); + return Health.up().withDetail("message", healthMsg).build(); } else { - return Health.down().withDetail("message", "Could not ping Solr instances; please check the logs for details").build(); + String notHealthyMsg = "Could not ping Solr cluster; please check the logs for details"; + log.error(notHealthyMsg); + return Health.down().withDetail("message", notHealthyMsg).build(); } } @@ -36,7 +46,7 @@ public class HealthCheck implements HealthIndicator { if (solrRepository.ping().getStatus() != 0) { return false; } - } catch (SolrServerException | IOException e) { + } catch (SolrException | IOException | SolrServerException e) { return false; } diff --git a/src/main/java/eu/openaire/api/config/health/SolrStartupHealthCheck.java b/src/main/java/eu/openaire/api/config/health/SolrStartupHealthCheck.java index 6f5c7d5..8b45260 100644 --- a/src/main/java/eu/openaire/api/config/health/SolrStartupHealthCheck.java +++ b/src/main/java/eu/openaire/api/config/health/SolrStartupHealthCheck.java @@ -1,2 +1,34 @@ -package eu.openaire.api.config.health;public class SolrStartupHealthCheck { +package eu.openaire.api.config.health; + +import eu.openaire.api.repositories.SolrRepository; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.common.SolrException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class SolrStartupHealthCheck implements ApplicationRunner { + private final Logger log = LogManager.getLogger(this.getClass()); + + @Autowired + private SolrRepository solrRepository; + + @Override + public void run(ApplicationArguments args) throws Exception { + try { + if (solrRepository.ping().getStatus() != 0) { + throw new RuntimeException("Could not ping Solr cluster at startup"); + } + } catch (SolrException | IOException | SolrServerException e) { + throw new RuntimeException("Failed to connect to Solr cluster at startup", e); + } + + log.info("Successfully pinged Solr cluster at startup"); + } } diff --git a/src/main/java/eu/openaire/api/repositories/SolrRepository.java b/src/main/java/eu/openaire/api/repositories/SolrRepository.java index 10ad7a2..7322678 100644 --- a/src/main/java/eu/openaire/api/repositories/SolrRepository.java +++ b/src/main/java/eu/openaire/api/repositories/SolrRepository.java @@ -9,8 +9,8 @@ import org.apache.logging.log4j.Logger; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.client.solrj.response.SolrPingResponse; import org.apache.solr.common.SolrDocument; -import org.apache.solr.common.SolrException; import org.springframework.stereotype.Repository; import java.io.IOException; @@ -64,7 +64,7 @@ public class SolrRepository { } - public void ping() throws SolrServerException, SolrException, IOException { - solrConnectionManager.ping(); + public SolrPingResponse ping() throws SolrServerException, IOException { + return solrConnectionManager.ping(); } } diff --git a/src/main/java/eu/openaire/api/solr/SolrConnectionManager.java b/src/main/java/eu/openaire/api/solr/SolrConnectionManager.java index 92efa3d..052da85 100644 --- a/src/main/java/eu/openaire/api/solr/SolrConnectionManager.java +++ b/src/main/java/eu/openaire/api/solr/SolrConnectionManager.java @@ -8,7 +8,7 @@ import org.apache.logging.log4j.Logger; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.CloudSolrClient; -import org.apache.solr.common.SolrException; +import org.apache.solr.client.solrj.response.SolrPingResponse; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -42,7 +42,10 @@ public class SolrConnectionManager { // create SolrClient with ZooKeeper hosts // TODO: add socket (or connection) timeout? - solrClient = new CloudSolrClient.Builder(zkHosts, Optional.empty()).build(); + solrClient = new CloudSolrClient.Builder(zkHosts, Optional.empty()) + .withConnectionTimeout(15000) + .withSocketTimeout(1000) + .build(); // set the appropriate Solr collection ((CloudSolrClient) solrClient).setDefaultCollection(solrProperties.getCollection()); @@ -52,8 +55,8 @@ public class SolrConnectionManager { } - public void ping() throws IOException, SolrException, SolrServerException { - log.info("Solr ping response: " + solrClient.ping().toString()); + public SolrPingResponse ping() throws IOException, SolrServerException { + return solrClient.ping(); } @PreDestroy -- 2.17.1 From 0c256ea06f2106974e24de9fc83d08c0d45c582a Mon Sep 17 00:00:00 2001 From: Serafeim Chatzopoulos Date: Wed, 12 Jun 2024 13:59:41 +0300 Subject: [PATCH 3/3] Change SolrJ client connection and socket timeouts --- .../api/config/health/SolrStartupHealthCheck.java | 2 +- .../java/eu/openaire/api/solr/SolrConnectionManager.java | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/eu/openaire/api/config/health/SolrStartupHealthCheck.java b/src/main/java/eu/openaire/api/config/health/SolrStartupHealthCheck.java index 8b45260..c55943c 100644 --- a/src/main/java/eu/openaire/api/config/health/SolrStartupHealthCheck.java +++ b/src/main/java/eu/openaire/api/config/health/SolrStartupHealthCheck.java @@ -20,7 +20,7 @@ public class SolrStartupHealthCheck implements ApplicationRunner { private SolrRepository solrRepository; @Override - public void run(ApplicationArguments args) throws Exception { + public void run(ApplicationArguments args) throws RuntimeException { try { if (solrRepository.ping().getStatus() != 0) { throw new RuntimeException("Could not ping Solr cluster at startup"); diff --git a/src/main/java/eu/openaire/api/solr/SolrConnectionManager.java b/src/main/java/eu/openaire/api/solr/SolrConnectionManager.java index 052da85..f69fbb2 100644 --- a/src/main/java/eu/openaire/api/solr/SolrConnectionManager.java +++ b/src/main/java/eu/openaire/api/solr/SolrConnectionManager.java @@ -43,16 +43,15 @@ public class SolrConnectionManager { // create SolrClient with ZooKeeper hosts // TODO: add socket (or connection) timeout? solrClient = new CloudSolrClient.Builder(zkHosts, Optional.empty()) - .withConnectionTimeout(15000) - .withSocketTimeout(1000) + // time to wait when trying to establish a connection + .withConnectionTimeout(3000) + // time to wait for data to be read from the socket + .withSocketTimeout(30000) .build(); // set the appropriate Solr collection ((CloudSolrClient) solrClient).setDefaultCollection(solrProperties.getCollection()); - // uncomment ping below, to crash app on startup if Solr is not available -// this.ping(); - } public SolrPingResponse ping() throws IOException, SolrServerException { -- 2.17.1