diff --git a/aggregation-app/build.gradle b/aggregation-app/build.gradle index 7116b316..fd826328 100644 --- a/aggregation-app/build.gradle +++ b/aggregation-app/build.gradle @@ -14,6 +14,7 @@ dependencies { implementation(libs.yaci.store.utxo.starter) implementation(libs.yaci.store.account.starter) + implementation project(':components:healthcheck') implementation project(':aggregates:account') runtimeOnly 'org.postgresql:postgresql' diff --git a/aggregation-app/src/main/resources/application.yml b/aggregation-app/src/main/resources/application.yml index 3cd04458..eb851511 100644 --- a/aggregation-app/src/main/resources/application.yml +++ b/aggregation-app/src/main/resources/application.yml @@ -50,3 +50,24 @@ store: blocks-partition-size: 10 use-virtual-thread-for-batch-processing: false use-virtual-thread-for-event-processing: true + +ledger-sync: + healthcheck: + enabled: ${HEALTH_CHECK_ENABLED:true} + event-time-threshold: ${EVENT_TIME_THRESHOLD_IN_SECOND:600} + block-time-check-enabled: ${BLOCK_TIME_CHECK_ENABLED:true} + block-time-threshold: ${BLOCK_TIME_THRESHOLD_IN_SECOND:180} + +management: + endpoints: + enabled-by-default: false + web: + exposure: + include: "health,prometheus,health-status" + endpoint: + health: + enabled: true + prometheus: + enabled: true + health-status: + enabled: ${HEALTH_CHECK_ENABLED:true} \ No newline at end of file diff --git a/application/build.gradle b/application/build.gradle index e7e27035..0740a784 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -17,6 +17,7 @@ dependencies { implementation project(':components:common') implementation project(':components:consumer-common') implementation project(':components:scheduler') + implementation project(':components:healthcheck') implementation(libs.yaci.store.starter) diff --git a/application/src/main/java/org/cardanofoundation/ledgersync/healthcheck/LedgerSyncHealthEndpoint.java b/application/src/main/java/org/cardanofoundation/ledgersync/healthcheck/LedgerSyncHealthEndpoint.java deleted file mode 100644 index 9fc7580b..00000000 --- a/application/src/main/java/org/cardanofoundation/ledgersync/healthcheck/LedgerSyncHealthEndpoint.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.cardanofoundation.ledgersync.healthcheck; - -import lombok.RequiredArgsConstructor; -import org.cardanofoundation.ledgersync.dto.healthcheck.HealthStatus; -import org.cardanofoundation.ledgersync.service.HealthStatusService; -import org.springframework.boot.actuate.endpoint.annotation.Endpoint; -import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; - -@Endpoint(id = "health-status") -@RequiredArgsConstructor -@Component -public class LedgerSyncHealthEndpoint { - - private final HealthStatusService healthStatusService; - - @ReadOperation - @Bean - public ResponseEntity checkHealthStatus() { - var healthStatus = healthStatusService.getHealthStatus(); - - if (Boolean.FALSE.equals(healthStatus.getIsHealthy())) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(healthStatus); - } - - return ResponseEntity.ok().body(healthStatus); - } -} diff --git a/application/src/main/java/org/cardanofoundation/ledgersync/listeners/BlockEventListener.java b/application/src/main/java/org/cardanofoundation/ledgersync/listeners/BlockEventListener.java index 058c3cdb..eaaf95ef 100644 --- a/application/src/main/java/org/cardanofoundation/ledgersync/listeners/BlockEventListener.java +++ b/application/src/main/java/org/cardanofoundation/ledgersync/listeners/BlockEventListener.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.cardanofoundation.ledgersync.aggregate.AggregatedBlock; +import org.cardanofoundation.ledgersync.healthcheck.service.HealthCheckCachingService; import org.cardanofoundation.ledgersync.repository.BlockRepository; import org.cardanofoundation.ledgersync.service.*; import org.cardanofoundation.ledgersync.service.impl.block.BlockAggregatorServiceImpl; @@ -38,7 +39,6 @@ public class BlockEventListener { private final BlockRepository blockRepository; private final MetricCollectorService metricCollectorService; - private final HealthCheckCachingService healthCheckCachingService; private final AtomicInteger blockCount = new AtomicInteger(0); @Value("${blocks.batch-size}") @@ -95,10 +95,6 @@ public void handleGenesisBlock(GenesisBlockEvent genesisBlockEvent) { } genesisDataService.setupData(genesisHash); - healthCheckCachingService.saveLatestBlockSlot(genesisBlockEvent.getSlot()); - healthCheckCachingService.saveLatestBlockInsertTime(LocalDateTime.now(ZoneOffset.UTC)); - healthCheckCachingService.saveLatestBlockTime( - LocalDateTime.ofInstant(Instant.ofEpochSecond(genesisBlockEvent.getBlockTime()), ZoneId.of("UTC"))); } @EventListener @@ -121,10 +117,6 @@ public void handleRollback(RollbackEvent rollbackEvent) { rollbackService.rollBackFrom(rollbackBlockNo); metricCollectorService.collectRollbackMetric(); blockCount.set(0); - - healthCheckCachingService.saveLatestBlockSlot(rollbackEvent.getRollbackTo().getSlot()); - healthCheckCachingService.saveLatestBlockInsertTime(LocalDateTime.now(ZoneOffset.UTC)); - healthCheckCachingService.saveLatestBlockTime(rollBackBlock.get().getTime().toLocalDateTime()); } private boolean checkIfBlockExists(EventMetadata metadata) { @@ -182,13 +174,6 @@ private void handleAggregateBlock(EventMetadata eventMetadata, AggregatedBlock a int currentBlockCount = blockCount.incrementAndGet(); if (currentBlockCount % batchSize == 0 || lastReceivedTimeElapsed >= commitThreshold || eventMetadata.isSyncMode()) { blockSyncService.startBlockSyncing(); - - healthCheckCachingService.saveLatestBlockInsertTime(LocalDateTime.now(ZoneOffset.UTC)); - healthCheckCachingService.saveLatestBlockTime(LocalDateTime.ofEpochSecond( - eventMetadata.getBlockTime(), 0, ZoneOffset.ofHours(0))); - healthCheckCachingService.saveIsSyncMode(eventMetadata.isSyncMode()); - healthCheckCachingService.saveLatestBlockSlot(eventMetadata.getSlot()); - blockCount.set(0); } diff --git a/application/src/main/java/org/cardanofoundation/ledgersync/service/HealthCheckCachingService.java b/application/src/main/java/org/cardanofoundation/ledgersync/service/HealthCheckCachingService.java deleted file mode 100644 index 72848671..00000000 --- a/application/src/main/java/org/cardanofoundation/ledgersync/service/HealthCheckCachingService.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.cardanofoundation.ledgersync.service; - -import java.time.LocalDateTime; - -public interface HealthCheckCachingService { - - /** - * Cache latest block time - */ - void saveLatestBlockTime(LocalDateTime blockTime); - - /** - * Get the latest block - */ - LocalDateTime getLatestBlockTime(); - - /** - * Cache the time when the most recent block was inserted - */ - void saveLatestBlockInsertTime(LocalDateTime insertTime); - - LocalDateTime getLatestBlockInsertTime(); - - /** - * Cache latest slot no - */ - void saveLatestBlockSlot(Long slot); - - /** - * Get latest slot no - */ - Long getLatestBlockSlot(); - - /** - * Cache the value indicates whether the yaci is crawling with sync mode or not (use Chain-Sync protocol) - */ - void saveIsSyncMode(Boolean isSyncMode); - - /** - * Get the value indicates whether the yaci is crawling with sync mode or not (use Chain-Sync protocol) - */ - Boolean getIsSyncMode(); -} - diff --git a/application/src/main/java/org/cardanofoundation/ledgersync/service/HealthStatusService.java b/application/src/main/java/org/cardanofoundation/ledgersync/service/HealthStatusService.java deleted file mode 100644 index f9a4496d..00000000 --- a/application/src/main/java/org/cardanofoundation/ledgersync/service/HealthStatusService.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.cardanofoundation.ledgersync.service; - - -import org.cardanofoundation.ledgersync.dto.healthcheck.HealthStatus; - -public interface HealthStatusService { - - HealthStatus getHealthStatus(); -} diff --git a/application/src/main/java/org/cardanofoundation/ledgersync/service/impl/HealthStatusCachingServiceImpl.java b/application/src/main/java/org/cardanofoundation/ledgersync/service/impl/HealthStatusCachingServiceImpl.java deleted file mode 100644 index ed865e00..00000000 --- a/application/src/main/java/org/cardanofoundation/ledgersync/service/impl/HealthStatusCachingServiceImpl.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.cardanofoundation.ledgersync.service.impl; - -import jakarta.annotation.PostConstruct; -import org.cardanofoundation.ledgersync.service.HealthCheckCachingService; -import org.springframework.stereotype.Service; - -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; - -@Service -public class HealthStatusCachingServiceImpl implements HealthCheckCachingService { - private LocalDateTime latestBlockTime; - private LocalDateTime latestBlockInsertTime; - private final AtomicLong latestBlockSlot = new AtomicLong(); - private final AtomicBoolean isSyncMode = new AtomicBoolean(); - - @PostConstruct - void init() { - latestBlockInsertTime = LocalDateTime.now(ZoneOffset.UTC); - latestBlockSlot.set(-10L); // dummy value - isSyncMode.set(Boolean.FALSE); - } - - @Override - public void saveLatestBlockTime(LocalDateTime blockTime) { - latestBlockTime = blockTime; - } - - @Override - public LocalDateTime getLatestBlockTime() { - return latestBlockTime; - } - - @Override - public void saveLatestBlockInsertTime(LocalDateTime insertTime) { - latestBlockInsertTime = insertTime; - } - - @Override - public LocalDateTime getLatestBlockInsertTime() { - return latestBlockInsertTime; - } - - @Override - public void saveLatestBlockSlot(Long slot) { - latestBlockSlot.set(slot); - } - - @Override - public Long getLatestBlockSlot() { - return latestBlockSlot.get(); - } - - @Override - public void saveIsSyncMode(Boolean value) { - isSyncMode.set(value); - } - - @Override - public Boolean getIsSyncMode() { - return isSyncMode.get(); - } -} diff --git a/application/src/main/java/org/cardanofoundation/ledgersync/service/impl/HealthStatusServiceImpl.java b/application/src/main/java/org/cardanofoundation/ledgersync/service/impl/HealthStatusServiceImpl.java deleted file mode 100644 index 24c4bc3c..00000000 --- a/application/src/main/java/org/cardanofoundation/ledgersync/service/impl/HealthStatusServiceImpl.java +++ /dev/null @@ -1,158 +0,0 @@ -package org.cardanofoundation.ledgersync.service.impl; - -import com.bloxbean.cardano.yaci.store.common.config.StoreProperties; -import com.bloxbean.cardano.yaci.store.core.service.CursorService; -import com.bloxbean.cardano.yaci.store.core.service.HealthService; -import lombok.RequiredArgsConstructor; -import org.cardanofoundation.ledgersync.dto.healthcheck.HealthStatus; -import org.cardanofoundation.ledgersync.dto.healthcheck.Message; -import org.cardanofoundation.ledgersync.service.HealthCheckCachingService; -import org.cardanofoundation.ledgersync.service.HealthStatusService; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.temporal.ChronoUnit; -import java.util.Objects; - -@Service -@RequiredArgsConstructor -public class HealthStatusServiceImpl implements HealthStatusService { - - private final HealthCheckCachingService healthCheckCachingService; - private final HealthService healthService; - private final CursorService cursorService; - private final StoreProperties storeProperties; - - @Value("${ledger-sync.healthcheck.block-time-threshold}") - private Long blockTimeThresholdInSecond; - - @Value("${ledger-sync.healthcheck.non-batching-inserted-time-threshold}") - private Long nonBatchingInsertedTimeThresholdInSecond; - - @Value("${ledger-sync.healthcheck.batching-inserted-time-threshold}") - private Long batchingInsertedTimeThresholdInSecond; - - @Value("${ledger-sync.healthcheck.keepalive-time-threshold}") - private Long keepAliveResponseTimeThresholdInSecond; - - @Value("${blocks.batch-size}") - private Integer batchSize; - - @Override - public HealthStatus getHealthStatus() { - final LocalDateTime latestBlockInsertTime = healthCheckCachingService.getLatestBlockInsertTime(); - final LocalDateTime latestBlockTime = healthCheckCachingService.getLatestBlockTime(); - final long stopSlot = storeProperties.getSyncStopSlot(); - final Long insertedTimeThresholdInSecond = batchSize == 1 || healthCheckCachingService.getIsSyncMode().equals(Boolean.TRUE) ? - nonBatchingInsertedTimeThresholdInSecond : batchingInsertedTimeThresholdInSecond; - - boolean isHealthy = true; - Message message = Message.SYNCING_BUT_NOT_READY; - - if (stopSlot > 0) { - return getHealthStatusWhenStopSlotIsSet(stopSlot); - } - - if (Objects.isNull(latestBlockTime)) { // this latestBlockTime is only != null after a block is successfully inserted into the database - if (isOutOfThreshold(insertedTimeThresholdInSecond, latestBlockInsertTime)) { - if (isConnectionToNodeHealthy()) { - message = Message.CONNECTION_HEALTHY_BUT_BLOCK_CONSUMING_NOT_HEALTHY; - } else { - message = Message.IS_NOT_SYNCING; - } - - return HealthStatus.builder() - .isHealthy(false) - .messageDesc(message.getDesc()) - .messageCode(message.getCode()) - .latestBlockInsertTime(latestBlockInsertTime) - .hasStopSlot(true) - .build(); - } - } else { - if (!isConnectionToNodeHealthy()) { - isHealthy = false; - message = Message.IS_NOT_SYNCING; - } else if (isOutOfThreshold(insertedTimeThresholdInSecond, latestBlockInsertTime)) { - isHealthy = false; - message = Message.CONNECTION_HEALTHY_BUT_BLOCK_CONSUMING_NOT_HEALTHY; - } - } - - if (isHealthy && latestBlockTime != null && !isOutOfThreshold(blockTimeThresholdInSecond, latestBlockTime)) { - message = Message.READY_TO_SERVE; - } - - return HealthStatus.builder() - .isHealthy(isHealthy) - .messageCode(message.getCode()) - .messageDesc(message.getDesc()) - .latestBlockInsertTime(latestBlockInsertTime) - .hasStopSlot(false) - .build(); - } - - private HealthStatus getHealthStatusWhenStopSlotIsSet(Long stopSlot) { - final LocalDateTime latestBlockInsertTime = healthCheckCachingService.getLatestBlockInsertTime(); - final Long latestSlotNo = healthCheckCachingService.getLatestBlockSlot(); - final LocalDateTime latestBlockTime = healthCheckCachingService.getLatestBlockTime(); - final Long insertedTimeThresholdInSecond = batchSize == 1 || healthCheckCachingService.getIsSyncMode().equals(Boolean.TRUE) ? - nonBatchingInsertedTimeThresholdInSecond : batchingInsertedTimeThresholdInSecond; - - boolean isHealthy = true; - Message message = Message.SYNCING_BUT_NOT_READY; - - if (Objects.isNull(latestBlockTime)) { - var recentCursor = cursorService.getCursor(); - if (recentCursor.isPresent() && recentCursor.get().getSlot() >= stopSlot) { - message = Message.SYNCING_HAS_FINISHED; - } - - if (isOutOfThreshold(insertedTimeThresholdInSecond, latestBlockInsertTime)) { - isHealthy = false; - if (!isConnectionToNodeHealthy()) { - message = Message.IS_NOT_SYNCING; - } else { - message = Message.CONNECTION_HEALTHY_BUT_BLOCK_CONSUMING_NOT_HEALTHY; - } - } - } else { - if (!isConnectionToNodeHealthy()) { - isHealthy = false; - message = Message.IS_NOT_SYNCING; - } else if (isOutOfThreshold(insertedTimeThresholdInSecond, latestBlockInsertTime)) { - isHealthy = false; - message = Message.CONNECTION_HEALTHY_BUT_BLOCK_CONSUMING_NOT_HEALTHY; - } - } - - if (isHealthy && latestSlotNo >= stopSlot) { - message = Message.SYNCING_HAS_FINISHED; - } - - return HealthStatus.builder() - .isHealthy(isHealthy) - .messageCode(message.getCode()) - .messageDesc(message.getDesc()) - .latestBlockInsertTime(latestBlockInsertTime) - .hasStopSlot(true) - .build(); - } - - private boolean isOutOfThreshold(Long threshold, LocalDateTime time) { - long value = ChronoUnit.SECONDS.between(time, LocalDateTime.now(ZoneOffset.UTC)); - return threshold <= value; - } - - private LocalDateTime getLastKeepAliveResponseTime() { - return Instant.ofEpochMilli(healthService.getHealthStatus().getLastKeepAliveResponseTime()).atZone(ZoneId.of("UTC")).toLocalDateTime(); - } - - private boolean isConnectionToNodeHealthy() { - return healthService.getHealthStatus().isConnectionAlive() && !isOutOfThreshold(keepAliveResponseTimeThresholdInSecond, getLastKeepAliveResponseTime()); - } -} diff --git a/application/src/main/resources/config/application.yaml b/application/src/main/resources/config/application.yaml index 6a2ca0aa..96c80cd7 100755 --- a/application/src/main/resources/config/application.yaml +++ b/application/src/main/resources/config/application.yaml @@ -84,10 +84,10 @@ ledger-sync: initial-delay: ${POOL_OFFLINE_DATA_INIT_DELAY:20000} enabled: ${SCHEDULER_ENABLED:true} healthcheck: - block-time-threshold: ${BLOCK_TIME_THRESHOLD_IN_SECOND:240} - non-batching-inserted-time-threshold: ${NON_BATCHING_INSERTED_TIME_THRESHOLD_IN_SECOND:240} - batching-inserted-time-threshold: ${BATCHING_INSERTED_TIME_THRESHOLD_IN_SECOND:900} - keepalive-time-threshold: ${KEEPALIVE_TIME_THRESHOLD_IN_SECOND:1200} + enabled: ${HEALTH_CHECK_ENABLED:true} + event-time-threshold: ${EVENT_TIME_THRESHOLD_IN_SECOND:900} + block-time-check-enabled: ${BLOCK_TIME_CHECK_ENABLED:true} + block-time-threshold: ${BLOCK_TIME_THRESHOLD_IN_SECOND:180} management: endpoints: @@ -101,4 +101,4 @@ management: prometheus: enabled: true health-status: - enabled: true \ No newline at end of file + enabled: ${HEALTH_CHECK_ENABLED:true} \ No newline at end of file diff --git a/components/healthcheck/build.gradle b/components/healthcheck/build.gradle new file mode 100644 index 00000000..fe5d7619 --- /dev/null +++ b/components/healthcheck/build.gradle @@ -0,0 +1,17 @@ +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation(libs.yaci.store.events) + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +publishing { + publications { + mavenJava(MavenPublication) { + pom { + name = 'Ledger Sync Health Check' + description = 'Ledger Sync Health Check Module' + } + } + } +} diff --git a/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/HealthCheckConfiguration.java b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/HealthCheckConfiguration.java new file mode 100644 index 00000000..4df8e173 --- /dev/null +++ b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/HealthCheckConfiguration.java @@ -0,0 +1,28 @@ +package org.cardanofoundation.ledgersync.healthcheck; + +import org.cardanofoundation.ledgersync.healthcheck.service.HealthCheckCachingService; +import org.cardanofoundation.ledgersync.healthcheck.service.HealthStatusService; +import org.cardanofoundation.ledgersync.healthcheck.service.impl.HealthStatusServiceImpl; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnProperty(name = {"ledgersync.healthcheck.enabled"}, + havingValue = "true" +) +@EnableConfigurationProperties(HealthCheckProperties.class) +@ComponentScan(basePackages = {"org.cardanofoundation.ledgersync.healthcheck"}) +public class HealthCheckConfiguration { + + @Bean + @ConditionalOnMissingBean + public HealthStatusService healthStatusService(HealthCheckCachingService healthCheckCachingService, + HealthCheckProperties healthCheckProperties) { + return new HealthStatusServiceImpl(healthCheckCachingService, healthCheckProperties); + } + +} diff --git a/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/HealthCheckProperties.java b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/HealthCheckProperties.java new file mode 100644 index 00000000..4dcb6aee --- /dev/null +++ b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/HealthCheckProperties.java @@ -0,0 +1,16 @@ +package org.cardanofoundation.ledgersync.healthcheck; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Getter +@Setter +@ConfigurationProperties(prefix = "ledger-sync.healthcheck", ignoreUnknownFields = true) +public class HealthCheckProperties { + private boolean enabled = false; + private long eventTimeThreshold = 240; + private boolean blockTimeCheckEnabled = false; + private long blockTimeThreshold = 180; + private long stopSlot; +} diff --git a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/healthcheck/StreamerHealthEndpoint.java b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/endpoint/HealthStatusEndpoint.java similarity index 62% rename from streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/healthcheck/StreamerHealthEndpoint.java rename to components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/endpoint/HealthStatusEndpoint.java index 92684f83..5f35353b 100644 --- a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/healthcheck/StreamerHealthEndpoint.java +++ b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/endpoint/HealthStatusEndpoint.java @@ -1,17 +1,20 @@ -package org.cardanofoundation.ledgersync.streamer.healthcheck; +package org.cardanofoundation.ledgersync.healthcheck.endpoint; import lombok.RequiredArgsConstructor; -import org.cardanofoundation.ledgersync.streamer.service.HealthStatusService; +import org.cardanofoundation.ledgersync.healthcheck.model.HealthStatus; +import org.cardanofoundation.ledgersync.healthcheck.service.HealthStatusService; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @Endpoint(id = "health-status") @RequiredArgsConstructor +@ConditionalOnExpression("${ledgersync.healthcheck.enabled:true}") @Component -public class StreamerHealthEndpoint { +public class HealthStatusEndpoint { private final HealthStatusService healthStatusService; @ReadOperation @@ -19,7 +22,7 @@ public class StreamerHealthEndpoint { public ResponseEntity checkHealthStatus() { var healthStatus = healthStatusService.getHealthStatus(); - if (Boolean.TRUE.equals(healthStatus.isHealthy)) { + if (Boolean.TRUE.equals(healthStatus.getIsHealthy())) { return ResponseEntity.ok().body(healthStatus); } diff --git a/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/listener/BlockListener.java b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/listener/BlockListener.java new file mode 100644 index 00000000..3e7b984a --- /dev/null +++ b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/listener/BlockListener.java @@ -0,0 +1,73 @@ +package org.cardanofoundation.ledgersync.healthcheck.listener; + +import com.bloxbean.cardano.yaci.store.events.*; +import com.bloxbean.cardano.yaci.store.events.internal.BatchBlocksProcessedEvent; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.cardanofoundation.ledgersync.healthcheck.service.HealthCheckCachingService; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +@RequiredArgsConstructor +@Slf4j +@Component +@ConditionalOnExpression("${ledgersync.healthcheck.enabled:true}") +public class BlockListener { + private final HealthCheckCachingService healthCheckCachingService; + + @EventListener + public void handleRollback(RollbackEvent rollbackEvent) { + healthCheckCachingService.saveLatestEventTime(LocalDateTime.now(ZoneOffset.UTC)); + } + + @EventListener + public void handleBlockHeader(BlockHeaderEvent blockHeaderEvent) { + healthCheckCachingService.saveLatestEventTime(LocalDateTime.now(ZoneOffset.UTC)); + + if (!blockHeaderEvent.getMetadata().isParallelMode()) { + healthCheckCachingService.saveLatestSlotNo(blockHeaderEvent.getMetadata().getBlock()); + healthCheckCachingService.saveLatestBlockTime(LocalDateTime.ofEpochSecond(blockHeaderEvent.getMetadata().getBlockTime(), + 0, ZoneOffset.ofHours(0))); + } + } + + @EventListener + public void handleGenesisBlock(GenesisBlockEvent genesisBlockEvent) { + healthCheckCachingService.saveLatestEventTime(LocalDateTime.now(ZoneOffset.UTC)); + healthCheckCachingService.saveLatestSlotNo(genesisBlockEvent.getSlot()); + healthCheckCachingService.saveLatestBlockTime(LocalDateTime.ofEpochSecond(genesisBlockEvent.getBlockTime(), + 0, ZoneOffset.ofHours(0))); + } + + @EventListener + public void handleByronBlockEvent(ByronMainBlockEvent byronMainBlockEvent) { + healthCheckCachingService.saveLatestEventTime(LocalDateTime.now(ZoneOffset.UTC)); + + if (!byronMainBlockEvent.getMetadata().isParallelMode()) { + healthCheckCachingService.saveLatestSlotNo(byronMainBlockEvent.getMetadata().getSlot()); + healthCheckCachingService.saveLatestBlockTime(LocalDateTime.ofEpochSecond(byronMainBlockEvent.getMetadata().getBlockTime(), + 0, ZoneOffset.ofHours(0))); + } + } + + @EventListener + public void handleByronEbBlock(ByronEbBlockEvent byronEbBlockEvent) { + healthCheckCachingService.saveLatestEventTime(LocalDateTime.now(ZoneOffset.UTC)); + healthCheckCachingService.saveLatestSlotNo(byronEbBlockEvent.getMetadata().getSlot()); + healthCheckCachingService.saveLatestBlockTime(LocalDateTime.ofEpochSecond(byronEbBlockEvent.getMetadata().getBlockTime(), + 0, ZoneOffset.ofHours(0))); + } + + @EventListener + // for parallel mode + public void handleBatchBlocksProcessedEvent(BatchBlocksProcessedEvent batchBlocksProcessedEvent) { + healthCheckCachingService.saveLatestEventTime(LocalDateTime.now(ZoneOffset.UTC)); + healthCheckCachingService.saveLatestSlotNo(batchBlocksProcessedEvent.getMetadata().getSlot()); + healthCheckCachingService.saveLatestBlockTime(LocalDateTime.ofEpochSecond(batchBlocksProcessedEvent.getMetadata().getBlockTime(), + 0, ZoneOffset.ofHours(0))); + } +} diff --git a/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/model/HealthStatus.java b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/model/HealthStatus.java new file mode 100644 index 00000000..3103651a --- /dev/null +++ b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/model/HealthStatus.java @@ -0,0 +1,22 @@ +package org.cardanofoundation.ledgersync.healthcheck.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +@AllArgsConstructor +public class HealthStatus { + Boolean isHealthy; + String messageCode; + String messageDesc; + + public HealthStatus(Boolean isHealthy, Message message) { + this.isHealthy = isHealthy; + messageCode = message.getCode(); + messageDesc = message.getDesc(); + } +} diff --git a/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/model/Message.java b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/model/Message.java new file mode 100644 index 00000000..0c85a130 --- /dev/null +++ b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/model/Message.java @@ -0,0 +1,19 @@ +package org.cardanofoundation.ledgersync.healthcheck.model; + +import lombok.Getter; + +@Getter +public enum Message { + IS_GOOD("IS_GOOD", "The last time received a block event that is within the threshold"), + IS_BAD("IS_BAD", "The last time received a block event that is beyond the threshold"), + STOP_SLOT_HAS_REACHED("STOP_SLOT_HAS_REACHED", "Stop slot has been reached"), + BLOCK_HAS_REACHED_TIP("BLOCK_HAS_REACHED_TIP", "Block is the tip or near the tip"); + + private final String code; + private final String desc; + + Message(String code, String desc) { + this.code = code; + this.desc = desc; + } +} diff --git a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/service/HealthCheckCachingService.java b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/service/HealthCheckCachingService.java similarity index 63% rename from streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/service/HealthCheckCachingService.java rename to components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/service/HealthCheckCachingService.java index 146c7611..9459d5eb 100644 --- a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/service/HealthCheckCachingService.java +++ b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/service/HealthCheckCachingService.java @@ -1,4 +1,4 @@ -package org.cardanofoundation.ledgersync.streamer.service; +package org.cardanofoundation.ledgersync.healthcheck.service; import java.time.LocalDateTime; @@ -17,4 +17,11 @@ public interface HealthCheckCachingService { void saveLatestEventTime(LocalDateTime insertTime); LocalDateTime getLatestEventTime(); + + void saveLatestBlockTime(LocalDateTime blockTime); + + /** + * Cache the most recent block time + */ + LocalDateTime getLatestBlockTime(); } diff --git a/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/service/HealthStatusService.java b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/service/HealthStatusService.java new file mode 100644 index 00000000..1e103bb0 --- /dev/null +++ b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/service/HealthStatusService.java @@ -0,0 +1,8 @@ +package org.cardanofoundation.ledgersync.healthcheck.service; + + +import org.cardanofoundation.ledgersync.healthcheck.model.HealthStatus; + +public interface HealthStatusService { + HealthStatus getHealthStatus(); +} diff --git a/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/service/impl/HealthCheckCachingServiceImpl.java b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/service/impl/HealthCheckCachingServiceImpl.java new file mode 100644 index 00000000..60eae06f --- /dev/null +++ b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/service/impl/HealthCheckCachingServiceImpl.java @@ -0,0 +1,45 @@ +package org.cardanofoundation.ledgersync.healthcheck.service.impl; + +import org.cardanofoundation.ledgersync.healthcheck.service.HealthCheckCachingService; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.concurrent.atomic.AtomicLong; + +@Service +public class HealthCheckCachingServiceImpl implements HealthCheckCachingService { + private LocalDateTime latestBlockTime; + private LocalDateTime latestEventTime = LocalDateTime.now(ZoneOffset.UTC); + private final AtomicLong latestSlot = new AtomicLong(); + + @Override + public void saveLatestSlotNo(Long slotNo) { + latestSlot.set(slotNo); + } + + @Override + public Long getLatestSlotNo() { + return latestSlot.get(); + } + + @Override + public void saveLatestEventTime(LocalDateTime eventTime) { + latestEventTime = eventTime; + } + + @Override + public LocalDateTime getLatestEventTime() { + return latestEventTime; + } + + @Override + public void saveLatestBlockTime(LocalDateTime blockTime) { + latestBlockTime = blockTime; + } + + @Override + public LocalDateTime getLatestBlockTime() { + return latestBlockTime; + } +} diff --git a/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/service/impl/HealthStatusServiceImpl.java b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/service/impl/HealthStatusServiceImpl.java new file mode 100644 index 00000000..f8165ba3 --- /dev/null +++ b/components/healthcheck/src/main/java/org/cardanofoundation/ledgersync/healthcheck/service/impl/HealthStatusServiceImpl.java @@ -0,0 +1,45 @@ +package org.cardanofoundation.ledgersync.healthcheck.service.impl; + +import lombok.RequiredArgsConstructor; +import org.cardanofoundation.ledgersync.healthcheck.HealthCheckProperties; +import org.cardanofoundation.ledgersync.healthcheck.model.HealthStatus; +import org.cardanofoundation.ledgersync.healthcheck.model.Message; +import org.cardanofoundation.ledgersync.healthcheck.service.HealthCheckCachingService; +import org.cardanofoundation.ledgersync.healthcheck.service.HealthStatusService; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.temporal.ChronoUnit; + +@RequiredArgsConstructor +public class HealthStatusServiceImpl implements HealthStatusService { + private final HealthCheckCachingService healthCheckCachingService; + private final HealthCheckProperties healthCheckProperties; + + @Override + public HealthStatus getHealthStatus() { + final var latestEventTime = healthCheckCachingService.getLatestEventTime(); + final var latestSlotNo = healthCheckCachingService.getLatestSlotNo(); + final var latestBlockTime = healthCheckCachingService.getLatestBlockTime(); + final var stopSlot = healthCheckProperties.getStopSlot(); + + if (!isInThreshold(healthCheckProperties.getEventTimeThreshold(), latestEventTime)) { + if (stopSlot > 0 && latestSlotNo >= stopSlot) { + return new HealthStatus(Boolean.TRUE, Message.STOP_SLOT_HAS_REACHED); + } + return new HealthStatus(Boolean.FALSE, Message.IS_BAD); + } + + if (healthCheckProperties.isBlockTimeCheckEnabled() && latestBlockTime != null && + isInThreshold(healthCheckProperties.getBlockTimeThreshold(), latestBlockTime)) { + return new HealthStatus(Boolean.TRUE, Message.BLOCK_HAS_REACHED_TIP); + } + + return new HealthStatus(Boolean.TRUE, Message.IS_GOOD); + } + + private boolean isInThreshold(Long threshold, LocalDateTime time) { + long value = ChronoUnit.SECONDS.between(time, LocalDateTime.now(ZoneOffset.UTC)); + return value <= threshold; + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b448a711..9e99f303 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,6 +3,7 @@ yaci-store-starter="com.bloxbean.cardano:yaci-store-spring-boot-starter:0.1.0-rc yaci-store-utxo-starter="com.bloxbean.cardano:yaci-store-utxo-spring-boot-starter:0.1.0-rc2-5f1c04d-SNAPSHOT" yaci-store-account-starter="com.bloxbean.cardano:yaci-store-account-spring-boot-starter:0.1.0-rc2-5f1c04d-SNAPSHOT" yaci-store-remote-starter="com.bloxbean.cardano:yaci-store-remote-spring-boot-starter:0.1.0-rc2-5f1c04d-SNAPSHOT" +yaci-store-events="com.bloxbean.cardano:yaci-store-events:0.1.0-rc2-5f1c04d-SNAPSHOT" cardano-client-lib="com.bloxbean.cardano:cardano-client-lib:0.5.1" snakeyaml="org.yaml:snakeyaml:2.0" diff --git a/settings.gradle b/settings.gradle index 37a4f1aa..c368393e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,6 +10,7 @@ rootProject.name = 'ledger-sync' include 'components:common' include 'components:consumer-common' include 'components:scheduler' +include 'components:healthcheck' include 'aggregates:account' include 'application' diff --git a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/healthcheck/HealthStatus.java b/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/healthcheck/HealthStatus.java deleted file mode 100644 index cc8c7f55..00000000 --- a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/healthcheck/HealthStatus.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.cardanofoundation.ledgersync.streamer.healthcheck; - -import lombok.Builder; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@Builder -public class HealthStatus { - Boolean isHealthy; - Boolean hasStopSlot; - String messageCode; - String messageDesc; -} diff --git a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/healthcheck/Message.java b/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/healthcheck/Message.java deleted file mode 100644 index 3c9defae..00000000 --- a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/healthcheck/Message.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.cardanofoundation.ledgersync.streamer.healthcheck; - -import lombok.Getter; - -@Getter -public enum Message { - IS_CRAWLING("IS_CRAWLING", "Data is being crawled"), - IS_NOT_CRAWLING("IS_NOT_CRAWLING", "Data is not being crawled"), - STOP_SLOT_HAS_REACHED("STOP_SLOT_HAS_REACHED", "Stop slot has been reached"), - CONNECTION_HEALTHY_BUT_DATA_CRAWLING_NOT_HEALTHY("CONNECTION_HEALTHY_BUT_DATA_CRAWLING_NOT_HEALTHY", - "Connection to node is healthy but data crawling is not healthy"); - - private final String code; - private final String desc; - - Message(String code, String desc) { - this.code = code; - this.desc = desc; - } -} diff --git a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/listener/BlockEventListener.java b/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/listener/BlockEventListener.java deleted file mode 100644 index cb681c8e..00000000 --- a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/listener/BlockEventListener.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.cardanofoundation.ledgersync.streamer.listener; - -import com.bloxbean.cardano.yaci.store.events.*; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.cardanofoundation.ledgersync.streamer.service.HealthCheckCachingService; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -import java.time.LocalDateTime; -import java.time.ZoneOffset; - -@Component -@RequiredArgsConstructor -@Slf4j -public class BlockEventListener { - private final HealthCheckCachingService healthCheckCachingService; - - @EventListener - @Transactional - public void handleRollback(RollbackEvent rollbackEvent) { - healthCheckCachingService.saveLatestEventTime(LocalDateTime.now(ZoneOffset.UTC)); - healthCheckCachingService.saveLatestSlotNo(rollbackEvent.getRollbackTo().getSlot()); - } - - @EventListener - @Transactional - public void handleBlockHeader(BlockHeaderEvent blockHeaderEvent) { - healthCheckCachingService.saveLatestEventTime(LocalDateTime.now(ZoneOffset.UTC)); - healthCheckCachingService.saveLatestSlotNo(blockHeaderEvent.getBlockHeader().getHeaderBody().getSlot()); - } - - @EventListener - @Transactional - public void handleGenesisBlock(GenesisBlockEvent genesisBlockEvent) { - healthCheckCachingService.saveLatestEventTime(LocalDateTime.now(ZoneOffset.UTC)); - healthCheckCachingService.saveLatestSlotNo(genesisBlockEvent.getSlot()); - } - - @EventListener - @Transactional - public void handleByronBlockEvent(ByronMainBlockEvent byronMainBlockEvent) { - healthCheckCachingService.saveLatestEventTime(LocalDateTime.now(ZoneOffset.UTC)); - healthCheckCachingService.saveLatestSlotNo(byronMainBlockEvent.getMetadata().getSlot()); - } - - @EventListener - @Transactional - public void handleByronEbBlock(ByronEbBlockEvent byronEbBlockEvent) { - healthCheckCachingService.saveLatestEventTime(LocalDateTime.now(ZoneOffset.UTC)); - healthCheckCachingService.saveLatestSlotNo(byronEbBlockEvent.getMetadata().getSlot()); - } -} diff --git a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/service/HealthStatusService.java b/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/service/HealthStatusService.java deleted file mode 100644 index f4d762c5..00000000 --- a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/service/HealthStatusService.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.cardanofoundation.ledgersync.streamer.service; - -import org.cardanofoundation.ledgersync.streamer.healthcheck.HealthStatus; - -public interface HealthStatusService { - HealthStatus getHealthStatus(); -} diff --git a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/service/impl/HealthCheckCachingServiceImpl.java b/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/service/impl/HealthCheckCachingServiceImpl.java deleted file mode 100644 index bf76e23d..00000000 --- a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/service/impl/HealthCheckCachingServiceImpl.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.cardanofoundation.ledgersync.streamer.service.impl; - -import jakarta.annotation.PostConstruct; -import lombok.RequiredArgsConstructor; -import org.cardanofoundation.ledgersync.streamer.service.HealthCheckCachingService; -import org.springframework.stereotype.Service; - -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.util.concurrent.atomic.AtomicLong; - -@Service -@RequiredArgsConstructor -public class HealthCheckCachingServiceImpl implements HealthCheckCachingService { - private final AtomicLong latestSlotNo = new AtomicLong(); - private LocalDateTime latestEventTime; - - @PostConstruct - void init() { - latestSlotNo.set(-10); // dummy value - latestEventTime = LocalDateTime.now(ZoneOffset.UTC); - } - - @Override - public void saveLatestSlotNo(Long slotNo) { - latestSlotNo.set(slotNo); - } - - @Override - public Long getLatestSlotNo() { - return latestSlotNo.get(); - } - - @Override - public void saveLatestEventTime(LocalDateTime eventTime) { - latestEventTime = eventTime; - } - - @Override - public LocalDateTime getLatestEventTime() { - return latestEventTime; - } -} diff --git a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/service/impl/HealthStatusServiceImpl.java b/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/service/impl/HealthStatusServiceImpl.java deleted file mode 100644 index 9272b246..00000000 --- a/streamer-app/src/main/java/org/cardanofoundation/ledgersync/streamer/service/impl/HealthStatusServiceImpl.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.cardanofoundation.ledgersync.streamer.service.impl; - -import com.bloxbean.cardano.yaci.store.common.config.StoreProperties; -import com.bloxbean.cardano.yaci.store.core.service.HealthService; -import lombok.RequiredArgsConstructor; -import org.cardanofoundation.ledgersync.streamer.healthcheck.HealthStatus; -import org.cardanofoundation.ledgersync.streamer.healthcheck.Message; -import org.cardanofoundation.ledgersync.streamer.service.HealthCheckCachingService; -import org.cardanofoundation.ledgersync.streamer.service.HealthStatusService; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.temporal.ChronoUnit; - -@Service -@RequiredArgsConstructor -public class HealthStatusServiceImpl implements HealthStatusService { - private final HealthCheckCachingService healthCheckCachingService; - private final StoreProperties storeProperties; - private final HealthService healthService; - - @Value("${streamer.healthcheck.event-time-threshold}") - private long publishedTimeThreshold; - - @Value("${streamer.healthcheck.keepalive-time-threshold}") - private Long keepAliveResponseTimeThresholdInSecond; - - @Override - public HealthStatus getHealthStatus() { - final LocalDateTime latestEventTime = healthCheckCachingService.getLatestEventTime(); - final Long latestSlotNo = healthCheckCachingService.getLatestSlotNo(); - boolean hasStopSlot = storeProperties.getSyncStopSlot() != 0; - boolean isHealthy = true; - Message message = Message.IS_CRAWLING; - - if (isOutOfThreshold(publishedTimeThreshold, latestEventTime)) { - isHealthy = false; - if (isConnectionToNodeHealthy()) { - message = Message.CONNECTION_HEALTHY_BUT_DATA_CRAWLING_NOT_HEALTHY; - } else { - message = Message.IS_NOT_CRAWLING; - } - } - - if (isHealthy && latestSlotNo != null && hasStopSlot && latestSlotNo >= storeProperties.getSyncStopSlot()){ - message = Message.STOP_SLOT_HAS_REACHED; - } - - return HealthStatus.builder() - .isHealthy(isHealthy) - .hasStopSlot(hasStopSlot) - .messageCode(message.getCode()) - .messageDesc(message.getDesc()) - .build(); - } - - private boolean isOutOfThreshold(Long threshold, LocalDateTime time) { - long value = ChronoUnit.SECONDS.between(time, LocalDateTime.now(ZoneOffset.UTC)); - return threshold <= value; - } - - private LocalDateTime getLastKeepAliveResponseTime() { - return Instant.ofEpochMilli(healthService.getHealthStatus().getLastKeepAliveResponseTime()).atZone(ZoneId.of("UTC")).toLocalDateTime(); - } - - private boolean isConnectionToNodeHealthy() { - return healthService.getHealthStatus().isConnectionAlive() && - !isOutOfThreshold(keepAliveResponseTimeThresholdInSecond, getLastKeepAliveResponseTime()); - } -} diff --git a/streamer-app/src/main/resources/application.yml b/streamer-app/src/main/resources/application.yml index 9c388ed9..e671be14 100644 --- a/streamer-app/src/main/resources/application.yml +++ b/streamer-app/src/main/resources/application.yml @@ -19,10 +19,12 @@ spring: max.request.size: 200000000 default-binder: ${scs_binder} -streamer: +ledger-sync: healthcheck: - event-time-threshold: ${EVENT_TIME_THRESHOLD_IN_SECOND:120} - keepalive-time-threshold: ${KEEPALIVE_TIME_THRESHOLD_IN_SECOND:120} + enabled: ${HEALTH_CHECK_ENABLED:true} + event-time-threshold: ${EVENT_TIME_THRESHOLD_IN_SECOND:300} + block-time-check-enabled: ${BLOCK_TIME_CHECK_ENABLED:false} + block-time-threshold: ${BLOCK_TIME_THRESHOLD_IN_SECOND:180} management: endpoints: @@ -36,4 +38,4 @@ management: prometheus: enabled: true health-status: - enabled: true + enabled: ${HEALTH_CHECK_ENABLED:true}