Skip to content
This repository was archived by the owner on Sep 16, 2024. It is now read-only.

Commit 43080bd

Browse files
committed
#423 Fix for replicas when forests per host exceeds number of hosts
Issue pertained to the number of forests on each host causing a failure in determining which host replicas should go on to.
1 parent 8e869f3 commit 43080bd

File tree

3 files changed

+64
-5
lines changed

3 files changed

+64
-5
lines changed

src/main/java/com/marklogic/appdeployer/command/forests/AbstractReplicaBuilderStrategy.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package com.marklogic.appdeployer.command.forests;
22

33
import com.marklogic.appdeployer.AppConfig;
4+
import com.marklogic.client.ext.helper.LoggingObject;
45
import com.marklogic.mgmt.api.forest.ForestReplica;
56

67
import java.util.Map;
78

8-
public abstract class AbstractReplicaBuilderStrategy implements ReplicaBuilderStrategy {
9+
public abstract class AbstractReplicaBuilderStrategy extends LoggingObject implements ReplicaBuilderStrategy {
910

1011
/**
1112
* Configures the fast and large data directories for a replica based on what's in AppConfig for the given

src/main/java/com/marklogic/appdeployer/command/forests/DistributedReplicaBuilderStrategy.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public void buildReplicas(List<Forest> forests, ForestPlan forestPlan, AppConfig
3535
}
3636

3737
for (String host : hostToForests.keySet()) {
38+
logger.info("Determining replicas for host: " + host);
3839

3940
// availableHosts will be the hosts that we can put a forest's replicas on, which excludes the host where
4041
// the forest lives. We also want to have the hosts in different order as we assign replicas to hosts, so
@@ -46,6 +47,8 @@ public void buildReplicas(List<Forest> forests, ForestPlan forestPlan, AppConfig
4647
availableHosts.addAll(hostNames.subList(hostIndex + 1, hostNames.size()));
4748
}
4849
availableHosts.addAll(hostNames.subList(0, hostIndex));
50+
final int availableHostCount = availableHosts.size();
51+
logger.info("Available hosts for replicas: " + availableHosts);
4952

5053
int hostPointer = 0;
5154

@@ -59,11 +62,12 @@ public void buildReplicas(List<Forest> forests, ForestPlan forestPlan, AppConfig
5962
replicas.add(replica);
6063

6164
int replicaHostPointer = hostPointer + i - 1;
62-
if (replicaHostPointer == availableHosts.size()) {
63-
replicaHostPointer = 0;
65+
if (replicaHostPointer >= availableHostCount) {
66+
// Must do a modulo here in the event that there are more primary forests per host than number of hosts
67+
replicaHostPointer %= availableHostCount;
6468
}
65-
6669
replica.setHost(availableHosts.get(replicaHostPointer));
70+
logger.info(format("Built replica '%s' for forest '%s' on host '%s'", replica.getReplicaName(), currForest.getForestName(), replica.getHost()));
6771

6872
dataDirectoryPointer++;
6973
if (dataDirectoryPointer == replicaDataDirectories.size()) {
@@ -82,7 +86,7 @@ public void buildReplicas(List<Forest> forests, ForestPlan forestPlan, AppConfig
8286

8387
++hostPointer;
8488

85-
if (hostPointer == availableHosts.size()) {
89+
if (hostPointer == availableHostCount) {
8690
hostPointer = 0;
8791
}
8892

src/test/java/com/marklogic/appdeployer/command/forests/BuildForestReplicaTest.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
import org.junit.Assert;
99
import org.junit.Test;
1010

11+
import java.util.HashMap;
1112
import java.util.List;
13+
import java.util.Map;
14+
import java.util.concurrent.atomic.AtomicInteger;
15+
import java.util.stream.Stream;
1216

1317
public class BuildForestReplicaTest extends Assert {
1418

@@ -37,6 +41,56 @@ public void multipleForestsOnEachHost() {
3741
assertEquals("host2", forests.get(5).getForestReplica().get(0).getHost());
3842
}
3943

44+
/**
45+
* Test was added for https://github.com/marklogic-community/ml-app-deployer/issues/423, where a bug was detected
46+
* when the "host pointer" in the implementation code could exceed the number of hosts. This test reproduced the bug.
47+
* Just doing some basic assertions on the number of forests and replicas created, but the key is that the test no
48+
* longer fails.
49+
*/
50+
@Test
51+
public void sameNumberOfForestsAsHosts() {
52+
AppConfig appConfig = newAppConfig("mlForestsPerHost", "db,4");
53+
54+
List<Forest> forests = builder.buildForests(
55+
new ForestPlan("db", "host1", "host2", "host3", "host4").withReplicaCount(3), appConfig);
56+
57+
assertEquals(16, forests.size());
58+
forests.forEach(forest -> {
59+
assertEquals(3, forest.getForestReplica().size());
60+
});
61+
}
62+
63+
/**
64+
* Similar to the above test, just even more forests.
65+
*/
66+
@Test
67+
public void numberOfForestsPerHostIsMoreThanDoubleTheNumberOfHosts() {
68+
AppConfig appConfig = newAppConfig("mlForestsPerHost", "db,10");
69+
70+
List<Forest> forests = builder.buildForests(
71+
new ForestPlan("db", "host1", "host2", "host3", "host4").withReplicaCount(3), appConfig);
72+
73+
assertEquals(40, forests.size());
74+
75+
Map<String, AtomicInteger> hostToReplicaCounts = new HashMap<>();
76+
Stream.of("host1", "host2", "host3", "host4").forEach(host -> hostToReplicaCounts.put(host, new AtomicInteger(0)));
77+
78+
AtomicInteger replicaCount = new AtomicInteger(0);
79+
80+
forests.forEach(forest -> {
81+
assertEquals(3, forest.getForestReplica().size());
82+
forest.getForestReplica().forEach(replica -> {
83+
hostToReplicaCounts.get(replica.getHost()).getAndIncrement();
84+
replicaCount.getAndIncrement();
85+
});
86+
});
87+
88+
assertEquals("Expecting 120 replicas; we have 40 forests, and we expect 3 replicas for each", 120, replicaCount.get());
89+
Stream.of("host1", "host2", "host3", "host4").forEach(host -> {
90+
assertEquals("Each host should have 30 replicas, as there are 120 total", 30, hostToReplicaCounts.get(host).get());
91+
});
92+
}
93+
4094
@Test
4195
public void customNamingStrategyWithDistributedStrategy() {
4296
AppConfig appConfig = newAppConfig("mlForestsPerHost", "my-database,2");

0 commit comments

Comments
 (0)