Commit 23773918 authored by Georg Mittendorfer's avatar Georg Mittendorfer

Make get node info response accumulation configurable, make logging less...

Make get node info response accumulation configurable, make logging less verbose and prevent permanent recalculation attempts.
parent 701a7823
Pipeline #59087118 passed with stage
in 3 minutes and 19 seconds
......@@ -46,8 +46,10 @@ import java.time.Duration;
public class PiriConfiguration {
@Bean
public GetNodeInfoResponsePostProcessor getNodeInfoResponsePostProcessor(CommandChecker commandChecker) {
return new GetNodeInfoResponsePostProcessor(commandChecker.isEnabled(new AttachToTangle()), Duration.ofMinutes(1));
public GetNodeInfoResponsePostProcessor getNodeInfoResponsePostProcessor(CommandChecker commandChecker, Environment env) {
Boolean accumulateNodeInfo = env.getProperty("piri.getNodeInfo.response.accumulate", Boolean.class, true);
Duration cacheTimeout = parseDuration(env.getProperty("piri.getNodeInfo.response.accumulate.cache", "1m"));
return new GetNodeInfoResponsePostProcessor(commandChecker.isEnabled(new AttachToTangle()), accumulateNodeInfo, cacheTimeout);
}
@Bean
......
......@@ -40,13 +40,15 @@ public class GetNodeInfoResponsePostProcessor {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final boolean powAvailable;
private final boolean accumulateNodeInfo;
private final NodeInfo globalNodeInfo;
private long validUntil;
private boolean validGlobalNodeInfo;
private final Duration cacheTimeout;
public GetNodeInfoResponsePostProcessor(boolean powAvailable, Duration cacheTimeout) {
public GetNodeInfoResponsePostProcessor(boolean powAvailable, boolean accumulateNodeInfo, Duration cacheTimeout) {
this.powAvailable = powAvailable;
this.accumulateNodeInfo = accumulateNodeInfo;
this.cacheTimeout = cacheTimeout;
this.globalNodeInfo = new NodeInfo();
this.validUntil = System.currentTimeMillis();
......@@ -65,7 +67,7 @@ public class GetNodeInfoResponsePostProcessor {
public NodeInfo updateGlobalNodeInfo(Seq<Node> nodes) {
if (System.currentTimeMillis() >= validUntil) { // only update if outdated
if (accumulateNodeInfo && System.currentTimeMillis() >= validUntil) { // only update if outdated and multiple nodes are available
Set<NodeInfo> syncedNodeInfos = nodes.map(Node::getLatestNodeInfo).filter(NodeInfo::isSynced).collect(Collectors.toSet());
if (syncedNodeInfos.size() > 1) {
......@@ -107,12 +109,13 @@ public class GetNodeInfoResponsePostProcessor {
globalNodeInfo.setNeighbors((int) Math.floor(neighbors.getPercentile(50)));
globalNodeInfo.setTransactionsToRequest((int) Math.floor(txToRequest.getPercentile(50)));
validGlobalNodeInfo = true;
validUntil = System.currentTimeMillis() + cacheTimeout.toMillis();
validGlobalNodeInfo = true; // use global node info
validUntil = System.currentTimeMillis() + cacheTimeout.toMillis(); // next recalculation
} else {
logger.info("Could not calculate global node info data. Number of synced nodes: [{}].", syncedNodeInfos.size());
validGlobalNodeInfo = false;
logger.debug("Could not calculate global node info data. Only [{}] synced nodes available.", syncedNodeInfos.size());
validGlobalNodeInfo = false; // don't use global node info
validUntil = System.currentTimeMillis() + cacheTimeout.toMillis(); // prevent too many recalculation attempts
}
}
......@@ -120,7 +123,7 @@ public class GetNodeInfoResponsePostProcessor {
}
private void setGlobalProperties(NodeInfo nodeInfo) {
if (validGlobalNodeInfo) { // only override if valid data available
if (accumulateNodeInfo && validGlobalNodeInfo) { // only override if valid data available
nodeInfo.setJreAvailableProcessors(globalNodeInfo.getJreAvailableProcessors());
nodeInfo.setJreTotalMemory(globalNodeInfo.getJreTotalMemory());
nodeInfo.setJreFreeMemory(globalNodeInfo.getJreFreeMemory());
......
......@@ -26,7 +26,15 @@ piri.session.binding.enabled=true
piri.session.binding.clean.interval=1m
piri.session.binding.idle.time=1m
# if set to true the node info of all synced nodes is merged together into one for information like memory consumption,
# cpus, number of neighbors, load and tips so that the client doesn't get individual node information.
piri.getNodeInfo.response.accumulate=true
# duration how long to cache accumulated node info response information until next recalculation. recalculation is costly.
piri.getNodeInfo.response.accumulate.cache=1m
# if set to false then a retry of a failed command will happen on another node if available
piri.retry.successive-on-same-node=false
# number of maximum retries per node
piri.retry.max-per-node=1
# rate limiter config
......
......@@ -49,14 +49,14 @@ public class GetNodeInfoResponsePostProcessorTest {
@Test
public void givenExpectedPowFeatureWhenPostProcessThenDoNothing() {
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, Duration.ZERO);
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, false, Duration.ZERO);
assertThat(postProcessor.postProcess(INFO_WITH_REMOTE_POW)).contains(FEATURES_REMOTE_POW);
assertThat(postProcessor.postProcess(INFO_WITH_SOMETHING_AND_REMOTE_POW)).contains(FEATURES_SOMETHING_WITH_POW);
}
@Test
public void givenMissingRemotePowFeatureWhenPostProcessThenAdd() {
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, Duration.ZERO);
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, false, Duration.ZERO);
assertThat(postProcessor.postProcess(INFO_WITHOUT_REMOTE_POW)).contains(FEATURES_SOMETHING_WITH_POW);
assertThat(postProcessor.postProcess(INFO_EMPTY)).contains(FEATURES_REMOTE_POW);
assertThat(postProcessor.postProcess(INFO_WITH_EMPTY_FEATURES)).contains(FEATURES_REMOTE_POW);
......@@ -64,7 +64,7 @@ public class GetNodeInfoResponsePostProcessorTest {
@Test
public void givenExpectedMissingPowFeatureWhenPostProcessThenDoNothing() {
GetNodeInfoResponsePostProcessor postProcessorPowDisabled = new GetNodeInfoResponsePostProcessor(false, Duration.ZERO);
GetNodeInfoResponsePostProcessor postProcessorPowDisabled = new GetNodeInfoResponsePostProcessor(false, false, Duration.ZERO);
assertThat(postProcessorPowDisabled.postProcess(INFO_WITHOUT_REMOTE_POW)).contains(FEATURES_SOMETHING);
assertThat(postProcessorPowDisabled.postProcess(INFO_EMPTY)).doesNotContain("features");
assertThat(postProcessorPowDisabled.postProcess(INFO_WITH_EMPTY_FEATURES)).contains(FEATURES_EMPTY);
......@@ -72,14 +72,14 @@ public class GetNodeInfoResponsePostProcessorTest {
@Test
public void givenUnexpectedRemotePowFeatureWhenPostProcessThenRemove() {
GetNodeInfoResponsePostProcessor postProcessorPowDisabled = new GetNodeInfoResponsePostProcessor(false, Duration.ZERO);
GetNodeInfoResponsePostProcessor postProcessorPowDisabled = new GetNodeInfoResponsePostProcessor(false, false, Duration.ZERO);
assertThat(postProcessorPowDisabled.postProcess(INFO_WITH_REMOTE_POW)).contains(FEATURES_EMPTY);
assertThat(postProcessorPowDisabled.postProcess(INFO_WITH_SOMETHING_AND_REMOTE_POW)).contains(FEATURES_SOMETHING);
}
@Test
public void whenAcceptThenAssertGetNodeInfoAndOkResponse() {
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, Duration.ZERO);
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, false, Duration.ZERO);
assertThat(postProcessor.accept(new GetNodeInfo(), ResponseEntity.ok("test"))).isTrue();
assertThat(postProcessor.accept(null, ResponseEntity.ok("test"))).isFalse();
assertThat(postProcessor.accept(new GetNodeInfo(), ResponseEntity.badRequest().build())).isFalse();
......@@ -88,7 +88,7 @@ public class GetNodeInfoResponsePostProcessorTest {
@Test
public void whenUpdateGlobalNodeInfoThenCalculateNeighbors() {
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, Duration.ZERO);
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, true, Duration.ZERO);
NodeInfo result = postProcessor.updateGlobalNodeInfo(List.of(
node(neighbors(4)), // median
node(neighbors(132)),
......@@ -100,7 +100,7 @@ public class GetNodeInfoResponsePostProcessorTest {
@Test
public void whenUpdateGlobalNodeInfoThenCalculateProcessors() {
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, Duration.ZERO);
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, true, Duration.ZERO);
NodeInfo result = postProcessor.updateGlobalNodeInfo(List.of(
node(processors(4)), // median
node(processors(32)),
......@@ -112,7 +112,7 @@ public class GetNodeInfoResponsePostProcessorTest {
@Test
public void whenUpdateGlobalNodeInfoThenCalculateMemory() {
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, Duration.ZERO);
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, true, Duration.ZERO);
NodeInfo result = postProcessor.updateGlobalNodeInfo(List.of(
node(memory(1, 2, 3)),
node(memory(16, 17, 18)),
......@@ -129,7 +129,7 @@ public class GetNodeInfoResponsePostProcessorTest {
@Test
public void whenUpdateGlobalNodeInfoThenCalculateTips() {
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, Duration.ZERO);
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, true, Duration.ZERO);
NodeInfo result = postProcessor.updateGlobalNodeInfo(List.of(
node(tips(4)),
node(tips(42)),
......@@ -141,7 +141,7 @@ public class GetNodeInfoResponsePostProcessorTest {
@Test
public void whenUpdateGlobalNodeInfoThenCalculateTransactionsToRequest() {
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, Duration.ZERO);
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, true, Duration.ZERO);
NodeInfo result = postProcessor.updateGlobalNodeInfo(List.of(
node(txToRequest(4)),
node(txToRequest(42)),
......@@ -153,7 +153,7 @@ public class GetNodeInfoResponsePostProcessorTest {
@Test
public void whenUpdateGlobalNodeInfoThenUseAtLestTwoSyncedNodes() {
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, Duration.ZERO);
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, true, Duration.ZERO);
NodeInfo old = postProcessor.updateGlobalNodeInfo(List.empty());
assertThat(old.getNeighbors()).isZero();
NodeInfo result = postProcessor.updateGlobalNodeInfo(List.of(
......@@ -172,7 +172,7 @@ public class GetNodeInfoResponsePostProcessorTest {
@Test
public void whenUpdateGlobalNodeInfoThenUpdateOnlyIfOutdated() throws InterruptedException {
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, Duration.ofMillis(100));
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, true, Duration.ofMillis(100));
postProcessor.updateGlobalNodeInfo(List.of(
node(tips(4)),
node(tips(42)),
......@@ -197,7 +197,7 @@ public class GetNodeInfoResponsePostProcessorTest {
@Test
public void givenInvalidGlobalNodeInfoWhenPostProcessThenDoNotUpdateGlobalProperties() {
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, Duration.ZERO);
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, true, Duration.ZERO);
assertThat(postProcessor.postProcess("{\"tips\":2}")).contains("tips\":2");
postProcessor.updateGlobalNodeInfo(List.of(
node(tips(42)) // one node is not enough to update global node info
......@@ -205,6 +205,17 @@ public class GetNodeInfoResponsePostProcessorTest {
assertThat(postProcessor.postProcess("{\"tips\":2}")).contains("tips\":2");
}
@Test
public void givenAccumulationDisabledWhenPostProcessThenDoNotUpdateGlobalProperties() {
GetNodeInfoResponsePostProcessor postProcessor = new GetNodeInfoResponsePostProcessor(true, false, Duration.ZERO);
NodeInfo nodeInfo = postProcessor.updateGlobalNodeInfo(List.of(
node(tips(42)),
node(tips(42))
));
assertThat(nodeInfo.getTips()).isZero();
assertThat(postProcessor.postProcess("{\"tips\":3}")).contains("tips\":3");
}
private Node node(NodeInfo nodeInfo) {
Node node = mock(Node.class);
when(node.getLatestNodeInfo()).thenReturn(nodeInfo);
......@@ -238,5 +249,4 @@ public class GetNodeInfoResponsePostProcessorTest {
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment