11/*
2- * Copyright 2023-2023 IEXEC BLOCKCHAIN TECH
2+ * Copyright 2023-2024 IEXEC BLOCKCHAIN TECH
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
2121import lombok .Getter ;
2222import lombok .extern .slf4j .Slf4j ;
2323import org .springframework .beans .factory .annotation .Autowired ;
24- import org .springframework .beans .factory .annotation .Value ;
2524import org .springframework .boot .actuate .health .Health ;
2625import org .springframework .boot .actuate .health .HealthIndicator ;
2726import org .springframework .boot .actuate .health .Status ;
27+ import org .springframework .boot .context .event .ApplicationReadyEvent ;
2828import org .springframework .context .ApplicationEventPublisher ;
29+ import org .springframework .context .event .EventListener ;
2930import org .springframework .stereotype .Component ;
3031
31- import javax .annotation .PostConstruct ;
3232import java .time .Clock ;
3333import java .time .Duration ;
3434import java .time .LocalDateTime ;
@@ -49,18 +49,14 @@ public class BlockchainConnectionHealthIndicator implements HealthIndicator {
4949 /**
5050 * Number of consecutive failures before declaring this Scheduler is out-of-service.
5151 */
52- private final int outOfServiceThreshold ;
5352 private final ScheduledExecutorService monitoringExecutor ;
5453
5554 /**
56- * Current number of consecutive failures.
55+ * Current number of consecutive failures. Startup value is greater than {@literal 0} to be out-of-service.
5756 */
58- @ Getter
59- private int consecutiveFailures = 0 ;
57+ private int consecutiveFailures = 1 ;
6058 @ Getter
6159 private LocalDateTime firstFailure = null ;
62- @ Getter
63- private boolean outOfService = false ;
6460
6561 /**
6662 * Required for test purposes.
@@ -71,15 +67,11 @@ public class BlockchainConnectionHealthIndicator implements HealthIndicator {
7167 public BlockchainConnectionHealthIndicator (
7268 ApplicationEventPublisher applicationEventPublisher ,
7369 Web3jService web3jService ,
74- ChainConfig chainConfig ,
75- @ Value ("${chain.health.pollingIntervalInBlocks}" ) int pollingIntervalInBlocks ,
76- @ Value ("${chain.health.outOfServiceThreshold}" ) int outOfServiceThreshold ) {
70+ ChainConfig chainConfig ) {
7771 this (
7872 applicationEventPublisher ,
7973 web3jService ,
8074 chainConfig ,
81- pollingIntervalInBlocks ,
82- outOfServiceThreshold ,
8375 Executors .newSingleThreadScheduledExecutor (),
8476 Clock .systemDefaultZone ()
8577 );
@@ -89,28 +81,24 @@ public BlockchainConnectionHealthIndicator(
8981 ApplicationEventPublisher applicationEventPublisher ,
9082 Web3jService web3jService ,
9183 ChainConfig chainConfig ,
92- int pollingIntervalInBlocks ,
93- int outOfServiceThreshold ,
9484 ScheduledExecutorService monitoringExecutor ,
9585 Clock clock ) {
9686 this .applicationEventPublisher = applicationEventPublisher ;
9787 this .web3jService = web3jService ;
98- this .pollingInterval = chainConfig .getBlockTime ().multipliedBy (pollingIntervalInBlocks );
99- this .outOfServiceThreshold = outOfServiceThreshold ;
88+ this .pollingInterval = chainConfig .getBlockTime ();
10089 this .monitoringExecutor = monitoringExecutor ;
10190 this .clock = clock ;
10291 }
10392
104- @ PostConstruct
93+ @ EventListener ( ApplicationReadyEvent . class )
10594 void scheduleMonitoring () {
10695 monitoringExecutor .scheduleAtFixedRate (this ::checkConnection , 0 , pollingInterval .toSeconds (), TimeUnit .SECONDS );
10796 }
10897
10998 /**
11099 * Check blockchain is reachable by retrieving the latest block number.
111100 * <p>
112- * If it isn't, then increment {@link BlockchainConnectionHealthIndicator#consecutiveFailures} counter.
113- * If counter reaches {@link BlockchainConnectionHealthIndicator#outOfServiceThreshold},
101+ * If it isn't, then increment {@link BlockchainConnectionHealthIndicator#consecutiveFailures} counter,
114102 * then this Health Indicator becomes {@link Status#OUT_OF_SERVICE}.
115103 * <p>
116104 * If blockchain is reachable, then reset the counter.
@@ -131,32 +119,20 @@ void checkConnection() {
131119 /**
132120 * Increment the {@link BlockchainConnectionHealthIndicator#consecutiveFailures} counter.
133121 * <p>
134- * If first failure, set the {@link BlockchainConnectionHealthIndicator#firstFailure} to current time.
135- * <p>
136- * If {@link BlockchainConnectionHealthIndicator#outOfServiceThreshold} has been reached for the first time:
122+ * If first failure:
137123 * <ul>
138- * <li> Set {@link BlockchainConnectionHealthIndicator#outOfService} to {@literal true}
139- * <li> Publish a {@link ChainDisconnectedEvent} event.
124+ * <li> Publish a {@link ChainDisconnectedEvent} event
125+ * <li> Set the {@link BlockchainConnectionHealthIndicator#firstFailure} to current time
140126 * </ul>
141127 */
142128 private void connectionFailed () {
143- ++consecutiveFailures ;
144- if (consecutiveFailures >= outOfServiceThreshold ) {
145- log .error ("Blockchain hasn't been accessed for a long period. " +
146- "This Scheduler is now OUT-OF-SERVICE until communication is restored." +
147- " [unavailabilityPeriod:{}]" , pollingInterval .multipliedBy (outOfServiceThreshold ));
148- if (!outOfService ) {
149- outOfService = true ;
150- applicationEventPublisher .publishEvent (new ChainDisconnectedEvent (this ));
151- }
152- } else {
153- if (consecutiveFailures == 1 ) {
154- firstFailure = LocalDateTime .now (clock );
155- }
156- log .warn ("Blockchain is unavailable. Will retry connection." +
157- " [unavailabilityPeriod:{}, nextRetry:{}]" ,
158- pollingInterval .multipliedBy (consecutiveFailures ), pollingInterval );
129+ if (!isOutOfService ()) {
130+ log .error ("Blockchain communication failed, this Scheduler is now OUT-OF-SERVICE until communication is restored." );
131+ log .debug ("Publishing ChainDisconnectedEvent" );
132+ applicationEventPublisher .publishEvent (new ChainDisconnectedEvent (this ));
133+ firstFailure = LocalDateTime .now (clock );
159134 }
135+ ++consecutiveFailures ;
160136 }
161137
162138 /**
@@ -166,27 +142,24 @@ private void connectionFailed() {
166142 * <ul>
167143 * <li>Log a "connection restored" message.
168144 * <li>Reset the {@link BlockchainConnectionHealthIndicator#firstFailure} var to {@code null}
169- * <li>If {@link Status#OUT_OF_SERVICE}, publish a {@link ChainConnectedEvent} event and reset the
170- * {@link BlockchainConnectionHealthIndicator#outOfService} state
145+ * <li>If {@link Status#OUT_OF_SERVICE}, publish a {@link ChainConnectedEvent} event
171146 * </ul>
172147 */
173148 private void connectionSucceeded (long latestBlockNumber ) {
174- if (consecutiveFailures > 0 ) {
149+ if (isOutOfService () ) {
175150 log .info ("Blockchain connection is now restored after a period of unavailability." +
176- " [block:{}, unavailabilityPeriod:{}]" ,
151+ " [block:{}, unavailabilityPeriod:{}]" ,
177152 latestBlockNumber , pollingInterval .multipliedBy (consecutiveFailures ));
178153 firstFailure = null ;
179154 consecutiveFailures = 0 ;
180- if (outOfService ) {
181- outOfService = false ;
182- applicationEventPublisher .publishEvent (new ChainConnectedEvent (this ));
183- }
155+ log .debug ("Publishing ChainConnectedEvent" );
156+ applicationEventPublisher .publishEvent (new ChainConnectedEvent (this ));
184157 }
185158 }
186159
187160 @ Override
188161 public Health health () {
189- final Health .Builder healthBuilder = outOfService
162+ final Health .Builder healthBuilder = isOutOfService ()
190163 ? Health .outOfService ()
191164 : Health .up ();
192165
@@ -197,10 +170,23 @@ public Health health() {
197170 return healthBuilder
198171 .withDetail ("consecutiveFailures" , consecutiveFailures )
199172 .withDetail ("pollingInterval" , pollingInterval )
200- .withDetail ("outOfServiceThreshold" , outOfServiceThreshold )
201173 .build ();
202174 }
203175
176+ /**
177+ * Returns whether the scheduler should be considered out-of-service.
178+ *
179+ * @return {@literal true} in case of at least one consecutive failures, {@literal false} otherwise.
180+ */
181+ public boolean isOutOfService () {
182+ return consecutiveFailures > 0 ;
183+ }
184+
185+ /**
186+ * Returns whether the scheduler is up or not.
187+ *
188+ * @return {@literal true} if the scheduler is up, {@literal false} otherwise.
189+ */
204190 public boolean isUp () {
205191 return health ().getStatus () == Status .UP ;
206192 }
0 commit comments