Skip to content

Commit c245d60

Browse files
authored
feat: configure block timestamp threshold above which the scheduler will be OUT-OF-SERVICE (#768)
1 parent d2cb21f commit c245d60

File tree

10 files changed

+45
-21
lines changed

10 files changed

+45
-21
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ You can configure the _iExec Core Scheduler_ with the following properties:
3636
| `IEXEC_CORE_WALLET_PASSWORD` | Password to unlock the wallet of the server. | String | `whatever` |
3737
| `IEXEC_BLOCKCHAIN_NODE_ADDRESS` | Private URL to connect to the blockchain node. | URL | `http://localhost:8545` |
3838
| `POOL_ADDRESS` | On-chain address of the workerpool managed by the current _iExec Core Scheduler_. | String | `0x365E7BABAa85eC61Dffe5b520763062e6C29dA27` |
39+
| `CHAIN_OUTOFSERVICETHRESHOLD` | Threshold in seconds, the scheduler is OUT-OF-SERVICE when the last known block timestamp is older. | String | `PT5S` |
3940
| `IEXEC_START_BLOCK_NUMBER` | Subscribe to new deal events from a specific block number. | Positive integer | `0` |
4041
| `IEXEC_GAS_PRICE_MULTIPLIER` | Transactions will be sent with `networkGasPrice * gasPriceMultiplier`. | Float | `1.0` |
4142
| `IEXEC_GAS_PRICE_CAP` | In Wei, will be used for transactions if `networkGasPrice * gasPriceMultiplier > gasPriceCap` | Integer | `22000000000` |

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
services:
22

33
chain:
4-
image: docker-regis.iex.ec/poco-chain:1.0.0-poco-v5.5.0-voucher-v1.0.0-nethermind
4+
image: docker-regis.iex.ec/poco-chain:1.1.0-poco-v6.1.0-contracts-voucher-v1.0.0-nethermind
55
expose:
66
- "8545"
77

src/main/java/com/iexec/core/chain/BlockchainConnectionHealthIndicator.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public class BlockchainConnectionHealthIndicator implements HealthIndicator {
4747
* Interval between 2 requests onto the chain.
4848
*/
4949
private final Duration pollingInterval;
50+
private final Duration outOfServiceThreshold;
5051
/**
5152
* Number of consecutive failures before declaring this Scheduler is out-of-service.
5253
*/
@@ -85,6 +86,7 @@ public BlockchainConnectionHealthIndicator(
8586
Clock clock) {
8687
this.applicationEventPublisher = applicationEventPublisher;
8788
this.pollingInterval = chainConfig.getBlockTime();
89+
this.outOfServiceThreshold = chainConfig.getSyncTimeout();
8890
this.monitoringExecutor = monitoringExecutor;
8991
this.clock = clock;
9092
}
@@ -108,7 +110,7 @@ void scheduleMonitoring() {
108110
void checkConnection() {
109111
log.debug("Latest on-chain block number [block:{}]", latestBlockNumber);
110112
final Instant now = Instant.now();
111-
final Instant threshold = Instant.ofEpochSecond(latestBlockTimestamp).plusSeconds(pollingInterval.toSeconds());
113+
final Instant threshold = Instant.ofEpochSecond(latestBlockTimestamp).plusSeconds(outOfServiceThreshold.toSeconds());
112114
if (now.isAfter(threshold)) {
113115
connectionFailed();
114116
} else {

src/main/java/com/iexec/core/chain/ChainConfig.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ public class ChainConfig {
5858
@NotNull(message = "Block time must not be null")
5959
private Duration blockTime;
6060

61+
@Value("${chain.out-of-service-threshold}")
62+
@NotNull(message = "OUT-OF-SERVICE threshold must not be null")
63+
private Duration syncTimeout;
64+
6165
@Value("${chain.node-address}")
6266
@URL(message = "Node address must be a valid URL")
6367
@NotEmpty(message = "Node address must not be empty")

src/main/resources/application.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ wallet:
5252

5353
chain:
5454
node-address: ${IEXEC_BLOCKCHAIN_NODE_ADDRESS:http://localhost:8545}
55+
out-of-service-threshold: PT5S
5556
pool-address: ${POOL_ADDRESS:0x365E7BABAa85eC61Dffe5b520763062e6C29dA27}
5657
start-block-number: ${IEXEC_START_BLOCK_NUMBER:0}
5758
gas-price-multiplier: ${IEXEC_GAS_PRICE_MULTIPLIER:1.0} # txs will be sent with networkGasPrice*gasPriceMultiplier, 4.0 means super fast

src/test/java/com/iexec/core/chain/BlockchainConnectionHealthIndicatorTests.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,13 @@
1616

1717
package com.iexec.core.chain;
1818

19-
import org.assertj.core.api.Assertions;
2019
import org.junit.jupiter.api.BeforeEach;
2120
import org.junit.jupiter.api.Test;
2221
import org.junit.jupiter.api.extension.ExtendWith;
2322
import org.junit.jupiter.params.ParameterizedTest;
2423
import org.junit.jupiter.params.provider.Arguments;
2524
import org.junit.jupiter.params.provider.MethodSource;
2625
import org.mockito.Mock;
27-
import org.mockito.Mockito;
2826
import org.mockito.junit.jupiter.MockitoExtension;
2927
import org.springframework.boot.actuate.health.Health;
3028
import org.springframework.context.ApplicationEventPublisher;
@@ -35,8 +33,10 @@
3533
import java.util.concurrent.TimeUnit;
3634
import java.util.stream.Stream;
3735

36+
import static org.assertj.core.api.Assertions.assertThat;
3837
import static org.mockito.ArgumentMatchers.any;
3938
import static org.mockito.ArgumentMatchers.eq;
39+
import static org.mockito.Mockito.verify;
4040
import static org.mockito.Mockito.when;
4141

4242
@ExtendWith(MockitoExtension.class)
@@ -57,6 +57,7 @@ class BlockchainConnectionHealthIndicatorTests {
5757
@BeforeEach
5858
void init() {
5959
when(chainConfig.getBlockTime()).thenReturn(BLOCK_TIME);
60+
when(chainConfig.getSyncTimeout()).thenReturn(BLOCK_TIME);
6061

6162
this.blockchainConnectionHealthIndicator = new BlockchainConnectionHealthIndicator(
6263
applicationEventPublisher,
@@ -71,7 +72,7 @@ void init() {
7172
void shouldScheduleMonitoring() {
7273
blockchainConnectionHealthIndicator.scheduleMonitoring();
7374

74-
Mockito.verify(executor).scheduleAtFixedRate(
75+
verify(executor).scheduleAtFixedRate(
7576
any(),
7677
eq(0L),
7778
eq(5L),
@@ -141,8 +142,8 @@ void checkConnection(int previousConsecutiveFailures,
141142
final boolean outOfService = blockchainConnectionHealthIndicator.isOutOfService();
142143
final LocalDateTime firstFailure = blockchainConnectionHealthIndicator.getFirstFailure();
143144

144-
Assertions.assertThat(outOfService).isEqualTo(expectedOutOfService);
145-
Assertions.assertThat(firstFailure).isEqualTo(expectedFirstFailure);
145+
assertThat(outOfService).isEqualTo(expectedOutOfService);
146+
assertThat(firstFailure).isEqualTo(expectedFirstFailure);
146147
}
147148
// endregion
148149

@@ -161,7 +162,7 @@ void shouldReturnOutOfService() {
161162
.build();
162163

163164
final Health health = blockchainConnectionHealthIndicator.health();
164-
Assertions.assertThat(health).isEqualTo(expectedHealth);
165+
assertThat(health).isEqualTo(expectedHealth);
165166
}
166167

167168
@Test
@@ -175,7 +176,7 @@ void shouldReturnUpAndNoFirstFailure() {
175176
.build();
176177

177178
final Health health = blockchainConnectionHealthIndicator.health();
178-
Assertions.assertThat(health).isEqualTo(expectedHealth);
179+
assertThat(health).isEqualTo(expectedHealth);
179180
}
180181

181182
@Test
@@ -192,7 +193,7 @@ void shouldReturnUpButWithFirstFailure() {
192193
.build();
193194

194195
final Health health = blockchainConnectionHealthIndicator.health();
195-
Assertions.assertThat(health).isEqualTo(expectedHealth);
196+
assertThat(health).isEqualTo(expectedHealth);
196197
}
197198
// endregion
198199

src/test/java/com/iexec/core/ChainConfigTest.java renamed to src/test/java/com/iexec/core/chain/ChainConfigTests.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,21 @@
1414
* limitations under the License.
1515
*/
1616

17-
package com.iexec.core;
17+
package com.iexec.core.chain;
1818

19-
import com.iexec.core.chain.ChainConfig;
2019
import jakarta.validation.ConstraintViolation;
2120
import jakarta.validation.Validation;
2221
import jakarta.validation.Validator;
2322
import jakarta.validation.ValidatorFactory;
2423
import org.junit.jupiter.api.BeforeEach;
2524
import org.junit.jupiter.api.Test;
25+
2626
import java.time.Duration;
2727
import java.util.Set;
28+
2829
import static org.assertj.core.api.Assertions.assertThat;
2930

30-
class ChainConfigTest {
31+
class ChainConfigTests {
3132
private static final String IEXEC_NODE_ADDRESS = "https://bellecour.iex.ec";
3233
private static final String IEXEC_HUB_ADDRESS = "0x1a69b2eb604db8eba185df03ea4f5288dcbbd248";
3334
private static final String POOL_ADDRESS = "poolAddress";
@@ -48,6 +49,7 @@ void chainIdMustBePositive() {
4849
false,
4950
IEXEC_HUB_ADDRESS,
5051
Duration.ofMillis(100),
52+
Duration.ofSeconds(30),
5153
IEXEC_NODE_ADDRESS,
5254
POOL_ADDRESS,
5355
0L,
@@ -67,6 +69,7 @@ void nodeAddressMustBeValidURL() {
6769
false,
6870
IEXEC_HUB_ADDRESS,
6971
Duration.ofMillis(100),
72+
Duration.ofSeconds(30),
7073
"invalid-url", // invalid URL
7174
POOL_ADDRESS,
7275
0L,
@@ -86,6 +89,7 @@ void nodeAddressMustNotBeEmpty() {
8689
false,
8790
IEXEC_HUB_ADDRESS,
8891
Duration.ofMillis(100),
92+
Duration.ofSeconds(30),
8993
"", // empty nodeAddress
9094
POOL_ADDRESS,
9195
0L,
@@ -105,6 +109,7 @@ void blockTimeMustBeAtLeast100ms() {
105109
false,
106110
IEXEC_HUB_ADDRESS,
107111
Duration.ofMillis(99), // less than 100ms
112+
Duration.ofSeconds(30),
108113
IEXEC_NODE_ADDRESS,
109114
POOL_ADDRESS,
110115
0L,
@@ -124,6 +129,7 @@ void blockTimeMustBeAtMost20Seconds() {
124129
false,
125130
IEXEC_HUB_ADDRESS,
126131
Duration.ofSeconds(21), // more than 20 seconds
132+
Duration.ofSeconds(30),
127133
IEXEC_NODE_ADDRESS,
128134
POOL_ADDRESS,
129135
0L,
@@ -143,6 +149,7 @@ void gasPriceMultiplierMustBePositive() {
143149
false,
144150
IEXEC_HUB_ADDRESS,
145151
Duration.ofMillis(100),
152+
Duration.ofSeconds(30),
146153
IEXEC_NODE_ADDRESS,
147154
POOL_ADDRESS,
148155
0L,
@@ -162,6 +169,7 @@ void gasPriceCapMustBePositiveOrZero() {
162169
false,
163170
IEXEC_HUB_ADDRESS,
164171
Duration.ofMillis(100),
172+
Duration.ofSeconds(30),
165173
IEXEC_NODE_ADDRESS,
166174
POOL_ADDRESS,
167175
0L,
@@ -181,6 +189,7 @@ void hubAddressMustBeValidEthereumAddress() {
181189
false,
182190
"0x0", // invalid address
183191
Duration.ofMillis(100),
192+
Duration.ofSeconds(30),
184193
IEXEC_NODE_ADDRESS,
185194
POOL_ADDRESS,
186195
0L,

src/test/java/com/iexec/core/chain/WalletConfigurationTest.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import com.iexec.commons.poco.chain.SignerService;
2121
import org.junit.jupiter.api.Test;
2222
import org.junit.jupiter.api.io.TempDir;
23+
import org.springframework.boot.convert.ApplicationConversionService;
2324
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
25+
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
2426
import org.web3j.crypto.WalletUtils;
2527

2628
import java.io.File;
@@ -37,7 +39,11 @@ class WalletConfigurationTest {
3739
void shouldCreateBeans() throws Exception {
3840
final String tempWalletName = WalletUtils.generateFullNewWalletFile("changeit", tempWalletDir);
3941
final String tempWalletPath = tempWalletDir.getAbsolutePath() + File.separator + tempWalletName;
40-
runner.withPropertyValues("chain.node-address=http://localhost:8545", "chain.pool-address=0x1", "chain.start-block-number=0", "chain.gas-price-multiplier=1.0", "chain.gas-price-cap=0")
42+
runner.withPropertyValues("chain.out-of-service-threshold=PT30S", "chain.node-address=http://localhost:8545", "chain.pool-address=0x1", "chain.start-block-number=0", "chain.gas-price-multiplier=1.0", "chain.gas-price-cap=0")
43+
.withBean(PropertySourcesPlaceholderConfigurer.class,
44+
PropertySourcesPlaceholderConfigurer::new)
45+
.withInitializer(context ->
46+
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance()))
4147
.withBean(IexecHubService.class)
4248
.withBean(PublicChainConfig.class, 65535, true, "http://localhost:8545", "0xC129e7917b7c7DeDfAa5Fff1FB18d5D7050fE8ca", Duration.ofSeconds(5))
4349
.withBean(WalletConfiguration.class, tempWalletPath, "changeit")

src/test/java/com/iexec/core/chain/WebSocketBlockchainListenerTests.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
import static org.awaitility.Awaitility.await;
3939

4040
@Testcontainers
41-
@SpringBootTest
41+
@SpringBootTest(properties = "chain.out-of-service-threshold=PT30S")
4242
class WebSocketBlockchainListenerTests {
4343
private static final String CHAIN_SVC_NAME = "chain";
4444
private static final int CHAIN_SVC_PORT = 8545;
@@ -64,10 +64,14 @@ static void registerProperties(DynamicPropertyRegistry registry) {
6464
environment.getServiceHost(CONFIG_SVC_NAME, CONFIG_SVC_PORT),
6565
environment.getServicePort(CONFIG_SVC_NAME, CONFIG_SVC_PORT))
6666
);
67-
registry.add("sprint.data.mongodb.host", () -> environment.getServiceHost(MONGO_SVC_NAME, MONGO_SVC_PORT));
67+
registry.add("spring.data.mongodb.host", () -> environment.getServiceHost(MONGO_SVC_NAME, MONGO_SVC_PORT));
6868
registry.add("spring.data.mongodb.port", () -> environment.getServicePort(MONGO_SVC_NAME, MONGO_SVC_PORT));
6969
}
7070

71+
private static String getServiceUrl(final String serviceHost, final int servicePort) {
72+
return String.format("http://%s:%s", serviceHost, servicePort);
73+
}
74+
7175
@Autowired
7276
private MeterRegistry meterRegistry;
7377

@@ -77,10 +81,6 @@ static void registerProperties(DynamicPropertyRegistry registry) {
7781
@Autowired
7882
private Web3jService web3jService;
7983

80-
private static String getServiceUrl(String serviceHost, int servicePort) {
81-
return "http://" + serviceHost + ":" + servicePort;
82-
}
83-
8484
@Test
8585
void shouldConnect() {
8686
await().atMost(10L, TimeUnit.SECONDS)

src/test/java/com/iexec/core/configuration/ConfigurationServiceTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ void init() {
6666
}
6767

6868
private void initService(long startBlockNumber) throws Exception {
69-
final ChainConfig chainConfig = new ChainConfig(65535, true, "", Duration.ofSeconds(5), "", "0x0", startBlockNumber, 1.0f, 0);
69+
final ChainConfig chainConfig = new ChainConfig(65535, true, "", Duration.ofSeconds(5), Duration.ofSeconds(30), "", "0x0", startBlockNumber, 1.0f, 0);
7070
configurationService = new ConfigurationService(configurationRepository, replayConfigurationRepository, chainConfig);
7171
configurationService.run();
7272
}

0 commit comments

Comments
 (0)