/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cassandra.dht.tokenallocator; import java.util.List; import java.util.Map; import java.util.NavigableMap; import java.util.Random; import org.apache.commons.math3.stat.descriptive.SummaryStatistics; import org.apache.cassandra.dht.IPartitioner; import org.apache.cassandra.dht.Token; /** * Base class for {@link NoReplicationTokenAllocatorTest} and {@link AbstractReplicationAwareTokenAllocatorTest}, */ abstract class TokenAllocatorTestBase { protected static final int TARGET_CLUSTER_SIZE = 250; protected static final int MAX_VNODE_COUNT = 64; interface TestReplicationStrategy extends ReplicationStrategy<Unit> { void addUnit(Unit n); void removeUnit(Unit n); /** * Returns a list of all replica units for given token. */ List<Unit> getReplicas(Token token, NavigableMap<Token, Unit> sortedTokens); /** * Returns the start of the token span that is replicated in this token. * Note: Though this is not trivial to see, the replicated span is always contiguous. A token in the same * group acts as a barrier; if one is not found the token replicates everything up to the replica'th distinct * group seen in front of it. */ Token replicationStart(Token token, Unit unit, NavigableMap<Token, Unit> sortedTokens); /** * Multiplier for the acceptable disbalance in the cluster. With some strategies it is harder to achieve good * results. */ double spreadExpectation(); } interface TokenCount { int tokenCount(int perUnitCount, Random rand); double spreadExpectation(); } TokenCount fixedTokenCount = new TokenCount() { public int tokenCount(int perUnitCount, Random rand) { return perUnitCount; } public double spreadExpectation() { return 4; // High tolerance to avoid flakiness. } }; TokenCount varyingTokenCount = new TokenCount() { public int tokenCount(int perUnitCount, Random rand) { if (perUnitCount == 1) return 1; // 25 to 175% return rand.nextInt(perUnitCount * 3 / 2) + (perUnitCount + 3) / 4; } public double spreadExpectation() { return 8; // High tolerance to avoid flakiness. } }; Random seededRand = new Random(2); public void random(Map<Token, Unit> map, TestReplicationStrategy rs, int unitCount, TokenCount tc, int perUnitCount, IPartitioner partitioner) { System.out.format("\nRandom generation of %d units with %d tokens each\n", unitCount, perUnitCount); Random rand = seededRand; for (int i = 0; i < unitCount; i++) { Unit unit = new Unit(); rs.addUnit(unit); int tokens = tc.tokenCount(perUnitCount, rand); for (int j = 0; j < tokens; j++) { map.put(partitioner.getRandomToken(rand), unit); } } } public String mms(SummaryStatistics s) { return String.format("max %.2f min %.2f stddev %.4f", s.getMax(), s.getMin(), s.getStandardDeviation()); } class Summary { double min = 1; double max = 1; double stddev = 0; void update(SummaryStatistics stat) { min = Math.min(min, stat.getMin()); max = Math.max(max, stat.getMax()); stddev = Math.max(stddev, stat.getStandardDeviation()); } public String toString() { return String.format("max %.2f min %.2f stddev %.4f", max, min, stddev); } } int nextUnitId = 0; final class Unit implements Comparable<Unit> { int unitId = nextUnitId++; public String toString() { return Integer.toString(unitId); } @Override public int compareTo(Unit o) { return Integer.compare(unitId, o.unitId); } } }