configurable options for replication strategies

Patch by Jeremy Hanna; review by eevans for CASSANDRA-1066

git-svn-id: https://svn.apache.org/repos/asf/cassandra/trunk@981069 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Eric Evans 2010-07-31 15:42:27 +00:00
parent 170bd4d0a0
commit df8a93358c
35 changed files with 427 additions and 302 deletions

View File

@ -185,7 +185,8 @@ request_scheduler_id: keyspace
# - name: name of the keyspace; "system" and "definitions" are
# reserved for Cassandra Internals.
# - replica_placement_strategy: the class that determines how replicas
# are distributed among nodes. Must extend AbstractReplicationStrategy.
# are distributed among nodes. Contains both the class as well as
# configuration information. Must extend AbstractReplicationStrategy.
# Out of the box, Cassandra provides
# * org.apache.cassandra.locator.RackUnawareStrategy
# * org.apache.cassandra.locator.RackAwareStrategy
@ -201,9 +202,17 @@ request_scheduler_id: keyspace
# different rack in in the first.
#
# DatacenterShardStrategy is a generalization of RackAwareStrategy.
# For each datacenter, you can specify (in `datacenter.properties`)
# how many replicas you want on a per-keyspace basis. Replicas are
# placed on different racks within each DC, if possible.
# For each datacenter, you can specify how many replicas you want
# on a per-keyspace basis. Replicas are placed on different racks
# within each DC, if possible. This strategy also requires rack aware
# snitch, such as RackInferringSnitch or PropertyFileSnitch.
# An example:
# - name: Keyspace1
# replica_placement_strategy: org.apache.cassandra.locator.DatacenterShardStrategy
# strategy_options:
# DC1 : 3
# DC2 : 2
# DC3 : 1
#
# - replication_factor: Number of replicas of each row
# - column_families: column families associated with this keyspace
@ -236,8 +245,8 @@ request_scheduler_id: keyspace
# and 1. defaults to 1.0 (always read repair).
# - preload_row_cache: If true, will populate row cache on startup.
# Defaults to false.
# - gc_grace_seconds: specifies the time to wait before garbage
# collecting tombstones (deletion markers). defaults to 864000 (10
# - gc_grace_seconds: specifies the time to wait before garbage
# collecting tombstones (deletion markers). defaults to 864000 (10
# days). See http://wiki.apache.org/cassandra/DistributedDeletes
#
# NOTE: this keyspace definition is for demonstration purposes only.

View File

@ -120,6 +120,7 @@ protocol Cassandra {
record KsDef {
string name;
string strategy_class;
union{ map<string>, null } strategy_options;
int replication_factor;
array<CfDef> cf_defs;
}

View File

@ -375,8 +375,9 @@ struct CfDef {
struct KsDef {
1: required string name,
2: required string strategy_class,
3: required i32 replication_factor,
5: required list<CfDef> cf_defs,
3: optional map<string,string> strategy_options,
4: required i32 replication_factor,
5: required list<CfDef> cf_defs,
}
service Cassandra {

View File

@ -661,10 +661,23 @@ public class CassandraServer implements Cassandra {
Collections.<byte[], ColumnDefinition>emptyMap());
cfDefs.add(cfmeta);
}
Map<String, String> strategyOptions = null;
if (ksDef.strategy_options != null && !ksDef.strategy_options.isEmpty())
{
strategyOptions = new HashMap<String, String>();
for (Map.Entry<Utf8, Utf8> option : ksDef.strategy_options.entrySet())
{
strategyOptions.put(option.getKey().toString(), option.getValue().toString());
}
}
KSMetaData ksmeta = new KSMetaData(
ksDef.name.toString(),
(Class<? extends AbstractReplicationStrategy>)Class.forName(ksDef.strategy_class.toString()),
strategyOptions,
(int)ksDef.replication_factor,
cfDefs.toArray(new CFMetaData[cfDefs.size()]));
AddKeyspace add = new AddKeyspace(ksmeta);

View File

@ -118,7 +118,7 @@ public class RingCache
{
if (tokenMetadata == null)
throw new RuntimeException("Must refresh endpoints before looking up a key.");
AbstractReplicationStrategy strat = StorageService.getReplicationStrategy(tokenMetadata, keyspace);
return strat.getNaturalEndpoints(partitioner_.getToken(key), keyspace);
AbstractReplicationStrategy strat = StorageService.createReplicationStrategy(tokenMetadata, keyspace);
return strat.getNaturalEndpoints(partitioner_.getToken(key));
}
}

View File

@ -344,11 +344,15 @@ public class DatabaseDescriptor
CommitLog.setSegmentSize(conf.commitlog_rotation_threshold_in_mb * 1024 * 1024);
// Hardcoded system tables
KSMetaData systemMeta = new KSMetaData(Table.SYSTEM_TABLE, LocalStrategy.class, 1, new CFMetaData[]{CFMetaData.StatusCf,
CFMetaData.HintsCf,
CFMetaData.MigrationsCf,
CFMetaData.SchemaCf,
CFMetaData.StatisticsCf
KSMetaData systemMeta = new KSMetaData(Table.SYSTEM_TABLE,
LocalStrategy.class,
null,
1,
new CFMetaData[]{CFMetaData.StatusCf,
CFMetaData.HintsCf,
CFMetaData.MigrationsCf,
CFMetaData.SchemaCf,
CFMetaData.StatisticsCf
});
CFMetaData.map(CFMetaData.StatusCf);
CFMetaData.map(CFMetaData.HintsCf);
@ -618,8 +622,11 @@ public class DatabaseDescriptor
cf.gc_grace_seconds,
metadata);
}
defs.add(new KSMetaData(keyspace.name, strategyClass, keyspace.replication_factor, cfDefs));
defs.add(new KSMetaData(keyspace.name,
strategyClass,
keyspace.strategy_options,
keyspace.replication_factor,
cfDefs));
}
return defs;
@ -750,6 +757,12 @@ public class DatabaseDescriptor
throw new RuntimeException(table + " not found. Failure to call loadSchemas() perhaps?");
return meta.strategyClass;
}
public static KSMetaData getKSMetaData(String table)
{
assert table != null;
return tables.get(table);
}
public static String getJobTrackerAddress()
{

View File

@ -36,13 +36,15 @@ public final class KSMetaData
{
public final String name;
public final Class<? extends AbstractReplicationStrategy> strategyClass;
public final Map<String, String> strategyOptions;
public final int replicationFactor;
private final Map<String, CFMetaData> cfMetaData;
public KSMetaData(String name, Class<? extends AbstractReplicationStrategy> strategyClass, int replicationFactor, CFMetaData... cfDefs)
public KSMetaData(String name, Class<? extends AbstractReplicationStrategy> strategyClass, Map<String, String> strategyOptions, int replicationFactor, CFMetaData... cfDefs)
{
this.name = name;
this.strategyClass = strategyClass == null ? RackUnawareStrategy.class : strategyClass;
this.strategyOptions = strategyOptions;
this.replicationFactor = replicationFactor;
Map<String, CFMetaData> cfmap = new HashMap<String, CFMetaData>();
for (CFMetaData cfm : cfDefs)
@ -59,6 +61,7 @@ public final class KSMetaData
KSMetaData other = (KSMetaData)obj;
return other.name.equals(name)
&& ObjectUtils.equals(other.strategyClass, strategyClass)
&& ObjectUtils.equals(other.strategyOptions, strategyOptions)
&& other.replicationFactor == replicationFactor
&& other.cfMetaData.size() == cfMetaData.size()
&& other.cfMetaData.equals(cfMetaData);
@ -74,6 +77,14 @@ public final class KSMetaData
org.apache.cassandra.avro.KsDef ks = new org.apache.cassandra.avro.KsDef();
ks.name = new Utf8(name);
ks.strategy_class = new Utf8(strategyClass.getName());
if (strategyOptions != null)
{
ks.strategy_options = new HashMap<Utf8, Utf8>();
for (Map.Entry<String, String> e : strategyOptions.entrySet())
{
ks.strategy_options.put(new Utf8(e.getKey()), new Utf8(e.getValue()));
}
}
ks.replication_factor = replicationFactor;
ks.cf_defs = SerDeUtils.createArray(cfMetaData.size(), org.apache.cassandra.avro.CfDef.SCHEMA$);
for (CFMetaData cfm : cfMetaData.values())
@ -83,7 +94,7 @@ public final class KSMetaData
public static KSMetaData inflate(org.apache.cassandra.avro.KsDef ks) throws ConfigurationException
{
Class<AbstractReplicationStrategy> repStratClass = null;
Class<AbstractReplicationStrategy> repStratClass;
try
{
repStratClass = (Class<AbstractReplicationStrategy>)Class.forName(ks.strategy_class.toString());
@ -92,12 +103,21 @@ public final class KSMetaData
{
throw new ConfigurationException("Could not create ReplicationStrategy of type " + ks.strategy_class, ex);
}
Map<String, String> strategyOptions = null;
if (ks.strategy_options != null)
{
strategyOptions = new HashMap<String, String>();
for (Map.Entry<Utf8, Utf8> e : ks.strategy_options.entrySet())
{
strategyOptions.put(e.getKey().toString(), e.getValue().toString());
}
}
int cfsz = (int)ks.cf_defs.size();
CFMetaData[] cfMetaData = new CFMetaData[cfsz];
Iterator<org.apache.cassandra.avro.CfDef> cfiter = ks.cf_defs.iterator();
for (int i = 0; i < cfsz; i++)
cfMetaData[i] = CFMetaData.inflate(cfiter.next());
return new KSMetaData(ks.name.toString(), repStratClass, ks.replication_factor, cfMetaData);
return new KSMetaData(ks.name.toString(), repStratClass, strategyOptions, ks.replication_factor, cfMetaData);
}
}

View File

@ -1,5 +1,7 @@
package org.apache.cassandra.config;
import java.util.Map;
/**
* @deprecated Yaml configuration for Keyspaces and ColumnFamilies is deprecated in 0.7
*/
@ -7,6 +9,7 @@ public class RawKeyspace
{
public String name;
public String replica_placement_strategy;
public Map<String,String> strategy_options;
public Integer replication_factor;
public RawColumnFamily[] column_families;
}

View File

@ -0,0 +1,9 @@
package org.apache.cassandra.config;
import java.util.Map;
public class ReplicationStrategy
{
public String strategy_class;
public Map<String, String> strategy_options;
}

View File

@ -82,7 +82,7 @@ public class AddColumnFamily extends Migration
{
List<CFMetaData> newCfs = new ArrayList<CFMetaData>(ksm.cfMetaData().values());
newCfs.add(cfm);
return new KSMetaData(ksm.name, ksm.strategyClass, ksm.replicationFactor, newCfs.toArray(new CFMetaData[newCfs.size()]));
return new KSMetaData(ksm.name, ksm.strategyClass, ksm.strategyOptions, ksm.replicationFactor, newCfs.toArray(new CFMetaData[newCfs.size()]));
}
public void applyModels() throws IOException

View File

@ -81,7 +81,7 @@ public class DropColumnFamily extends Migration
List<CFMetaData> newCfs = new ArrayList<CFMetaData>(ksm.cfMetaData().values());
newCfs.remove(cfm);
assert newCfs.size() == ksm.cfMetaData().size() - 1;
return new KSMetaData(ksm.name, ksm.strategyClass, ksm.replicationFactor, newCfs.toArray(new CFMetaData[newCfs.size()]));
return new KSMetaData(ksm.name, ksm.strategyClass, ksm.strategyOptions, ksm.replicationFactor, newCfs.toArray(new CFMetaData[newCfs.size()]));
}
@Override

View File

@ -91,7 +91,7 @@ public class RenameColumnFamily extends Migration
assert newCfs.size() == ksm.cfMetaData().size() - 1;
CFMetaData newCfm = CFMetaData.rename(oldCfm, newName);
newCfs.add(newCfm);
return new KSMetaData(ksm.name, ksm.strategyClass, ksm.replicationFactor, newCfs.toArray(new CFMetaData[newCfs.size()]));
return new KSMetaData(ksm.name, ksm.strategyClass, ksm.strategyOptions, ksm.replicationFactor, newCfs.toArray(new CFMetaData[newCfs.size()]));
}
@Override

View File

@ -85,7 +85,7 @@ public class RenameKeyspace extends Migration
CFMetaData.purge(oldCf);
newCfs.add(CFMetaData.renameTable(oldCf, newName));
}
return new KSMetaData(newName, ksm.strategyClass, ksm.replicationFactor, newCfs.toArray(new CFMetaData[newCfs.size()]));
return new KSMetaData(newName, ksm.strategyClass, ksm.strategyOptions, ksm.replicationFactor, newCfs.toArray(new CFMetaData[newCfs.size()]));
}
@Override

