use of com.datastax.oss.driver.api.core.metadata.token.Token in project java-driver by datastax.
the class NetworkTopologyReplicationStrategyTest method should_abort_early_and_log_when_bad_replication_factor_cannot_be_met.
/**
* When the replication factors are invalid (user error) and a datacenter has a replication factor
* that cannot be met, we want to quickly abort and move on to the next DC (instead of keeping
* scanning the ring in vain, which results in quadratic complexity). We also log a warning to
* give the user a chance to fix their settings.
*
* @see <a href="https://datastax-oss.atlassian.net/browse/JAVA-702">JAVA-702</a>
* @see <a href="https://datastax-oss.atlassian.net/browse/JAVA-859">JAVA-859</a>
*/
@Test
public void should_abort_early_and_log_when_bad_replication_factor_cannot_be_met() {
// Given
List<Token> ring = ImmutableList.of(TOKEN01, TOKEN04, TOKEN14, TOKEN19);
locate(node1, DC1, RACK11);
locate(node2, DC2, RACK21);
Map<Token, Node> tokenToPrimary = ImmutableMap.of(TOKEN01, node1, TOKEN04, node2, TOKEN14, node1, TOKEN19, node2);
Logger logger = (Logger) LoggerFactory.getLogger(NetworkTopologyReplicationStrategy.class);
logger.addAppender(appender);
try {
// When
int traversedTokensForValidSettings = countTraversedTokens(ring, tokenToPrimary, ImmutableMap.of(DC1, "1", DC2, "1"));
// Then
// No logs:
verify(appender, never()).doAppend(any(ILoggingEvent.class));
// When
int traversedTokensForInvalidSettings = countTraversedTokens(ring, tokenToPrimary, ImmutableMap.of(DC1, "1", DC2, "1", DC3, "1"));
// Did not take more steps than the valid settings
assertThat(traversedTokensForInvalidSettings).isEqualTo(traversedTokensForValidSettings);
// Did log:
verify(appender).doAppend(loggingEventCaptor.capture());
ILoggingEvent log = loggingEventCaptor.getValue();
assertThat(log.getLevel()).isEqualTo(Level.WARN);
assertThat(log.getMessage()).contains("could not achieve replication factor");
} finally {
logger.detachAppender(appender);
}
}
use of com.datastax.oss.driver.api.core.metadata.token.Token in project java-driver by datastax.
the class NetworkTopologyReplicationStrategyTest method countTraversedTokens.
// Counts the number of steps on the ring for a particular computation
private int countTraversedTokens(List<Token> ring, Map<Token, Node> tokenToPrimary, ImmutableMap<String, String> replicationConfig) {
AtomicInteger count = new AtomicInteger();
List<Token> ringSpy = spy(ring);
when(ringSpy.get(anyInt())).thenAnswer(invocation -> {
count.incrementAndGet();
return invocation.callRealMethod();
});
new NetworkTopologyReplicationStrategy(replicationConfig, "test").computeReplicasByToken(tokenToPrimary, ringSpy);
return count.get();
}
use of com.datastax.oss.driver.api.core.metadata.token.Token in project java-driver by datastax.
the class TokenRangeBase method mergeWith.
@NonNull
@Override
public TokenRange mergeWith(@NonNull TokenRange that) {
if (this.equals(that)) {
return this;
}
if (!(this.intersects(that) || this.end.equals(that.getStart()) || that.getEnd().equals(this.start))) {
throw new IllegalArgumentException(String.format("Can't merge %s with %s because they neither intersect nor are adjacent", this, that));
}
if (this.isEmpty()) {
return that;
}
if (that.isEmpty()) {
return this;
}
// That's actually "starts in or is adjacent to the end of"
boolean thisStartsInThat = contains(that, this.start, true) || this.start.equals(that.getEnd());
boolean thatStartsInThis = contains(this, that.getStart(), true) || that.getStart().equals(this.end);
// about them below
if (thisStartsInThat && thatStartsInThis) {
return fullRing();
}
// Starting at this.start, see how far we can go while staying in at least one of the ranges.
Token mergedEnd = (thatStartsInThis && !contains(this, that.getEnd(), false)) ? that.getEnd() : this.end;
// Repeat in the other direction.
Token mergedStart = thisStartsInThat ? that.getStart() : this.start;
return newTokenRange(mergedStart, mergedEnd);
}
use of com.datastax.oss.driver.api.core.metadata.token.Token in project java-driver by datastax.
the class EverywhereReplicationStrategy method computeReplicasByToken.
@Override
public Map<Token, Set<Node>> computeReplicasByToken(Map<Token, Node> tokenToPrimary, List<Token> ring) {
ImmutableMap.Builder<Token, Set<Node>> result = ImmutableMap.builder();
Set<Node> allNodes = ImmutableSet.copyOf(tokenToPrimary.values());
for (Token token : tokenToPrimary.keySet()) {
result = result.put(token, allNodes);
}
return result.build();
}
use of com.datastax.oss.driver.api.core.metadata.token.Token in project java-driver by datastax.
the class NetworkTopologyReplicationStrategy method computeReplicasByToken.
@Override
public Map<Token, Set<Node>> computeReplicasByToken(Map<Token, Node> tokenToPrimary, List<Token> ring) {
// The implementation of this method was adapted from
// org.apache.cassandra.locator.NetworkTopologyStrategy
ImmutableMap.Builder<Token, Set<Node>> result = ImmutableMap.builder();
Map<String, Set<String>> racks = getRacksInDcs(tokenToPrimary.values());
Map<String, Integer> dcNodeCount = Maps.newHashMapWithExpectedSize(replicationFactors.size());
Set<String> warnedDcs = Sets.newHashSetWithExpectedSize(replicationFactors.size());
CanonicalNodeSetBuilder replicasBuilder = new CanonicalNodeSetBuilder();
// find maximum number of nodes in each DC
for (Node node : Sets.newHashSet(tokenToPrimary.values())) {
String dc = node.getDatacenter();
dcNodeCount.merge(dc, 1, Integer::sum);
}
for (int i = 0; i < ring.size(); i++) {
replicasBuilder.clear();
Map<String, Set<Node>> allDcReplicas = new HashMap<>();
Map<String, Set<String>> seenRacks = new HashMap<>();
Map<String, Set<Node>> skippedDcEndpoints = new HashMap<>();
for (String dc : replicationFactors.keySet()) {
allDcReplicas.put(dc, new HashSet<>());
seenRacks.put(dc, new HashSet<>());
// preserve order
skippedDcEndpoints.put(dc, new LinkedHashSet<>());
}
for (int j = 0; j < ring.size() && !allDone(allDcReplicas, dcNodeCount); j++) {
Node h = tokenToPrimary.get(getTokenWrapping(i + j, ring));
String dc = h.getDatacenter();
if (dc == null || !allDcReplicas.containsKey(dc)) {
continue;
}
ReplicationFactor dcConfig = replicationFactors.get(dc);
// since allDcReplicas.containsKey(dc)
assert dcConfig != null;
int rf = dcConfig.fullReplicas();
Set<Node> dcReplicas = allDcReplicas.get(dc);
if (dcReplicas.size() >= rf) {
continue;
}
String rack = h.getRack();
// Check if we already visited all racks in dc
if (rack == null || seenRacks.get(dc).size() == racks.get(dc).size()) {
replicasBuilder.add(h);
dcReplicas.add(h);
} else {
// Is this a new rack?
if (seenRacks.get(dc).contains(rack)) {
skippedDcEndpoints.get(dc).add(h);
} else {
replicasBuilder.add(h);
dcReplicas.add(h);
seenRacks.get(dc).add(rack);
// If we've run out of distinct racks, add the nodes skipped so far
if (seenRacks.get(dc).size() == racks.get(dc).size()) {
Iterator<Node> skippedIt = skippedDcEndpoints.get(dc).iterator();
while (skippedIt.hasNext() && dcReplicas.size() < rf) {
Node nextSkipped = skippedIt.next();
replicasBuilder.add(nextSkipped);
dcReplicas.add(nextSkipped);
}
}
}
}
}
// Warn the user because that leads to quadratic performance of this method (JAVA-702).
for (Map.Entry<String, Set<Node>> entry : allDcReplicas.entrySet()) {
String dcName = entry.getKey();
int expectedFactor = replicationFactors.get(dcName).fullReplicas();
int achievedFactor = entry.getValue().size();
if (achievedFactor < expectedFactor && !warnedDcs.contains(dcName)) {
LOG.warn("[{}] Error while computing token map for replication settings {}: " + "could not achieve replication factor {} for datacenter {} (found only {} replicas).", logPrefix, replicationConfig, expectedFactor, dcName, achievedFactor);
// only warn once per DC
warnedDcs.add(dcName);
}
}
result.put(ring.get(i), replicasBuilder.build());
}
return result.build();
}
Aggregations