View File

@ -149,10 +149,10 @@ public class BootStrapper
{
assert tokenMetadata.sortedTokens().size() > 0;
final AbstractReplicationStrategy strat = StorageService.instance.getReplicationStrategy(table);
Collection<Range> myRanges = strat.getPendingAddressRanges(tokenMetadata, token, address, table);
Collection<Range> myRanges = strat.getPendingAddressRanges(tokenMetadata, token, address);
Multimap<Range, InetAddress> myRangeAddresses = ArrayListMultimap.create();
Multimap<Range, InetAddress> rangeAddresses = strat.getRangeAddresses(tokenMetadata, table);
Multimap<Range, InetAddress> rangeAddresses = strat.getRangeAddresses(tokenMetadata);
for (Range myRange : myRanges)
{
for (Range range : rangeAddresses.keySet())

View File

@ -19,12 +19,16 @@
package org.apache.cassandra.locator;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.*;
import org.apache.cassandra.config.ConfigurationException;
import org.apache.cassandra.service.*;
import org.apache.commons.lang.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -34,13 +38,8 @@ import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.gms.FailureDetector;
import org.apache.cassandra.service.AbstractWriteResponseHandler;
import org.apache.cassandra.service.IResponseResolver;
import org.apache.cassandra.service.QuorumResponseHandler;
import org.apache.cassandra.service.WriteResponseHandler;
import org.apache.cassandra.thrift.ConsistencyLevel;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Pair;
import org.cliffc.high_scale_lib.NonBlockingHashMap;
/**
@ -50,42 +49,44 @@ public abstract class AbstractReplicationStrategy
{
private static final Logger logger = LoggerFactory.getLogger(AbstractReplicationStrategy.class);
public String table;
private TokenMetadata tokenMetadata;
protected final IEndpointSnitch snitch;
private volatile Map<EndpointCacheKey, ArrayList<InetAddress>> cachedEndpoints;
public final IEndpointSnitch snitch;
private volatile Map<Token, ArrayList<InetAddress>> cachedEndpoints;
public Map<String, String> configOptions;
AbstractReplicationStrategy(TokenMetadata tokenMetadata, IEndpointSnitch snitch)
AbstractReplicationStrategy(String table, TokenMetadata tokenMetadata, IEndpointSnitch snitch, Map<String, String> configOptions)
{
assert table != null;
assert snitch != null;
assert tokenMetadata != null;
this.tokenMetadata = tokenMetadata;
this.snitch = snitch;
cachedEndpoints = new NonBlockingHashMap<EndpointCacheKey, ArrayList<InetAddress>>();
cachedEndpoints = new NonBlockingHashMap<Token, ArrayList<InetAddress>>();
this.tokenMetadata.register(this);
this.configOptions = configOptions;
this.table = table;
}
/**
* get the (possibly cached) endpoints that should store the given Token, for the given table.
* get the (possibly cached) endpoints that should store the given Token
* Note that while the endpoints are conceptually a Set (no duplicates will be included),
* we return a List to avoid an extra allocation when sorting by proximity later
* @param searchToken the token the natural endpoints are requested for
* @param table the table the natural endpoints are requested for
* @return a copy of the natural endpoints for the given token and table
* @throws IllegalStateException if the number of requested replicas is greater than the number of known endpints
* @return a copy of the natural endpoints for the given token
* @throws IllegalStateException if the number of requested replicas is greater than the number of known endpoints
*/
public ArrayList<InetAddress> getNaturalEndpoints(Token searchToken, String table) throws IllegalStateException
public ArrayList<InetAddress> getNaturalEndpoints(Token searchToken) throws IllegalStateException
{
int replicas = getReplicationFactor(table);
int replicas = getReplicationFactor();
Token keyToken = TokenMetadata.firstToken(tokenMetadata.sortedTokens(), searchToken);
EndpointCacheKey cacheKey = new EndpointCacheKey(table, keyToken);
ArrayList<InetAddress> endpoints = cachedEndpoints.get(cacheKey);
ArrayList<InetAddress> endpoints = cachedEndpoints.get(keyToken);
if (endpoints == null)
{
TokenMetadata tokenMetadataClone = tokenMetadata.cloneOnlyTokenMap();
keyToken = TokenMetadata.firstToken(tokenMetadataClone.sortedTokens(), searchToken);
cacheKey = new EndpointCacheKey(table, keyToken);
endpoints = new ArrayList<InetAddress>(calculateNaturalEndpoints(searchToken, tokenMetadataClone, table));
cachedEndpoints.put(cacheKey, endpoints);
endpoints = new ArrayList<InetAddress>(calculateNaturalEndpoints(searchToken, tokenMetadataClone));
cachedEndpoints.put(keyToken, endpoints);
}
// calculateNaturalEndpoints should have checked this already, this is a safety
@ -95,27 +96,25 @@ public abstract class AbstractReplicationStrategy
}
/**
* calculate the natural endpionts for the given token, for the given table.
* calculate the natural endpoints for the given token
*
* @see #getNaturalEndpoints(org.apache.cassandra.dht.Token, String)
* @see #getNaturalEndpoints(org.apache.cassandra.dht.Token)
*
* @param searchToken the token the natural endpoints are requested for
* @param table the table the natural endpoints are requested for
* @return a copy of the natural endpoints for the given token and table
* @return a copy of the natural endpoints for the given token
* @throws IllegalStateException if the number of requested replicas is greater than the number of known endpoints
*/
public abstract Set<InetAddress> calculateNaturalEndpoints(Token searchToken, TokenMetadata tokenMetadata, String table) throws IllegalStateException;
public abstract Set<InetAddress> calculateNaturalEndpoints(Token searchToken, TokenMetadata tokenMetadata) throws IllegalStateException;
public AbstractWriteResponseHandler getWriteResponseHandler(Collection<InetAddress> writeEndpoints,
Multimap<InetAddress, InetAddress> hintedEndpoints,
ConsistencyLevel consistencyLevel,
String table)
ConsistencyLevel consistencyLevel)
{
return new WriteResponseHandler(writeEndpoints, hintedEndpoints, consistencyLevel, table);
}
// instance method so test subclasses can override it
int getReplicationFactor(String table)
int getReplicationFactor()
{
return DatabaseDescriptor.getReplicationFactor(table);
}
@ -170,14 +169,14 @@ public abstract class AbstractReplicationStrategy
* (fixing this would probably require merging tokenmetadata into replicationstrategy,
* so we could cache/invalidate cleanly.)
*/
public Multimap<InetAddress, Range> getAddressRanges(TokenMetadata metadata, String table)
public Multimap<InetAddress, Range> getAddressRanges(TokenMetadata metadata)
{
Multimap<InetAddress, Range> map = HashMultimap.create();
for (Token token : metadata.sortedTokens())
{
Range range = metadata.getPrimaryRangeFor(token);
for (InetAddress ep : calculateNaturalEndpoints(token, metadata, table))
for (InetAddress ep : calculateNaturalEndpoints(token, metadata))
{
map.put(ep, range);
}
@ -186,14 +185,14 @@ public abstract class AbstractReplicationStrategy
return map;
}
public Multimap<Range, InetAddress> getRangeAddresses(TokenMetadata metadata, String table)
public Multimap<Range, InetAddress> getRangeAddresses(TokenMetadata metadata)
{
Multimap<Range, InetAddress> map = HashMultimap.create();
for (Token token : metadata.sortedTokens())
{
Range range = metadata.getPrimaryRangeFor(token);
for (InetAddress ep : calculateNaturalEndpoints(token, metadata, table))
for (InetAddress ep : calculateNaturalEndpoints(token, metadata))
{
map.put(range, ep);
}
@ -202,32 +201,27 @@ public abstract class AbstractReplicationStrategy
return map;
}
public Multimap<InetAddress, Range> getAddressRanges(String table)
public Multimap<InetAddress, Range> getAddressRanges()
{
return getAddressRanges(tokenMetadata, table);
return getAddressRanges(tokenMetadata);
}
public Collection<Range> getPendingAddressRanges(TokenMetadata metadata, Token pendingToken, InetAddress pendingAddress, String table)
public Collection<Range> getPendingAddressRanges(TokenMetadata metadata, Token pendingToken, InetAddress pendingAddress)
{
TokenMetadata temp = metadata.cloneOnlyTokenMap();
temp.updateNormalToken(pendingToken, pendingAddress);
return getAddressRanges(temp, table).get(pendingAddress);
return getAddressRanges(temp).get(pendingAddress);
}
public QuorumResponseHandler getQuorumResponseHandler(IResponseResolver responseResolver, ConsistencyLevel consistencyLevel, String table)
public QuorumResponseHandler getQuorumResponseHandler(IResponseResolver responseResolver, ConsistencyLevel consistencyLevel)
{
return new QuorumResponseHandler(responseResolver, consistencyLevel, table);
}
protected static class EndpointCacheKey extends Pair<String, Token>
{
public EndpointCacheKey(String table, Token keyToken) {super(table, keyToken);}
}
protected void clearCachedEndpoints()
{
logger.debug("clearing cached endpoints");
cachedEndpoints = new NonBlockingHashMap<EndpointCacheKey, ArrayList<InetAddress>>();
cachedEndpoints = new NonBlockingHashMap<Token, ArrayList<InetAddress>>();
}
public void invalidateCachedTokenEndpointValues()
@ -239,4 +233,47 @@ public abstract class AbstractReplicationStrategy
{
clearCachedEndpoints();
}
public static AbstractReplicationStrategy createReplicationStrategy(String table,
Class<? extends AbstractReplicationStrategy> strategyClass,
TokenMetadata tokenMetadata,
IEndpointSnitch snitch,
Map<String, String> strategyOptions)
throws ConfigurationException
{
AbstractReplicationStrategy strategy;
Class [] parameterTypes = new Class[] {String.class, TokenMetadata.class, IEndpointSnitch.class, Map.class};
try
{
Constructor<? extends AbstractReplicationStrategy> constructor = strategyClass.getConstructor(parameterTypes);
strategy = constructor.newInstance(table, tokenMetadata, snitch, strategyOptions);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
return strategy;
}
public static AbstractReplicationStrategy createReplicationStrategy(String table,
String strategyClassName,
TokenMetadata tokenMetadata,
IEndpointSnitch snitch,
Map<String, String> strategyOptions)
throws ConfigurationException
{
AbstractReplicationStrategy strategy;
Class<? extends AbstractReplicationStrategy> c;
try
{
c = (Class<? extends AbstractReplicationStrategy>) Class.forName(strategyClassName);
}
catch (ClassNotFoundException e)
{
throw new ConfigurationException("Invalid replication strategy class: " + strategyClassName);
}
return createReplicationStrategy(table, c, tokenMetadata, snitch, strategyOptions);
}
}

View File

@ -21,10 +21,12 @@
package org.apache.cassandra.locator;
import java.io.*;
import java.net.InetAddress;
import java.util.*;
import java.util.Map.Entry;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -33,8 +35,6 @@ import org.apache.cassandra.config.ConfigurationException;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.service.*;
import org.apache.cassandra.thrift.ConsistencyLevel;
import org.apache.cassandra.utils.ResourceWatcher;
import org.apache.cassandra.utils.WrappedRunnable;
/**
* This Replication Strategy takes a property file that gives the intended
@ -51,51 +51,30 @@ import org.apache.cassandra.utils.WrappedRunnable;
*/
public class DatacenterShardStrategy extends AbstractReplicationStrategy
{
private static final String DATACENTER_PROPERTY_FILENAME = "datacenters.properties";
private AbstractRackAwareSnitch snitch;
private volatile Map<String, Map<String, Integer>> datacenters;
private Map<String, Integer> datacenters;
private static final Logger logger = LoggerFactory.getLogger(DatacenterShardStrategy.class);
public DatacenterShardStrategy(TokenMetadata tokenMetadata, IEndpointSnitch snitch) throws ConfigurationException
public DatacenterShardStrategy(String table, TokenMetadata tokenMetadata, IEndpointSnitch snitch, Map<String, String> configOptions) throws ConfigurationException
{
super(tokenMetadata, snitch);
super(table, tokenMetadata, snitch, configOptions);
if ((!(snitch instanceof AbstractRackAwareSnitch)))
throw new IllegalArgumentException("DatacenterShardStrategy requires a rack-aware endpointsnitch");
this.snitch = (AbstractRackAwareSnitch)snitch;
reloadConfiguration();
Runnable runnable = new WrappedRunnable()
{
protected void runMayThrow() throws ConfigurationException
{
reloadConfiguration();
}
};
ResourceWatcher.watch(DATACENTER_PROPERTY_FILENAME, runnable, 60 * 1000);
}
public synchronized void reloadConfiguration() throws ConfigurationException
{
Properties props = PropertyFileSnitch.resourceToProperties(DATACENTER_PROPERTY_FILENAME);
Map<String, Map<String, Integer>> newDatacenters = new HashMap<String, Map<String, Integer>>();
for (Entry entry : props.entrySet())
Map<String, Integer> newDatacenters = new HashMap<String, Integer>();
for (Entry entry : configOptions.entrySet())
{
String[] keys = ((String)entry.getKey()).split(":");
Map<String, Integer> map = newDatacenters.get(keys[0]);
if (map == null)
map = new HashMap<String, Integer>();
map.put(keys[1], Integer.parseInt((String) entry.getValue()));
newDatacenters.put(keys[0], map);
newDatacenters.put((String) entry.getKey(), Integer.parseInt((String) entry.getValue()));
}
datacenters = Collections.unmodifiableMap(newDatacenters);
logger.info(DATACENTER_PROPERTY_FILENAME + " changed, clearing endpoint cache");
clearCachedEndpoints();
}
public Set<InetAddress> calculateNaturalEndpoints(Token searchToken, TokenMetadata tokenMetadata, String table)
public Set<InetAddress> calculateNaturalEndpoints(Token searchToken, TokenMetadata tokenMetadata)
{
int totalReplicas = getReplicationFactor(table);
Map<String, Integer> remainingReplicas = new HashMap<String, Integer>(datacenters.get(table));
int totalReplicas = getReplicationFactor();
Map<String, Integer> remainingReplicas = new HashMap<String, Integer>(datacenters);
Map<String, Set<String>> dcUsedRacks = new HashMap<String, Set<String>>();
Set<InetAddress> endpoints = new LinkedHashSet<InetAddress>(totalReplicas);
@ -152,22 +131,22 @@ public class DatacenterShardStrategy extends AbstractReplicationStrategy
return endpoints;
}
public int getReplicationFactor(String table)
public int getReplicationFactor()
{
int total = 0;
for (int repFactor : datacenters.get(table).values())
for (int repFactor : datacenters.values())
total += repFactor;
return total;
}
public int getReplicationFactor(String dc, String table)
public int getReplicationFactor(String dc)
{
return datacenters.get(table).get(dc);
return datacenters.get(dc);
}
public Set<String> getDatacenters(String table)
public Set<String> getDatacenters()
{
return datacenters.get(table).keySet();
return datacenters.keySet();
}
/**
@ -177,7 +156,7 @@ public class DatacenterShardStrategy extends AbstractReplicationStrategy
* return a DCQRH with a map of all the DC rep factor.
*/
@Override
public AbstractWriteResponseHandler getWriteResponseHandler(Collection<InetAddress> writeEndpoints, Multimap<InetAddress, InetAddress> hintedEndpoints, ConsistencyLevel consistency_level, String table)
public AbstractWriteResponseHandler getWriteResponseHandler(Collection<InetAddress> writeEndpoints, Multimap<InetAddress, InetAddress> hintedEndpoints, ConsistencyLevel consistency_level)
{
if (consistency_level == ConsistencyLevel.DCQUORUM)
{
@ -188,7 +167,7 @@ public class DatacenterShardStrategy extends AbstractReplicationStrategy
{
return new DatacenterSyncWriteResponseHandler(writeEndpoints, hintedEndpoints, consistency_level, table);
}
return super.getWriteResponseHandler(writeEndpoints, hintedEndpoints, consistency_level, table);
return super.getWriteResponseHandler(writeEndpoints, hintedEndpoints, consistency_level);
}
/**
@ -196,12 +175,12 @@ public class DatacenterShardStrategy extends AbstractReplicationStrategy
* level is DCQUORUM/DCQUORUMSYNC then it will return a DCQRH.
*/
@Override
public QuorumResponseHandler getQuorumResponseHandler(IResponseResolver responseResolver, ConsistencyLevel consistencyLevel, String table)
public QuorumResponseHandler getQuorumResponseHandler(IResponseResolver responseResolver, ConsistencyLevel consistencyLevel)
{
if (consistencyLevel.equals(ConsistencyLevel.DCQUORUM) || consistencyLevel.equals(ConsistencyLevel.DCQUORUMSYNC))
{
return new DatacenterQuorumResponseHandler(responseResolver, consistencyLevel, table);
}
return super.getQuorumResponseHandler(responseResolver, consistencyLevel, table);
return super.getQuorumResponseHandler(responseResolver, consistencyLevel);
}
}

View File

@ -21,6 +21,7 @@ package org.apache.cassandra.locator;
import java.net.InetAddress;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.cassandra.utils.FBUtilities;
@ -28,12 +29,12 @@ import org.apache.cassandra.dht.Token;
public class LocalStrategy extends AbstractReplicationStrategy
{
public LocalStrategy(TokenMetadata tokenMetadata, IEndpointSnitch snitch)
public LocalStrategy(String table, TokenMetadata tokenMetadata, IEndpointSnitch snitch, Map<String, String> configOptions)
{
super(tokenMetadata, snitch);
super(table, tokenMetadata, snitch, configOptions);
}
public Set<InetAddress> calculateNaturalEndpoints(Token token, TokenMetadata metadata, String table)
public Set<InetAddress> calculateNaturalEndpoints(Token token, TokenMetadata metadata)
{
Set<InetAddress> endpoints = new HashSet<InetAddress>(1);
InetAddress local = FBUtilities.getLocalAddress();

View File

@ -20,10 +20,7 @@
package org.apache.cassandra.locator;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.*;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.dht.Token;
@ -37,16 +34,16 @@ import org.apache.cassandra.dht.Token;
*/
public class RackAwareStrategy extends AbstractReplicationStrategy
{
public RackAwareStrategy(TokenMetadata tokenMetadata, IEndpointSnitch snitch)
public RackAwareStrategy(String table, TokenMetadata tokenMetadata, IEndpointSnitch snitch, Map<String, String> configOptions)
{
super(tokenMetadata, snitch);
super(table, tokenMetadata, snitch, configOptions);
if (!(snitch instanceof AbstractRackAwareSnitch))
throw new IllegalArgumentException(("RackAwareStrategy requires AbstractRackAwareSnitch."));
}
public Set<InetAddress> calculateNaturalEndpoints(Token token, TokenMetadata metadata, String table)
public Set<InetAddress> calculateNaturalEndpoints(Token token, TokenMetadata metadata)
{
int replicas = getReplicationFactor(table);
int replicas = getReplicationFactor();
Set<InetAddress> endpoints = new LinkedHashSet<InetAddress>(replicas);
ArrayList<Token> tokens = metadata.sortedTokens();

View File

@ -20,12 +20,8 @@
package org.apache.cassandra.locator;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.*;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.dht.Token;
/**
@ -36,14 +32,14 @@ import org.apache.cassandra.dht.Token;
*/
public class RackUnawareStrategy extends AbstractReplicationStrategy
{
public RackUnawareStrategy(TokenMetadata tokenMetadata, IEndpointSnitch snitch)
public RackUnawareStrategy(String table, TokenMetadata tokenMetadata, IEndpointSnitch snitch, Map<String, String> configOptions)
{
super(tokenMetadata, snitch);
super(table, tokenMetadata, snitch, configOptions);
}
public Set<InetAddress> calculateNaturalEndpoints(Token token, TokenMetadata metadata, String table)
public Set<InetAddress> calculateNaturalEndpoints(Token token, TokenMetadata metadata)
{
int replicas = getReplicationFactor(table);
int replicas = getReplicationFactor();
ArrayList<Token> tokens = metadata.sortedTokens();
Set<InetAddress> endpoints = new LinkedHashSet<InetAddress>(replicas);
@ -62,4 +58,5 @@ public class RackUnawareStrategy extends AbstractReplicationStrategy
return endpoints;
}
}

View File

@ -45,6 +45,6 @@ public class DatacenterQuorumResponseHandler<T> extends QuorumResponseHandler<T>
public int determineBlockFor(ConsistencyLevel consistency_level, String table)
{
DatacenterShardStrategy stategy = (DatacenterShardStrategy) StorageService.instance.getReplicationStrategy(table);
return (stategy.getReplicationFactor(localdc, table) / 2) + 1;
return (stategy.getReplicationFactor(localdc) / 2) + 1;
}
}

View File

@ -56,7 +56,6 @@ public class DatacenterSyncWriteResponseHandler extends AbstractWriteResponseHan
private final DatacenterShardStrategy strategy;
private HashMap<String, AtomicInteger> responses = new HashMap<String, AtomicInteger>();
private final String table;
public DatacenterSyncWriteResponseHandler(Collection<InetAddress> writeEndpoints, Multimap<InetAddress, InetAddress> hintedEndpoints, ConsistencyLevel consistencyLevel, String table)
{
@ -64,12 +63,11 @@ public class DatacenterSyncWriteResponseHandler extends AbstractWriteResponseHan
super(writeEndpoints, hintedEndpoints, consistencyLevel);
assert consistencyLevel == ConsistencyLevel.DCQUORUM;
this.table = table;
strategy = (DatacenterShardStrategy) StorageService.instance.getReplicationStrategy(table);
for (String dc : strategy.getDatacenters(table))
for (String dc : strategy.getDatacenters())
{
int rf = strategy.getReplicationFactor(dc, table);
int rf = strategy.getReplicationFactor(dc);
responses.put(dc, new AtomicInteger((rf / 2) + 1));
}
}
@ -88,14 +86,14 @@ public class DatacenterSyncWriteResponseHandler extends AbstractWriteResponseHan
return;
}
// all the quorum conditionas are met
// all the quorum conditions are met
condition.signal();
}
public void assureSufficientLiveNodes() throws UnavailableException
{
Map<String, AtomicInteger> dcEndpoints = new HashMap<String, AtomicInteger>();
for (String dc: strategy.getDatacenters(table))
for (String dc: strategy.getDatacenters())
dcEndpoints.put(dc, new AtomicInteger());
for (InetAddress destination : hintedEndpoints.keySet())
{
@ -105,8 +103,8 @@ public class DatacenterSyncWriteResponseHandler extends AbstractWriteResponseHan
dcEndpoints.get(destinationDC).incrementAndGet();
}
// Throw exception if any of the DC doesnt have livenodes to accept write.
for (String dc: strategy.getDatacenters(table))
// Throw exception if any of the DC doesn't have livenodes to accept write.
for (String dc: strategy.getDatacenters())
{
if (dcEndpoints.get(dc).get() != responses.get(dc).get())
throw new UnavailableException();

View File

@ -63,7 +63,7 @@ public class DatacenterWriteResponseHandler extends WriteResponseHandler
protected int determineBlockFor(String table)
{
DatacenterShardStrategy strategy = (DatacenterShardStrategy) StorageService.instance.getReplicationStrategy(table);
return (strategy.getReplicationFactor(localdc, table) / 2) + 1;
return (strategy.getReplicationFactor(localdc) / 2) + 1;
}
@ -92,4 +92,4 @@ public class DatacenterWriteResponseHandler extends WriteResponseHandler
throw new UnavailableException();
}
}
}
}

View File

@ -200,7 +200,7 @@ public class StorageProxy implements StorageProxyMBean
Multimap<InetAddress, InetAddress> hintedEndpoints = rs.getHintedEndpoints(writeEndpoints);
// send out the writes, as in mutate() above, but this time with a callback that tracks responses
final AbstractWriteResponseHandler responseHandler = rs.getWriteResponseHandler(writeEndpoints, hintedEndpoints, consistency_level, table);
final AbstractWriteResponseHandler responseHandler = rs.getWriteResponseHandler(writeEndpoints, hintedEndpoints, consistency_level);
responseHandler.assureSufficientLiveNodes();
responseHandlers.add(responseHandler);
@ -421,7 +421,7 @@ public class StorageProxy implements StorageProxyMBean
logger.debug("strongread reading " + (m == message ? "data" : "digest") + " for " + command + " from " + m.getMessageId() + "@" + endpoint);
}
AbstractReplicationStrategy rs = StorageService.instance.getReplicationStrategy(command.table);
QuorumResponseHandler<Row> quorumResponseHandler = rs.getQuorumResponseHandler(new ReadResponseResolver(command.table), consistency_level, command.table);
QuorumResponseHandler<Row> quorumResponseHandler = rs.getQuorumResponseHandler(new ReadResponseResolver(command.table), consistency_level);
MessagingService.instance.sendRR(messages, endpoints, quorumResponseHandler);
quorumResponseHandlers.add(quorumResponseHandler);
commandEndpoints.add(endpoints);
@ -449,7 +449,7 @@ public class StorageProxy implements StorageProxyMBean
if (randomlyReadRepair(command))
{
AbstractReplicationStrategy rs = StorageService.instance.getReplicationStrategy(command.table);
QuorumResponseHandler<Row> qrhRepair = rs.getQuorumResponseHandler(new ReadResponseResolver(command.table), ConsistencyLevel.QUORUM, command.table);
QuorumResponseHandler<Row> qrhRepair = rs.getQuorumResponseHandler(new ReadResponseResolver(command.table), ConsistencyLevel.QUORUM);
if (logger.isDebugEnabled())
logger.debug("Digest mismatch:", ex);
Message messageRepair = command.makeReadMessage();
@ -530,7 +530,7 @@ public class StorageProxy implements StorageProxyMBean
// collect replies and resolve according to consistency level
RangeSliceResponseResolver resolver = new RangeSliceResponseResolver(command.keyspace, liveEndpoints);
AbstractReplicationStrategy rs = StorageService.instance.getReplicationStrategy(command.keyspace);
QuorumResponseHandler<List<Row>> handler = rs.getQuorumResponseHandler(resolver, consistency_level, command.keyspace);
QuorumResponseHandler<List<Row>> handler = rs.getQuorumResponseHandler(resolver, consistency_level);
// TODO bail early if live endpoints can't satisfy requested
// consistency level
for (InetAddress endpoint : liveEndpoints)
@ -795,7 +795,7 @@ public class StorageProxy implements StorageProxyMBean
Message message = command.getMessage();
RangeSliceResponseResolver resolver = new RangeSliceResponseResolver(command.keyspace, endpoints);
AbstractReplicationStrategy rs = StorageService.instance.getReplicationStrategy(command.keyspace);
QuorumResponseHandler<List<Row>> handler = rs.getQuorumResponseHandler(resolver, consistency_level, command.keyspace);
QuorumResponseHandler<List<Row>> handler = rs.getQuorumResponseHandler(resolver, consistency_level);
MessagingService.instance.sendRR(message, endpoints.get(0), handler);
try
{

View File

@ -21,7 +21,6 @@ package org.apache.cassandra.service;
import java.io.IOError;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Constructor;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
@ -62,7 +61,6 @@ import org.apache.cassandra.io.DeletionService;
import org.apache.cassandra.io.sstable.SSTableReader;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.locator.AbstractReplicationStrategy;
import org.apache.cassandra.locator.IEndpointSnitch;
import org.apache.cassandra.locator.TokenMetadata;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.net.ResponseVerbHandler;
@ -274,7 +272,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
public void initReplicationStrategy(String table)
{
AbstractReplicationStrategy strat = getReplicationStrategy(tokenMetadata_, table);
AbstractReplicationStrategy strat = createReplicationStrategy(tokenMetadata_, table);
replicationStrategies.put(table, strat);
}
@ -283,17 +281,19 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
replicationStrategies.remove(table);
}
public static AbstractReplicationStrategy getReplicationStrategy(TokenMetadata tokenMetadata, String table)
public static AbstractReplicationStrategy createReplicationStrategy(TokenMetadata tokenMetadata, String table)
{
AbstractReplicationStrategy replicationStrategy = null;
Class<? extends AbstractReplicationStrategy> cls = DatabaseDescriptor.getReplicaPlacementStrategyClass(table);
if (cls == null)
throw new RuntimeException(String.format("No replica strategy configured for %s", table));
Class [] parameterTypes = new Class[] { TokenMetadata.class, IEndpointSnitch.class};
AbstractReplicationStrategy replicationStrategy;
KSMetaData ksm = DatabaseDescriptor.getKSMetaData(table);
try
{
Constructor<? extends AbstractReplicationStrategy> constructor = cls.getConstructor(parameterTypes);
replicationStrategy = constructor.newInstance(tokenMetadata, DatabaseDescriptor.getEndpointSnitch());
replicationStrategy = AbstractReplicationStrategy.createReplicationStrategy(
table,
ksm.strategyClass,
tokenMetadata,
DatabaseDescriptor.getEndpointSnitch(),
ksm.strategyOptions
);
}
catch (Exception e)
{
@ -543,7 +543,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
Map<Range, List<InetAddress>> rangeToEndpointMap = new HashMap<Range, List<InetAddress>>();
for (Range range : ranges)
{
rangeToEndpointMap.put(range, getReplicationStrategy(keyspace).getNaturalEndpoints(range.right, keyspace));
rangeToEndpointMap.put(range, getReplicationStrategy(keyspace).getNaturalEndpoints(range.right));
}
return rangeToEndpointMap;
}
@ -785,7 +785,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
return;
}
Multimap<InetAddress, Range> addressRanges = strategy.getAddressRanges(table);
Multimap<InetAddress, Range> addressRanges = strategy.getAddressRanges();
// Copy of metadata reflecting the situation after all leave operations are finished.
TokenMetadata allLeftMetadata = tm.cloneAfterAllLeft();
@ -799,8 +799,8 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
// all leaving nodes are gone.
for (Range range : affectedRanges)
{
Set<InetAddress> currentEndpoints = strategy.calculateNaturalEndpoints(range.right, tm, table);
Set<InetAddress> newEndpoints = strategy.calculateNaturalEndpoints(range.right, allLeftMetadata, table);
Set<InetAddress> currentEndpoints = strategy.calculateNaturalEndpoints(range.right, tm);
Set<InetAddress> newEndpoints = strategy.calculateNaturalEndpoints(range.right, allLeftMetadata);
newEndpoints.removeAll(currentEndpoints);
pendingRanges.putAll(range, newEndpoints);
}
@ -815,7 +815,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
InetAddress endpoint = entry.getValue();
allLeftMetadata.updateNormalToken(entry.getKey(), endpoint);
for (Range range : strategy.getAddressRanges(allLeftMetadata, table).get(endpoint))
for (Range range : strategy.getAddressRanges(allLeftMetadata).get(endpoint))
pendingRanges.put(range, endpoint);
allLeftMetadata.removeEndpoint(endpoint);
}
@ -860,7 +860,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
if (logger_.isDebugEnabled())
logger_.debug(endpoint + " was removed, my added ranges: " + StringUtils.join(myNewRanges, ", "));
Multimap<Range, InetAddress> rangeAddresses = getReplicationStrategy(table).getRangeAddresses(tokenMetadata_, table);
Multimap<Range, InetAddress> rangeAddresses = getReplicationStrategy(table).getRangeAddresses(tokenMetadata_);
Multimap<InetAddress, Range> sourceRanges = HashMultimap.create();
IFailureDetector failureDetector = FailureDetector.instance;
@ -909,7 +909,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
// Find (for each range) all nodes that store replicas for these ranges as well
for (Range range : ranges)
currentReplicaEndpoints.put(range, getReplicationStrategy(table).calculateNaturalEndpoints(range.right, tokenMetadata_, table));
currentReplicaEndpoints.put(range, getReplicationStrategy(table).calculateNaturalEndpoints(range.right, tokenMetadata_));
TokenMetadata temp = tokenMetadata_.cloneAfterAllLeft();
@ -927,7 +927,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
// range.
for (Range range : ranges)
{
Set<InetAddress> newReplicaEndpoints = getReplicationStrategy(table).calculateNaturalEndpoints(range.right, temp, table);
Set<InetAddress> newReplicaEndpoints = getReplicationStrategy(table).calculateNaturalEndpoints(range.right, temp);
newReplicaEndpoints.removeAll(currentReplicaEndpoints.get(range));
if (logger_.isDebugEnabled())
if (newReplicaEndpoints.isEmpty())
@ -1227,7 +1227,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
*/
Collection<Range> getRangesForEndpoint(String table, InetAddress ep)
{
return getReplicationStrategy(table).getAddressRanges(table).get(ep);
return getReplicationStrategy(table).getAddressRanges().get(ep);
}
/**
@ -1277,7 +1277,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
*/
public List<InetAddress> getNaturalEndpoints(String table, Token token)
{
return getReplicationStrategy(table).getNaturalEndpoints(token, table);
return getReplicationStrategy(table).getNaturalEndpoints(token);
}
/**
@ -1295,7 +1295,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
public List<InetAddress> getLiveNaturalEndpoints(String table, Token token)
{
List<InetAddress> liveEps = new ArrayList<InetAddress>();
List<InetAddress> endpoints = getReplicationStrategy(table).getNaturalEndpoints(token, table);
List<InetAddress> endpoints = getReplicationStrategy(table).getNaturalEndpoints(token);
for (InetAddress endpoint : endpoints)
{

View File

@ -889,8 +889,9 @@ public class CassandraServer implements Cassandra.Iface
KSMetaData ksm = new KSMetaData(
ks_def.name,
(Class<? extends AbstractReplicationStrategy>)Class.forName(ks_def.strategy_class),
ks_def.replication_factor,
(Class<? extends AbstractReplicationStrategy>)Class.forName(ks_def.strategy_class),
ks_def.strategy_options,
ks_def.replication_factor,
cfDefs.toArray(new CFMetaData[cfDefs.size()]));
applyMigrationOnStage(new AddKeyspace(ksm));
return DatabaseDescriptor.getDefsVersion().toString();

View File

@ -142,7 +142,7 @@ class ThriftTester(BaseTester):
self.client.transport.close()
def define_schema(self):
keyspace1 = Cassandra.KsDef('Keyspace1', 'org.apache.cassandra.locator.RackUnawareStrategy', 1,
keyspace1 = Cassandra.KsDef('Keyspace1', 'org.apache.cassandra.locator.RackUnawareStrategy', None, 1,
[
Cassandra.CfDef('Keyspace1', 'Standard1'),
Cassandra.CfDef('Keyspace1', 'Standard2'),
@ -156,7 +156,7 @@ class ThriftTester(BaseTester):
Cassandra.CfDef('Keyspace1', 'Indexed1', column_metadata=[Cassandra.ColumnDef('birthdate', 'LongType', Cassandra.IndexType.KEYS, 'birthdate')]),
])
keyspace2 = Cassandra.KsDef('Keyspace2', 'org.apache.cassandra.locator.RackUnawareStrategy', 1,
keyspace2 = Cassandra.KsDef('Keyspace2', 'org.apache.cassandra.locator.RackUnawareStrategy', None, 1,
[
Cassandra.CfDef('Keyspace2', 'Standard1'),
Cassandra.CfDef('Keyspace2', 'Standard3'),
@ -164,12 +164,12 @@ class ThriftTester(BaseTester):
Cassandra.CfDef('Keyspace2', 'Super4', column_type='Super', subcomparator_type='TimeUUIDType'),
])
keyspace3 = Cassandra.KsDef('Keyspace3', 'org.apache.cassandra.locator.RackUnawareStrategy', 5,
keyspace3 = Cassandra.KsDef('Keyspace3', 'org.apache.cassandra.locator.RackUnawareStrategy', None, 5,
[
Cassandra.CfDef('Keyspace3', 'Standard1'),
])
keyspace4 = Cassandra.KsDef('Keyspace4', 'org.apache.cassandra.locator.RackUnawareStrategy', 3,
keyspace4 = Cassandra.KsDef('Keyspace4', 'org.apache.cassandra.locator.RackUnawareStrategy', None, 3,
[
Cassandra.CfDef('Keyspace4', 'Standard1'),
Cassandra.CfDef('Keyspace4', 'Standard3'),

View File

@ -1124,7 +1124,7 @@ class TestMutations(ThriftTester):
def test_system_keyspace_operations(self):
""" Test keyspace (add, drop, rename) operations """
# create
keyspace = KsDef('CreateKeyspace', 'org.apache.cassandra.locator.RackUnawareStrategy', 1,
keyspace = KsDef('CreateKeyspace', 'org.apache.cassandra.locator.RackUnawareStrategy', {}, 1,
[CfDef('CreateKeyspace', 'CreateKsCf')])
client.system_add_keyspace(keyspace)
newks = client.describe_keyspace('CreateKeyspace')

View File

@ -83,9 +83,9 @@ public class DatabaseDescriptorTest
assert DatabaseDescriptor.getNonSystemTables().size() == 0;
// add a few.
AddKeyspace ks0 = new AddKeyspace(new KSMetaData("ks0", RackUnawareStrategy.class, 3));
AddKeyspace ks0 = new AddKeyspace(new KSMetaData("ks0", RackUnawareStrategy.class, null, 3));
ks0.apply();
AddKeyspace ks1 = new AddKeyspace(new KSMetaData("ks1", RackUnawareStrategy.class, 3));
AddKeyspace ks1 = new AddKeyspace(new KSMetaData("ks1", RackUnawareStrategy.class, null, 3));
ks1.apply();
assert DatabaseDescriptor.getTableDefinition("ks0") != null;

View File

@ -66,7 +66,7 @@ public class DefsTest extends CleanupHelper
try
{
new AddColumnFamily(newCf).apply();
throw new AssertionError("You should't be able to do anything to a keyspace that doesn't exist.");
throw new AssertionError("You shouldn't be able to do anything to a keyspace that doesn't exist.");
}
catch (ConfigurationException expected)
{
@ -262,7 +262,7 @@ public class DefsTest extends CleanupHelper
{
DecoratedKey dk = Util.dk("key0");
CFMetaData newCf = new CFMetaData("NewKeyspace1", "AddedStandard1", ColumnFamilyType.Standard, ClockType.Timestamp, UTF8Type.instance, null, new TimestampReconciler(), "A new cf for a new ks", 0, false, 1.0, 0, 864000, Collections.<byte[], ColumnDefinition>emptyMap());
KSMetaData newKs = new KSMetaData(newCf.tableName, RackUnawareStrategy.class, 5, newCf);
KSMetaData newKs = new KSMetaData(newCf.tableName, RackUnawareStrategy.class, null, 5, newCf);
new AddKeyspace(newKs).apply();
@ -414,7 +414,7 @@ public class DefsTest extends CleanupHelper
{
assert DatabaseDescriptor.getTableDefinition("EmptyKeyspace") == null;
KSMetaData newKs = new KSMetaData("EmptyKeyspace", RackUnawareStrategy.class, 5, new CFMetaData[]{});
KSMetaData newKs = new KSMetaData("EmptyKeyspace", RackUnawareStrategy.class, null, 5);
new AddKeyspace(newKs).apply();
assert DatabaseDescriptor.getTableDefinition("EmptyKeyspace") != null;

View File

@ -23,7 +23,9 @@ import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
import org.junit.Test;
@ -40,16 +42,22 @@ public class DatacenterShardStrategyTest
@Test
public void testProperties() throws IOException, ParserConfigurationException, SAXException, ConfigurationException
{
PropertyFileSnitch snitch = new PropertyFileSnitch();
IEndpointSnitch snitch = new PropertyFileSnitch();
TokenMetadata metadata = new TokenMetadata();
createDummyTokens(metadata);
// Set the localhost to the tokenmetadata. Embeded cassandra way?
DatacenterShardStrategy strategy = new DatacenterShardStrategy(metadata, snitch);
assert strategy.getReplicationFactor("DC1", table) == 3;
assert strategy.getReplicationFactor("DC2", table) == 2;
assert strategy.getReplicationFactor("DC3", table) == 1;
Map<String, String> configOptions = new HashMap<String, String>();
configOptions.put("DC1", "3");
configOptions.put("DC2", "2");
configOptions.put("DC3", "1");
// Set the localhost to the tokenmetadata. Embedded cassandra way?
DatacenterShardStrategy strategy = new DatacenterShardStrategy(table, metadata, snitch, configOptions);
assert strategy.getReplicationFactor("DC1") == 3;
assert strategy.getReplicationFactor("DC2") == 2;
assert strategy.getReplicationFactor("DC3") == 1;
// Query for the natural hosts
ArrayList<InetAddress> endpoints = strategy.getNaturalEndpoints(new StringToken("123"), table);
ArrayList<InetAddress> endpoints = strategy.getNaturalEndpoints(new StringToken("123"));
assert 6 == endpoints.size();
assert 6 == new HashSet<InetAddress>(endpoints).size(); // ensure uniqueness
}

View File

@ -60,7 +60,7 @@ public class RackAwareStrategyTest
{
RackInferringSnitch endpointSnitch = new RackInferringSnitch();
AbstractReplicationStrategy strategy = new RackAwareStrategy(tmd, endpointSnitch);
AbstractReplicationStrategy strategy = new RackAwareStrategy("Keyspace1", tmd, endpointSnitch, null);
addEndpoint("0", "5", "254.0.0.1");
addEndpoint("10", "15", "254.0.0.2");
addEndpoint("20", "25", "254.0.0.3");
@ -85,7 +85,7 @@ public class RackAwareStrategyTest
{
RackInferringSnitch endpointSnitch = new RackInferringSnitch();
AbstractReplicationStrategy strategy = new RackAwareStrategy(tmd, endpointSnitch);
AbstractReplicationStrategy strategy = new RackAwareStrategy("Keyspace1", tmd, endpointSnitch, null);
addEndpoint("0", "5", "254.0.0.1");
addEndpoint("10", "15", "254.0.0.2");
addEndpoint("20", "25", "254.1.0.3");
@ -111,7 +111,7 @@ public class RackAwareStrategyTest
{
RackInferringSnitch endpointSnitch = new RackInferringSnitch();
AbstractReplicationStrategy strategy = new RackAwareStrategy(tmd, endpointSnitch);
AbstractReplicationStrategy strategy = new RackAwareStrategy("Keyspace1", tmd, endpointSnitch, null);
addEndpoint("0", "5", "254.0.0.1");
addEndpoint("10", "15", "254.0.0.2");
addEndpoint("20", "25", "254.0.1.3");
@ -160,7 +160,7 @@ public class RackAwareStrategyTest
{
for (Token keyToken : keyTokens)
{
List<InetAddress> endpoints = strategy.getNaturalEndpoints(keyToken, table);
List<InetAddress> endpoints = strategy.getNaturalEndpoints(keyToken);
for (int j = 0; j < endpoints.size(); j++)
{
ArrayList<InetAddress> hostsExpected = expectedResults.get(keyToken.toString());

View File

@ -26,6 +26,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import org.apache.cassandra.config.ConfigurationException;
import org.junit.Test;
import static org.junit.Assert.*;
@ -45,7 +46,7 @@ public class RackUnawareStrategyTest extends SchemaLoader
try
{
rs = StorageService.instance.getReplicationStrategy("SomeBogusTableThatDoesntExist");
throw new AssertionError("SS.getReplicationStrategy() should have thrown a RuntimeException.");
throw new AssertionError("SS.createReplicationStrategy() should have thrown a RuntimeException.");
}
catch (RuntimeException ex)
{
@ -54,26 +55,21 @@ public class RackUnawareStrategyTest extends SchemaLoader
}
@Test
public void testBigIntegerEndpoints() throws UnknownHostException
public void testBigIntegerEndpoints() throws UnknownHostException, ConfigurationException
{
TokenMetadata tmd = new TokenMetadata();
AbstractReplicationStrategy strategy = new RackUnawareStrategy(tmd, new SimpleSnitch());
List<Token> endpointTokens = new ArrayList<Token>();
List<Token> keyTokens = new ArrayList<Token>();
for (int i = 0; i < 5; i++) {
endpointTokens.add(new BigIntegerToken(String.valueOf(10 * i)));
keyTokens.add(new BigIntegerToken(String.valueOf(10 * i + 5)));
}
verifyGetNaturalEndpoints(tmd, strategy, endpointTokens.toArray(new Token[0]), keyTokens.toArray(new Token[0]));
verifyGetNaturalEndpoints(endpointTokens.toArray(new Token[0]), keyTokens.toArray(new Token[0]));
}
@Test
public void testStringEndpoints() throws UnknownHostException
public void testStringEndpoints() throws UnknownHostException, ConfigurationException
{
TokenMetadata tmd = new TokenMetadata();
IPartitioner partitioner = new OrderPreservingPartitioner();
AbstractReplicationStrategy strategy = new RackUnawareStrategy(tmd, new SimpleSnitch());
List<Token> endpointTokens = new ArrayList<Token>();
List<Token> keyTokens = new ArrayList<Token>();
@ -81,15 +77,19 @@ public class RackUnawareStrategyTest extends SchemaLoader
endpointTokens.add(new StringToken(String.valueOf((char)('a' + i * 2))));
keyTokens.add(partitioner.getToken(String.valueOf((char)('a' + i * 2 + 1)).getBytes()));
}
verifyGetNaturalEndpoints(tmd, strategy, endpointTokens.toArray(new Token[0]), keyTokens.toArray(new Token[0]));
verifyGetNaturalEndpoints(endpointTokens.toArray(new Token[0]), keyTokens.toArray(new Token[0]));
}
// given a list of endpoint tokens, and a set of key tokens falling between the endpoint tokens,
// make sure that the Strategy picks the right endpoints for the keys.
private void verifyGetNaturalEndpoints(TokenMetadata tmd, AbstractReplicationStrategy strategy, Token[] endpointTokens, Token[] keyTokens) throws UnknownHostException
private void verifyGetNaturalEndpoints(Token[] endpointTokens, Token[] keyTokens) throws UnknownHostException, ConfigurationException
{
TokenMetadata tmd;
AbstractReplicationStrategy strategy;
for (String table : DatabaseDescriptor.getNonSystemTables())
{
tmd = new TokenMetadata();
strategy = getStrategy(table, tmd);
List<InetAddress> hosts = new ArrayList<InetAddress>();
for (int i = 0; i < endpointTokens.length; i++)
{
@ -100,7 +100,7 @@ public class RackUnawareStrategyTest extends SchemaLoader
for (int i = 0; i < keyTokens.length; i++)
{
List<InetAddress> endpoints = strategy.getNaturalEndpoints(keyTokens[i], table);
List<InetAddress> endpoints = strategy.getNaturalEndpoints(keyTokens[i]);
assertEquals(DatabaseDescriptor.getReplicationFactor(table), endpoints.size());
List<InetAddress> correctEndpoints = new ArrayList<InetAddress>();
for (int j = 0; j < endpoints.size(); j++)
@ -111,13 +111,12 @@ public class RackUnawareStrategyTest extends SchemaLoader
}
@Test
public void testGetEndpointsDuringBootstrap() throws UnknownHostException
public void testGetEndpointsDuringBootstrap() throws UnknownHostException, ConfigurationException
{
// the token difference will be RING_SIZE * 2.
final int RING_SIZE = 10;
TokenMetadata tmd = new TokenMetadata();
TokenMetadata oldTmd = StorageServiceAccessor.setTokenMetadata(tmd);
AbstractReplicationStrategy strategy = new RackUnawareStrategy(tmd, new SimpleSnitch());
Token[] endpointTokens = new Token[RING_SIZE];
Token[] keyTokens = new Token[RING_SIZE];
@ -141,14 +140,18 @@ public class RackUnawareStrategyTest extends SchemaLoader
InetAddress bootstrapEndpoint = InetAddress.getByName("127.0.0.11");
tmd.addBootstrapToken(bsToken, bootstrapEndpoint);
AbstractReplicationStrategy strategy = null;
for (String table : DatabaseDescriptor.getNonSystemTables())
{
strategy = getStrategy(table, tmd);
StorageService.calculatePendingRanges(strategy, table);
int replicationFactor = DatabaseDescriptor.getReplicationFactor(table);
for (int i = 0; i < keyTokens.length; i++)
{
Collection<InetAddress> endpoints = tmd.getWriteEndpoints(keyTokens[i], table, strategy.getNaturalEndpoints(keyTokens[i], table));
Collection<InetAddress> endpoints = tmd.getWriteEndpoints(keyTokens[i], table, strategy.getNaturalEndpoints(keyTokens[i]));
assertTrue(endpoints.size() >= replicationFactor);
for (int j = 0; j < replicationFactor; j++)
@ -167,4 +170,24 @@ public class RackUnawareStrategyTest extends SchemaLoader
StorageServiceAccessor.setTokenMetadata(oldTmd);
}
private AbstractReplicationStrategy getStrategyWithNewTokenMetadata(AbstractReplicationStrategy strategy, TokenMetadata newTmd) throws ConfigurationException
{
return AbstractReplicationStrategy.createReplicationStrategy(
strategy.table,
strategy.getClass().getName(),
newTmd,
strategy.snitch,
strategy.configOptions);
}
private AbstractReplicationStrategy getStrategy(String table, TokenMetadata tmd) throws ConfigurationException
{
return AbstractReplicationStrategy.createReplicationStrategy(
table,
"org.apache.cassandra.locator.RackUnawareStrategy",
tmd,
new SimpleSnitch(),
null);
}
}

View File

@ -21,10 +21,9 @@ package org.apache.cassandra.locator;
import java.lang.reflect.Constructor;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
import org.apache.cassandra.service.StorageService;
import org.apache.commons.lang.StringUtils;
import org.junit.Test;
@ -39,12 +38,12 @@ public class ReplicationStrategyEndpointCacheTest extends SchemaLoader
private Token searchToken;
private AbstractReplicationStrategy strategy;
public void setup(Class stratClass) throws Exception
public void setup(Class stratClass, Map<String, String> strategyOptions) throws Exception
{
tmd = new TokenMetadata();
searchToken = new BigIntegerToken(String.valueOf(15));
Constructor constructor = stratClass.getConstructor(TokenMetadata.class, IEndpointSnitch.class);
strategy = (AbstractReplicationStrategy) constructor.newInstance(tmd, new PropertyFileSnitch());
strategy = getStrategyWithNewTokenMetadata(StorageService.instance.getReplicationStrategy("Keyspace3"), tmd);
tmd.updateNormalToken(new BigIntegerToken(String.valueOf(10)), InetAddress.getByName("127.0.0.1"));
tmd.updateNormalToken(new BigIntegerToken(String.valueOf(20)), InetAddress.getByName("127.0.0.2"));
@ -59,73 +58,73 @@ public class ReplicationStrategyEndpointCacheTest extends SchemaLoader
@Test
public void testEndpointsWereCached() throws Exception
{
runEndpointsWereCachedTest(FakeRackUnawareStrategy.class);
runEndpointsWereCachedTest(FakeRackAwareStrategy.class);
runEndpointsWereCachedTest(FakeDatacenterShardStrategy.class);
runEndpointsWereCachedTest(FakeRackUnawareStrategy.class, null);
runEndpointsWereCachedTest(FakeRackAwareStrategy.class, null);
runEndpointsWereCachedTest(FakeDatacenterShardStrategy.class, new HashMap<String, String>());
}
public void runEndpointsWereCachedTest(Class stratClass) throws Exception
public void runEndpointsWereCachedTest(Class stratClass, Map<String, String> configOptions) throws Exception
{
setup(stratClass);
assert strategy.getNaturalEndpoints(searchToken, "Keyspace3").equals(strategy.getNaturalEndpoints(searchToken, "Keyspace3"));
setup(stratClass, configOptions);
assert strategy.getNaturalEndpoints(searchToken).equals(strategy.getNaturalEndpoints(searchToken));
}
@Test
public void testCacheRespectsTokenChanges() throws Exception
{
runCacheRespectsTokenChangesTest(RackUnawareStrategy.class);
runCacheRespectsTokenChangesTest(RackAwareStrategy.class);
runCacheRespectsTokenChangesTest(DatacenterShardStrategy.class);
runCacheRespectsTokenChangesTest(RackUnawareStrategy.class, null);
runCacheRespectsTokenChangesTest(RackAwareStrategy.class, null);
runCacheRespectsTokenChangesTest(DatacenterShardStrategy.class, new HashMap<String, String>());
}
public void runCacheRespectsTokenChangesTest(Class stratClass) throws Exception
public void runCacheRespectsTokenChangesTest(Class stratClass, Map<String, String> configOptions) throws Exception
{
setup(stratClass);
setup(stratClass, configOptions);
ArrayList<InetAddress> initial;
ArrayList<InetAddress> endpoints;
endpoints = strategy.getNaturalEndpoints(searchToken, "Keyspace3");
endpoints = strategy.getNaturalEndpoints(searchToken);
assert endpoints.size() == 5 : StringUtils.join(endpoints, ",");
// test token addition, in DC2 before existing token
initial = strategy.getNaturalEndpoints(searchToken, "Keyspace3");
initial = strategy.getNaturalEndpoints(searchToken);
tmd.updateNormalToken(new BigIntegerToken(String.valueOf(35)), InetAddress.getByName("127.0.0.5"));
endpoints = strategy.getNaturalEndpoints(searchToken, "Keyspace3");
endpoints = strategy.getNaturalEndpoints(searchToken);
assert endpoints.size() == 5 : StringUtils.join(endpoints, ",");
assert !endpoints.equals(initial);
// test token removal, newly created token
initial = strategy.getNaturalEndpoints(searchToken, "Keyspace3");
initial = strategy.getNaturalEndpoints(searchToken);
tmd.removeEndpoint(InetAddress.getByName("127.0.0.5"));
endpoints = strategy.getNaturalEndpoints(searchToken, "Keyspace3");
endpoints = strategy.getNaturalEndpoints(searchToken);
assert endpoints.size() == 5 : StringUtils.join(endpoints, ",");
assert !endpoints.contains(InetAddress.getByName("127.0.0.5"));
assert !endpoints.equals(initial);
// test token change
initial = strategy.getNaturalEndpoints(searchToken, "Keyspace3");
initial = strategy.getNaturalEndpoints(searchToken);
//move .8 after search token but before other DC3
tmd.updateNormalToken(new BigIntegerToken(String.valueOf(25)), InetAddress.getByName("127.0.0.8"));
endpoints = strategy.getNaturalEndpoints(searchToken, "Keyspace3");
endpoints = strategy.getNaturalEndpoints(searchToken);
assert endpoints.size() == 5 : StringUtils.join(endpoints, ",");
assert !endpoints.equals(initial);
assert !endpoints.equals(initial);
}
protected static class FakeRackUnawareStrategy extends RackUnawareStrategy
{
private boolean called = false;
public FakeRackUnawareStrategy(TokenMetadata tokenMetadata, IEndpointSnitch snitch)
public FakeRackUnawareStrategy(String table, TokenMetadata tokenMetadata, IEndpointSnitch snitch, Map<String, String> configOptions)
{
super(tokenMetadata, snitch);
super(table, tokenMetadata, snitch, configOptions);
}
@Override
public Set<InetAddress> calculateNaturalEndpoints(Token token, TokenMetadata metadata, String table)
public Set<InetAddress> calculateNaturalEndpoints(Token token, TokenMetadata metadata)
{
assert !called : "calculateNaturalEndpoints was already called, result should have been cached";
called = true;
return super.calculateNaturalEndpoints(token, metadata, table);
return super.calculateNaturalEndpoints(token, metadata);
}
}
@ -133,17 +132,17 @@ public class ReplicationStrategyEndpointCacheTest extends SchemaLoader
{
private boolean called = false;
public FakeRackAwareStrategy(TokenMetadata tokenMetadata, IEndpointSnitch snitch)
public FakeRackAwareStrategy(String table, TokenMetadata tokenMetadata, IEndpointSnitch snitch, Map<String, String> configOptions)
{
super(tokenMetadata, snitch);
super(table, tokenMetadata, snitch, configOptions);
}
@Override
public Set<InetAddress> calculateNaturalEndpoints(Token token, TokenMetadata metadata, String table)
public Set<InetAddress> calculateNaturalEndpoints(Token token, TokenMetadata metadata)
{
assert !called : "calculateNaturalEndpoints was already called, result should have been cached";
called = true;
return super.calculateNaturalEndpoints(token, metadata, table);
return super.calculateNaturalEndpoints(token, metadata);
}
}
@ -151,17 +150,28 @@ public class ReplicationStrategyEndpointCacheTest extends SchemaLoader
{
private boolean called = false;
public FakeDatacenterShardStrategy(TokenMetadata tokenMetadata, IEndpointSnitch snitch) throws ConfigurationException
public FakeDatacenterShardStrategy(String table, TokenMetadata tokenMetadata, IEndpointSnitch snitch, Map<String, String> configOptions) throws ConfigurationException
{
super(tokenMetadata, snitch);
super(table, tokenMetadata, snitch, configOptions);
}
@Override
public Set<InetAddress> calculateNaturalEndpoints(Token token, TokenMetadata metadata, String table)
public Set<InetAddress> calculateNaturalEndpoints(Token token, TokenMetadata metadata)
{
assert !called : "calculateNaturalEndpoints was already called, result should have been cached";
called = true;
return super.calculateNaturalEndpoints(token, metadata, table);
return super.calculateNaturalEndpoints(token, metadata);
}
}
private AbstractReplicationStrategy getStrategyWithNewTokenMetadata(AbstractReplicationStrategy strategy, TokenMetadata newTmd) throws ConfigurationException
{
return AbstractReplicationStrategy.createReplicationStrategy(
strategy.table,
strategy.getClass().getName(),
newTmd,
strategy.snitch,
strategy.configOptions);
}
}

View File

@ -188,9 +188,9 @@ public class AntiEntropyServiceTest extends CleanupHelper
addTokens(1 + (2 * DatabaseDescriptor.getReplicationFactor(tablename)));
AbstractReplicationStrategy ars = StorageService.instance.getReplicationStrategy(tablename);
Set<InetAddress> expected = new HashSet<InetAddress>();
for (Range replicaRange : ars.getAddressRanges(tablename).get(FBUtilities.getLocalAddress()))
for (Range replicaRange : ars.getAddressRanges().get(FBUtilities.getLocalAddress()))
{
expected.addAll(ars.getRangeAddresses(tmd, tablename).get(replicaRange));
expected.addAll(ars.getRangeAddresses(tmd).get(replicaRange));
}
expected.remove(FBUtilities.getLocalAddress());
assertEquals(expected, AntiEntropyService.getNeighbors(tablename));

View File

@ -23,6 +23,7 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import org.apache.cassandra.config.ConfigurationException;
import org.junit.Test;
import static org.junit.Assert.*;
@ -36,7 +37,6 @@ import org.apache.cassandra.locator.AbstractReplicationStrategy;
import org.apache.cassandra.locator.RackUnawareStrategy;
import org.apache.cassandra.locator.SimpleSnitch;
import org.apache.cassandra.locator.TokenMetadata;
import org.apache.cassandra.utils.Pair;
public class MoveTest extends CleanupHelper
{
@ -63,10 +63,8 @@ public class MoveTest extends CleanupHelper
TokenMetadata tmd = ss.getTokenMetadata();
tmd.clearUnsafe();
IPartitioner partitioner = new RandomPartitioner();
AbstractReplicationStrategy testStrategy = new RackUnawareStrategy(tmd, new SimpleSnitch());
IPartitioner oldPartitioner = ss.setPartitionerUnsafe(partitioner);
Map<String, AbstractReplicationStrategy> oldStrategies = ss.setReplicationStrategyUnsafe(createReplacements(testStrategy));
ArrayList<Token> endpointTokens = new ArrayList<Token>();
ArrayList<Token> keyTokens = new ArrayList<Token>();
@ -74,52 +72,53 @@ public class MoveTest extends CleanupHelper
createInitialRing(ss, partitioner, endpointTokens, keyTokens, hosts, RING_SIZE);
final Map<Pair<String, Token>, List<InetAddress>> expectedEndpoints = new HashMap<Pair<String, Token>, List<InetAddress>>();
Map<Token, List<InetAddress>> expectedEndpoints = new HashMap<Token, List<InetAddress>>();
for (String table : DatabaseDescriptor.getNonSystemTables())
{
for (Token token : keyTokens)
{
List<InetAddress> endpoints = new ArrayList<InetAddress>();
Pair<String, Token> key = new Pair<String, Token>(table, token);
Iterator<Token> tokenIter = TokenMetadata.ringIterator(tmd.sortedTokens(), token);
while (tokenIter.hasNext())
{
endpoints.add(tmd.getEndpoint(tokenIter.next()));
}
expectedEndpoints.put(key, endpoints);
expectedEndpoints.put(token, endpoints);
}
}
// Third node leaves
ss.onChange(hosts.get(LEAVING_NODE), StorageService.MOVE_STATE, new ApplicationState(StorageService.STATE_LEAVING + StorageService.Delimiter + partitioner.getTokenFactory().toString(endpointTokens.get(LEAVING_NODE))));
ss.onChange(hosts.get(LEAVING_NODE),
StorageService.MOVE_STATE,
new ApplicationState(StorageService.STATE_LEAVING + StorageService.Delimiter + partitioner.getTokenFactory().toString(endpointTokens.get(LEAVING_NODE))));
assertTrue(tmd.isLeaving(hosts.get(LEAVING_NODE)));
AbstractReplicationStrategy strategy;
for (String table : DatabaseDescriptor.getNonSystemTables())
{
strategy = getStrategy(table, tmd);
for (Token token : keyTokens)
{
Pair<String, Token> key = new Pair<String, Token>(table, token);
int replicationFactor = DatabaseDescriptor.getReplicationFactor(table);
HashSet<InetAddress> actual = new HashSet<InetAddress>(tmd.getWriteEndpoints(token, table, testStrategy.calculateNaturalEndpoints(token, tmd, table)));
HashSet<InetAddress> actual = new HashSet<InetAddress>(tmd.getWriteEndpoints(token, table, strategy.calculateNaturalEndpoints(token, tmd)));
HashSet<InetAddress> expected = new HashSet<InetAddress>();
for (int i = 0; i < replicationFactor; i++)
{
expected.add(expectedEndpoints.get(key).get(i));
expected.add(expectedEndpoints.get(token).get(i));
}
// if the leaving node is in the endpoint list,
// then we should expect it plus one extra for when it's gone
if (expected.contains(hosts.get(LEAVING_NODE)))
expected.add(expectedEndpoints.get(key).get(replicationFactor));
expected.add(expectedEndpoints.get(token).get(replicationFactor));
assertEquals("mismatched endpoint sets", expected, actual);
}
}
ss.setPartitionerUnsafe(oldPartitioner);
ss.setReplicationStrategyUnsafe(oldStrategies);
}
/**
@ -127,17 +126,15 @@ public class MoveTest extends CleanupHelper
* simultaneously
*/
@Test
public void testSimultaneousMove() throws UnknownHostException
public void testSimultaneousMove() throws UnknownHostException, ConfigurationException
{
StorageService ss = StorageService.instance;
final int RING_SIZE = 10;
TokenMetadata tmd = ss.getTokenMetadata();
tmd.clearUnsafe();
IPartitioner partitioner = new RandomPartitioner();
AbstractReplicationStrategy testStrategy = new RackUnawareStrategy(tmd, new SimpleSnitch());
IPartitioner oldPartitioner = ss.setPartitionerUnsafe(partitioner);
Map<String, AbstractReplicationStrategy> oldStrategy = ss.setReplicationStrategyUnsafe(createReplacements(testStrategy));
ArrayList<Token> endpointTokens = new ArrayList<Token>();
ArrayList<Token> keyTokens = new ArrayList<Token>();
@ -150,7 +147,7 @@ public class MoveTest extends CleanupHelper
final int[] LEAVING = new int[] {6, 8, 9};
for (int leaving : LEAVING)
ss.onChange(hosts.get(leaving), StorageService.MOVE_STATE, new ApplicationState(StorageService.STATE_LEAVING + StorageService.Delimiter + partitioner.getTokenFactory().toString(endpointTokens.get(leaving))));
// boot two new nodes with keyTokens.get(5) and keyTokens.get(7)
InetAddress boot1 = InetAddress.getByName("127.0.1.1");
ss.onChange(boot1, StorageService.MOVE_STATE, new ApplicationState(StorageService.STATE_BOOTSTRAPPING + StorageService.Delimiter + partitioner.getTokenFactory().toString(keyTokens.get(5))));
@ -160,7 +157,11 @@ public class MoveTest extends CleanupHelper
Collection<InetAddress> endpoints = null;
/* don't require test update every time a new keyspace is added to test/conf/cassandra.yaml */
List<String> tables = Arrays.asList("Keyspace1", "Keyspace2", "Keyspace3", "Keyspace4");
Map<String, AbstractReplicationStrategy> tableStrategyMap = new HashMap<String, AbstractReplicationStrategy>();
for (int i=1; i<=4; i++)
{
tableStrategyMap.put("Keyspace" + i, getStrategy("Keyspace" + i, tmd));
}
// pre-calculate the results.
Map<String, Multimap<Token, InetAddress>> expectedEndpoints = new HashMap<String, Multimap<Token, InetAddress>>();
@ -209,11 +210,14 @@ public class MoveTest extends CleanupHelper
expectedEndpoints.get("Keyspace4").putAll(new BigIntegerToken("85"), makeAddrs("127.0.0.10", "127.0.0.1", "127.0.0.2", "127.0.0.3"));
expectedEndpoints.get("Keyspace4").putAll(new BigIntegerToken("95"), makeAddrs("127.0.0.1", "127.0.0.2", "127.0.0.3"));
for (String table : tables)
for (Map.Entry<String, AbstractReplicationStrategy> tableStrategy : tableStrategyMap.entrySet())
{
String table = tableStrategy.getKey();
AbstractReplicationStrategy strategy = tableStrategy.getValue();
for (int i = 0; i < keyTokens.size(); i++)
{
endpoints = tmd.getWriteEndpoints(keyTokens.get(i), table, testStrategy.getNaturalEndpoints(keyTokens.get(i), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(i), table, strategy.getNaturalEndpoints(keyTokens.get(i)));
assertTrue(expectedEndpoints.get(table).get(keyTokens.get(i)).size() == endpoints.size());
assertTrue(expectedEndpoints.get(table).get(keyTokens.get(i)).containsAll(endpoints));
}
@ -224,7 +228,7 @@ public class MoveTest extends CleanupHelper
// tokens 5, 15 and 25 should go three nodes
for (int i=0; i<3; ++i)
{
endpoints = tmd.getWriteEndpoints(keyTokens.get(i), table, testStrategy.getNaturalEndpoints(keyTokens.get(i), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(i), table, strategy.getNaturalEndpoints(keyTokens.get(i)));
assertTrue(endpoints.size() == 3);
assertTrue(endpoints.contains(hosts.get(i+1)));
assertTrue(endpoints.contains(hosts.get(i+2)));
@ -232,7 +236,7 @@ public class MoveTest extends CleanupHelper
}
// token 35 should go to nodes 4, 5, 6, 7 and boot1
endpoints = tmd.getWriteEndpoints(keyTokens.get(3), table, testStrategy.getNaturalEndpoints(keyTokens.get(3), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(3), table, strategy.getNaturalEndpoints(keyTokens.get(3)));
assertTrue(endpoints.size() == 5);
assertTrue(endpoints.contains(hosts.get(4)));
assertTrue(endpoints.contains(hosts.get(5)));
@ -241,7 +245,7 @@ public class MoveTest extends CleanupHelper
assertTrue(endpoints.contains(boot1));
// token 45 should go to nodes 5, 6, 7, 0, boot1 and boot2
endpoints = tmd.getWriteEndpoints(keyTokens.get(4), table, testStrategy.getNaturalEndpoints(keyTokens.get(4), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(4), table, strategy.getNaturalEndpoints(keyTokens.get(4)));
assertTrue(endpoints.size() == 6);
assertTrue(endpoints.contains(hosts.get(5)));
assertTrue(endpoints.contains(hosts.get(6)));
@ -251,7 +255,7 @@ public class MoveTest extends CleanupHelper
assertTrue(endpoints.contains(boot2));
// token 55 should go to nodes 6, 7, 8, 0, 1, boot1 and boot2
endpoints = tmd.getWriteEndpoints(keyTokens.get(5), table, testStrategy.getNaturalEndpoints(keyTokens.get(5), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(5), table, strategy.getNaturalEndpoints(keyTokens.get(5)));
assertTrue(endpoints.size() == 7);
assertTrue(endpoints.contains(hosts.get(6)));
assertTrue(endpoints.contains(hosts.get(7)));
@ -262,7 +266,7 @@ public class MoveTest extends CleanupHelper
assertTrue(endpoints.contains(boot2));
// token 65 should go to nodes 7, 8, 9, 0, 1 and boot2
endpoints = tmd.getWriteEndpoints(keyTokens.get(6), table, testStrategy.getNaturalEndpoints(keyTokens.get(6), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(6), table, strategy.getNaturalEndpoints(keyTokens.get(6)));
assertTrue(endpoints.size() == 6);
assertTrue(endpoints.contains(hosts.get(7)));
assertTrue(endpoints.contains(hosts.get(8)));
@ -272,7 +276,7 @@ public class MoveTest extends CleanupHelper
assertTrue(endpoints.contains(boot2));
// token 75 should to go nodes 8, 9, 0, 1, 2 and boot2
endpoints = tmd.getWriteEndpoints(keyTokens.get(7), table, testStrategy.getNaturalEndpoints(keyTokens.get(7), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(7), table, strategy.getNaturalEndpoints(keyTokens.get(7)));
assertTrue(endpoints.size() == 6);
assertTrue(endpoints.contains(hosts.get(8)));
assertTrue(endpoints.contains(hosts.get(9)));
@ -282,7 +286,7 @@ public class MoveTest extends CleanupHelper
assertTrue(endpoints.contains(boot2));
// token 85 should go to nodes 9, 0, 1 and 2
endpoints = tmd.getWriteEndpoints(keyTokens.get(8), table, testStrategy.getNaturalEndpoints(keyTokens.get(8), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(8), table, strategy.getNaturalEndpoints(keyTokens.get(8)));
assertTrue(endpoints.size() == 4);
assertTrue(endpoints.contains(hosts.get(9)));
assertTrue(endpoints.contains(hosts.get(0)));
@ -290,7 +294,7 @@ public class MoveTest extends CleanupHelper
assertTrue(endpoints.contains(hosts.get(2)));
// token 95 should go to nodes 0, 1 and 2
endpoints = tmd.getWriteEndpoints(keyTokens.get(9), table, testStrategy.getNaturalEndpoints(keyTokens.get(9), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(9), table, strategy.getNaturalEndpoints(keyTokens.get(9)));
assertTrue(endpoints.size() == 3);
assertTrue(endpoints.contains(hosts.get(0)));
assertTrue(endpoints.contains(hosts.get(1)));
@ -324,11 +328,14 @@ public class MoveTest extends CleanupHelper
expectedEndpoints.get("Keyspace4").get(new BigIntegerToken("75")).removeAll(makeAddrs("127.0.0.10"));
expectedEndpoints.get("Keyspace4").get(new BigIntegerToken("85")).removeAll(makeAddrs("127.0.0.10"));
for (String table : tables)
for (Map.Entry<String, AbstractReplicationStrategy> tableStrategy : tableStrategyMap.entrySet())
{
String table = tableStrategy.getKey();
AbstractReplicationStrategy strategy = tableStrategy.getValue();
for (int i = 0; i < keyTokens.size(); i++)
{
endpoints = tmd.getWriteEndpoints(keyTokens.get(i), table, testStrategy.getNaturalEndpoints(keyTokens.get(i), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(i), table, strategy.getNaturalEndpoints(keyTokens.get(i)));
assertTrue(expectedEndpoints.get(table).get(keyTokens.get(i)).size() == endpoints.size());
assertTrue(expectedEndpoints.get(table).get(keyTokens.get(i)).containsAll(endpoints));
}
@ -339,7 +346,7 @@ public class MoveTest extends CleanupHelper
// tokens 5, 15 and 25 should go three nodes
for (int i=0; i<3; ++i)
{
endpoints = tmd.getWriteEndpoints(keyTokens.get(i), table, testStrategy.getNaturalEndpoints(keyTokens.get(i), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(i), table, strategy.getNaturalEndpoints(keyTokens.get(i)));
assertTrue(endpoints.size() == 3);
assertTrue(endpoints.contains(hosts.get(i+1)));
assertTrue(endpoints.contains(hosts.get(i+2)));
@ -347,21 +354,21 @@ public class MoveTest extends CleanupHelper
}
// token 35 goes to nodes 4, 5 and boot1
endpoints = tmd.getWriteEndpoints(keyTokens.get(3), table, testStrategy.getNaturalEndpoints(keyTokens.get(3), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(3), table, strategy.getNaturalEndpoints(keyTokens.get(3)));
assertTrue(endpoints.size() == 3);
assertTrue(endpoints.contains(hosts.get(4)));
assertTrue(endpoints.contains(hosts.get(5)));
assertTrue(endpoints.contains(boot1));
// token 45 goes to nodes 5, boot1 and node7
endpoints = tmd.getWriteEndpoints(keyTokens.get(4), table, testStrategy.getNaturalEndpoints(keyTokens.get(4), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(4), table, strategy.getNaturalEndpoints(keyTokens.get(4)));
assertTrue(endpoints.size() == 3);
assertTrue(endpoints.contains(hosts.get(5)));
assertTrue(endpoints.contains(boot1));
assertTrue(endpoints.contains(hosts.get(7)));
// token 55 goes to boot1, 7, boot2, 8 and 0
endpoints = tmd.getWriteEndpoints(keyTokens.get(5), table, testStrategy.getNaturalEndpoints(keyTokens.get(5), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(5), table, strategy.getNaturalEndpoints(keyTokens.get(5)));
assertTrue(endpoints.size() == 5);
assertTrue(endpoints.contains(boot1));
assertTrue(endpoints.contains(hosts.get(7)));
@ -370,7 +377,7 @@ public class MoveTest extends CleanupHelper
assertTrue(endpoints.contains(hosts.get(0)));
// token 65 goes to nodes 7, boot2, 8, 0 and 1
endpoints = tmd.getWriteEndpoints(keyTokens.get(6), table, testStrategy.getNaturalEndpoints(keyTokens.get(6), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(6), table, strategy.getNaturalEndpoints(keyTokens.get(6)));
assertTrue(endpoints.size() == 5);
assertTrue(endpoints.contains(hosts.get(7)));
assertTrue(endpoints.contains(boot2));
@ -379,7 +386,7 @@ public class MoveTest extends CleanupHelper
assertTrue(endpoints.contains(hosts.get(1)));
// token 75 goes to nodes boot2, 8, 0, 1 and 2
endpoints = tmd.getWriteEndpoints(keyTokens.get(7), table, testStrategy.getNaturalEndpoints(keyTokens.get(7), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(7), table, strategy.getNaturalEndpoints(keyTokens.get(7)));
assertTrue(endpoints.size() == 5);
assertTrue(endpoints.contains(boot2));
assertTrue(endpoints.contains(hosts.get(8)));
@ -388,14 +395,14 @@ public class MoveTest extends CleanupHelper
assertTrue(endpoints.contains(hosts.get(2)));
// token 85 goes to nodes 0, 1 and 2
endpoints = tmd.getWriteEndpoints(keyTokens.get(8), table, testStrategy.getNaturalEndpoints(keyTokens.get(8), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(8), table, strategy.getNaturalEndpoints(keyTokens.get(8)));
assertTrue(endpoints.size() == 3);
assertTrue(endpoints.contains(hosts.get(0)));
assertTrue(endpoints.contains(hosts.get(1)));
assertTrue(endpoints.contains(hosts.get(2)));
// token 95 goes to nodes 0, 1 and 2
endpoints = tmd.getWriteEndpoints(keyTokens.get(9), table, testStrategy.getNaturalEndpoints(keyTokens.get(9), table));
endpoints = tmd.getWriteEndpoints(keyTokens.get(9), table, strategy.getNaturalEndpoints(keyTokens.get(9)));
assertTrue(endpoints.size() == 3);
assertTrue(endpoints.contains(hosts.get(0)));
assertTrue(endpoints.contains(hosts.get(1)));
@ -403,7 +410,6 @@ public class MoveTest extends CleanupHelper
}
ss.setPartitionerUnsafe(oldPartitioner);
ss.setReplicationStrategyUnsafe(oldStrategy);
}
@Test
@ -413,10 +419,8 @@ public class MoveTest extends CleanupHelper
TokenMetadata tmd = ss.getTokenMetadata();
tmd.clearUnsafe();
IPartitioner partitioner = new RandomPartitioner();
AbstractReplicationStrategy testStrategy = new RackUnawareStrategy(tmd, new SimpleSnitch());
IPartitioner oldPartitioner = ss.setPartitionerUnsafe(partitioner);
Map<String, AbstractReplicationStrategy> oldStrategy = ss.setReplicationStrategyUnsafe(createReplacements(testStrategy));
ArrayList<Token> endpointTokens = new ArrayList<Token>();
ArrayList<Token> keyTokens = new ArrayList<Token>();
@ -472,7 +476,6 @@ public class MoveTest extends CleanupHelper
assertTrue(tmd.getBootstrapTokens().isEmpty());
ss.setPartitionerUnsafe(oldPartitioner);
ss.setReplicationStrategyUnsafe(oldStrategy);
}
@Test
@ -482,10 +485,8 @@ public class MoveTest extends CleanupHelper
TokenMetadata tmd = ss.getTokenMetadata();
tmd.clearUnsafe();
IPartitioner partitioner = new RandomPartitioner();
AbstractReplicationStrategy testStrategy = new RackUnawareStrategy(tmd, new SimpleSnitch());
IPartitioner oldPartitioner = ss.setPartitionerUnsafe(partitioner);
Map<String, AbstractReplicationStrategy> oldStrategy = ss.setReplicationStrategyUnsafe(createReplacements(testStrategy));
ArrayList<Token> endpointTokens = new ArrayList<Token>();
ArrayList<Token> keyTokens = new ArrayList<Token>();
@ -516,7 +517,6 @@ public class MoveTest extends CleanupHelper
assertTrue(tmd.getToken(hosts.get(2)).equals(keyTokens.get(4)));
ss.setPartitionerUnsafe(oldPartitioner);
ss.setReplicationStrategyUnsafe(oldStrategy);
}
@Test
@ -526,10 +526,8 @@ public class MoveTest extends CleanupHelper
TokenMetadata tmd = ss.getTokenMetadata();
tmd.clearUnsafe();
IPartitioner partitioner = new RandomPartitioner();
AbstractReplicationStrategy testStrategy = new RackUnawareStrategy(tmd, new SimpleSnitch());
IPartitioner oldPartitioner = ss.setPartitionerUnsafe(partitioner);
Map<String, AbstractReplicationStrategy> oldStrategy = ss.setReplicationStrategyUnsafe(createReplacements(testStrategy));
ArrayList<Token> endpointTokens = new ArrayList<Token>();
ArrayList<Token> keyTokens = new ArrayList<Token>();
@ -566,7 +564,6 @@ public class MoveTest extends CleanupHelper
assertFalse(tmd.isLeaving(hosts.get(2)));
ss.setPartitionerUnsafe(oldPartitioner);
ss.setReplicationStrategyUnsafe(oldStrategy);
}
@Test
@ -576,10 +573,8 @@ public class MoveTest extends CleanupHelper
TokenMetadata tmd = ss.getTokenMetadata();
tmd.clearUnsafe();
IPartitioner partitioner = new RandomPartitioner();
AbstractReplicationStrategy testStrategy = new RackUnawareStrategy(tmd, new SimpleSnitch());
IPartitioner oldPartitioner = ss.setPartitionerUnsafe(partitioner);
Map<String, AbstractReplicationStrategy> oldStrategy = ss.setReplicationStrategyUnsafe(createReplacements(testStrategy));
ArrayList<Token> endpointTokens = new ArrayList<Token>();
ArrayList<Token> keyTokens = new ArrayList<Token>();
@ -608,7 +603,6 @@ public class MoveTest extends CleanupHelper
assertFalse(tmd.isLeaving(hosts.get(2)));
ss.setPartitionerUnsafe(oldPartitioner);
ss.setReplicationStrategyUnsafe(oldStrategy);
}
/**
@ -643,4 +637,15 @@ public class MoveTest extends CleanupHelper
addrs.add(InetAddress.getByName(host));
return addrs;
}
private AbstractReplicationStrategy getStrategy(String table, TokenMetadata tmd) throws ConfigurationException
{
return AbstractReplicationStrategy.createReplicationStrategy(
table,
"org.apache.cassandra.locator.RackUnawareStrategy",
tmd,
new SimpleSnitch(),
null);
}
}