diff --git a/CHANGES.txt b/CHANGES.txt
index e115d07219..e83069fd40 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,13 @@
-dev
+0.8-dev
+ * avoid double RowMutation serialization on write path (CASSANDRA-1800)
+
+
+0.7-dev
+ * expose getNaturalEndpoints in StorageServiceMBean taking byte[]
+ key; RMI cannot serialize ByteBuffer (CASSANDRA-1833)
+
+
+0.7.0-rc2
* fix live-column-count of slice ranges including tombstoned supercolumn
with live subcolumn (CASSANDRA-1591)
* rename o.a.c.internal.AntientropyStage -> AntiEntropyStage,
@@ -23,7 +32,17 @@ dev
* close file handle used for post-flush truncate (CASSANDRA-1790)
* various code cleanup (CASSANDRA-1793, -1794, -1795)
* fix range queries against wrapped range (CASSANDRA-1781)
- * avoid double RowMutation serialization on write path (CASSANDRA-1800)
+ * fix consistencylevel calculations for NetworkTopologyStrategy
+ (CASSANDRA-1804)
+ * cli support index type enum names (CASSANDRA-1810)
+ * improved validation of column_metadata (CASSANDRA-1813)
+ * reads at ConsistencyLevel > 1 throw UnavailableException
+ immediately if insufficient live nodes exist (CASSANDRA-1803)
+ * copy bytebuffers for local writes to avoid retaining the entire
+ Thrift frame (CASSANDRA-1801)
+ * fix NPE adding index to column w/o prior metadata (CASSANDRA-1764)
+ * reduce fat client timeout (CASSANDRA-1730)
+ * fix botched merge of CASSANDRA-1316
0.7.0-rc1
diff --git a/build.xml b/build.xml
index b9f583db37..ff575e6c0a 100644
--- a/build.xml
+++ b/build.xml
@@ -47,7 +47,7 @@
-
+
diff --git a/conf/cassandra-env.sh b/conf/cassandra-env.sh
index 9a946ad68d..2870fdb34d 100644
--- a/conf/cassandra-env.sh
+++ b/conf/cassandra-env.sh
@@ -78,7 +78,7 @@ if [ "`uname`" = "Linux" ] ; then
JVM_OPTS="$JVM_OPTS -Xss128k"
fi
-# GC tuning options.
+# GC tuning options
JVM_OPTS="$JVM_OPTS -XX:+UseParNewGC"
JVM_OPTS="$JVM_OPTS -XX:+UseConcMarkSweepGC"
JVM_OPTS="$JVM_OPTS -XX:+CMSParallelRemarkEnabled"
@@ -87,6 +87,14 @@ JVM_OPTS="$JVM_OPTS -XX:MaxTenuringThreshold=1"
JVM_OPTS="$JVM_OPTS -XX:CMSInitiatingOccupancyFraction=75"
JVM_OPTS="$JVM_OPTS -XX:+UseCMSInitiatingOccupancyOnly"
+# GC logging options -- uncomment to enable
+# JVM_OPTS="$JVM_OPTS -XX:+PrintGCDetails"
+# JVM_OPTS="$JVM_OPTS -XX:+PrintGCTimeStamps"
+# JVM_OPTS="$JVM_OPTS -XX:+PrintClassHistogram"
+# JVM_OPTS="$JVM_OPTS -XX:+PrintTenuringDistribution"
+# JVM_OPTS="$JVM_OPTS -XX:+PrintGCApplicationStoppedTime"
+# JVM_OPTS="$JVM_OPTS -Xloggc:/var/log/cassandra/gc.log"
+
# Prefer binding to IPv4 network intefaces (when net.ipv6.bindv6only=1). See
# http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6342561 (short version:
# comment out this entry to enable IPv6 support).
diff --git a/contrib/maven/pom.xml b/contrib/maven/pom.xml
deleted file mode 100644
index 5d98eb76a8..0000000000
--- a/contrib/maven/pom.xml
+++ /dev/null
@@ -1,551 +0,0 @@
-
-
-
-
- org.apache
- apache
- 6
-
- 4.0.0
-
- org.apache.cassandra
- cassandra
- 0.6-SNAPSHOT
- jar
- Cassandra
- 2009
-
-
- 2.0.9
-
-
- http://incubator.apache.org/cassandra
-
-
-
- cassandra-user
- cassandra-user-subscribe@incubator.apache.org
- cassandra-user-unsubscribe@incubator.apache.org
- cassandra-user@incubator.apache.org
- http://mail-archives.apache.org/mod_mbox/incubator-cassandra-user/
-
-
- cassandra-dev
- cassandra-dev-subscribe@incubator.apache.org
- cassandra-dev-unsubscribe@incubator.apache.org
- cassandra-dev@incubator.apache.org
- http://mail-archives.apache.org/mod_mbox/incubator-cassandra-dev/
-
-
- cassandra-commits
- cassandra-commits-subscribe@incubator.apache.org
- cassandra-commits-unsubscribe@incubator.apache.org
- cassandra-commits@incubator.apache.org
- http://mail-archives.apache.org/mod_mbox/incubator-cassandra-commits/
-
-
-
-
-
-
-
-
-
-
- JIRA
- https://issues.apache.org/jira/browse/CASSANDRA
-
-
-
-
- hudson
- http://hudson.zones.apache.org/hudson
-
-
-
- mail
- true
- true
- false
- false
- cassandra-commits@incubator.apache.org
-
-
-
-
-
-
- cassandra-website
- scp://people.apache.org/x1/www/incubator.apache.org/cassandra/maven/${pom.version}
-
-
-
-
- scm:svn:http://svn.apache.org/repos/asf/incubator/cassandra/trunk
- scm:svn:https://svn.apache.org/repos/asf/incubator/cassandra/trunk
- http://svn.apache.org/viewvc/incubator/cassandra/trunk/
-
-
-
-
-
- commons-collections
- commons-collections
- 3.2.1
-
-
- commons-cli
- commons-cli
- 1.1
-
-
- commons-lang
- commons-lang
- 2.4
-
-
- jline
- jline
- 0.9.94
-
-
- log4j
- log4j
- 1.2.15
-
-
-
- javax.jms
- jms
-
-
- com.sun.jmx
- jmxri
-
-
- com.sun.jdmk
- jmxtools
-
-
-
-
- org.slf4j
- slf4j-api
- 1.5.8
-
-
- org.slf4j
- slf4j-log4j12
- 1.5.8
-
-
- org.antlr
- antlr-runtime
- 3.1.3
-
-
- com.google.collections
- google-collections
- 1.0-rc1
-
-
-
-
-
- high-scale-lib
- high-scale-lib
- UNKNOWN
- system
- ${basedir}/lib/high-scale-lib.jar
-
-
- flexjson
- flexjson
- 1.7
- system
- ${basedir}/lib/flexjson-1.7.jar
-
-
- libthrift
- libthrift
- UNKNOWN
- system
- ${basedir}/lib/libthrift-r820831.jar
-
-
- jsonsimple
- jsonsimple
- UNKNOWN
- system
- ${basedir}/lib/json_simple-1.1.jar
-
-
- com.reardencommerce
- clhm
- UNKNOWN
- system
- ${basedir}/lib/clhm-production.jar
-
-
-
-
- junit
- junit
- 4.6
- test
-
-
-
-
-
- ${basedir}/src/java
- ${basedir}/test/unit
- build/classes
-
-
-
- ${basedir}/test/conf
-
- **/*
-
-
-
- ${basedir}/test/resources
-
- *.json
-
-
-
-
-
-
-
-
- org.antlr
- antlr3-maven-plugin
- 3.1.3-1
-
-
- process-sources
-
- antlr
-
-
- ${basedir}/src/java
-
-
-
-
-
-
-
- org.codehaus.mojo
- build-helper-maven-plugin
- 1.3
-
-
- add-source
- generate-sources
-
- add-source
-
-
-
- ${basedir}/interface/gen-java
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- 1.6
- 1.6
- true
- true
- true
- true
-
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
-
-
- storage-config
- ${basedir}/test/conf
-
-
- always
-
- **/TestRingCache.java
-
-
-
-
-
-
- org.codehaus.mojo
- cobertura-maven-plugin
- 2.0
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-release-plugin
- 2.0-beta-9
-
- true
- false
- clean install
- deploy
- -Papache-release
-
-
-
- org.codehaus.mojo
- ianal-maven-plugin
- 1.0-alpha-1
-
-
- org.codehaus.mojo
- rat-maven-plugin
- 1.0-alpha-3
-
- false
-
-
-
- org.apache.maven.plugins
- maven-enforcer-plugin
-
-
- validate
-
- enforce
-
-
-
-
- [2.0.9,)
-
-
-
-
-
-
-
- org.codehaus.mojo
- ianal-maven-plugin
-
-
-
- verify-legal-files
-
-
- true
-
-
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-jxr-plugin
-
-
- org.apache.maven.plugins
- maven-surefire-report-plugin
-
-
- org.apache.maven.plugins
- maven-pmd-plugin
-
-
- org.codehaus.mojo
- taglist-maven-plugin
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
-
-
- http://java.sun.com/j2se/1.6.0/docs/api/
- http://logging.apache.org/log4j/docs/api/
-
-
- true
- 900m
- 1.6
-
-
-
- org.codehaus.mojo
- cobertura-maven-plugin
- 2.2
-
-
- html
- xml
-
-
-
-
-
-
-
-
-
-
- thrift
-
- process-sources
-
-
-
- maven-antrun-plugin
-
-
- process-sources
-
-
-
-
-
-
-
- run
-
-
-
-
-
-
-
-
-
-
- apache-release
-
-
-
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
-
- ${gpg.passphrase}
-
-
-
-
- sign
-
-
-
-
-
-
-
- true
- org.apache.maven.plugins
- maven-deploy-plugin
-
- true
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
-
-
- attach-sources
-
- jar
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
-
- ${project.build.sourceEncoding}
-
-
-
- attach-javadocs
-
- jar
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
- single
-
- package
-
- true
-
-
- source-release
-
-
-
-
-
-
-
- org.apache.geronimo.genesis
- apache-source-release-assembly-descriptor
- 2.0
-
-
-
-
-
-
-
-
-
-
-
diff --git a/debian/changelog b/debian/changelog
index 0017e47f0d..765ccda88f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+cassandra (0.7.0~rc2) unstable; urgency=low
+
+ * Release candidate release.
+
+ -- Eric Evans Mon, 06 Dec 2010 11:19:40 -0600
+
cassandra (0.7.0~rc1) unstable; urgency=low
* Release candidate release.
diff --git a/src/java/org/apache/cassandra/cli/CliClient.java b/src/java/org/apache/cassandra/cli/CliClient.java
index 0aafe0c658..192ffa4ee9 100644
--- a/src/java/org/apache/cassandra/cli/CliClient.java
+++ b/src/java/org/apache/cassandra/cli/CliClient.java
@@ -468,7 +468,7 @@ public class CliClient extends CliUserHelp
return;
IndexClause clause = new IndexClause();
- String columnFamily = statement.getChild(0).getText();
+ String columnFamily = CliCompiler.getColumnFamily(statement, keyspacesMap.get(keySpace).cf_defs);
// ^(CONDITIONS ^(CONDITION $column $value) ...)
Tree conditions = statement.getChild(1);
@@ -1414,20 +1414,28 @@ public class CliClient extends CliUserHelp
*/
private IndexType getIndexTypeFromString(String indexTypeAsString)
{
- Integer indexTypeId;
IndexType indexType;
- try {
- indexTypeId = new Integer(indexTypeAsString);
+ try
+ {
+ indexType = IndexType.findByValue(new Integer(indexTypeAsString));
}
- catch (NumberFormatException e) {
- throw new RuntimeException("Could not convert " + indexTypeAsString + " into Integer.");
+ catch (NumberFormatException e)
+ {
+ try
+ {
+ // if this is not an integer lets try to get IndexType by name
+ indexType = IndexType.valueOf(indexTypeAsString);
+ }
+ catch (IllegalArgumentException ie)
+ {
+ throw new RuntimeException("IndexType '" + indexTypeAsString + "' is unsupported.");
+ }
}
- indexType = IndexType.findByValue(indexTypeId);
-
- if (indexType == null) {
- throw new RuntimeException(indexTypeAsString + " is unsupported.");
+ if (indexType == null)
+ {
+ throw new RuntimeException("IndexType '" + indexTypeAsString + "' is unsupported.");
}
return indexType;
diff --git a/src/java/org/apache/cassandra/config/CFMetaData.java b/src/java/org/apache/cassandra/config/CFMetaData.java
index 8f3f496858..59ec9f5a4e 100644
--- a/src/java/org/apache/cassandra/config/CFMetaData.java
+++ b/src/java/org/apache/cassandra/config/CFMetaData.java
@@ -41,6 +41,7 @@ import org.apache.cassandra.db.marshal.TimeUUIDType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.db.migration.Migration;
import org.apache.cassandra.io.SerDeUtils;
+import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Pair;
import org.apache.commons.lang.builder.EqualsBuilder;
@@ -751,7 +752,7 @@ public final class CFMetaData
org.apache.cassandra.avro.ColumnDef tcd = new org.apache.cassandra.avro.ColumnDef();
tcd.index_name = cd.getIndexName();
tcd.index_type = org.apache.cassandra.avro.IndexType.valueOf(cd.getIndexType().name());
- tcd.name = cd.name;
+ tcd.name = ByteBufferUtil.clone(cd.name);
tcd.validation_class = cd.validator.getClass().getName();
column_meta.add(tcd);
}
@@ -786,7 +787,7 @@ public final class CFMetaData
for (org.apache.cassandra.thrift.ColumnDef cdef : def.getColumn_metadata())
{
org.apache.cassandra.avro.ColumnDef tdef = new org.apache.cassandra.avro.ColumnDef();
- tdef.name = cdef.BufferForName();
+ tdef.name = ByteBufferUtil.clone(cdef.BufferForName());
tdef.validation_class = cdef.getValidation_class();
tdef.index_name = cdef.getIndex_name();
tdef.index_type = cdef.getIndex_type() == null ? null : org.apache.cassandra.avro.IndexType.valueOf(cdef.getIndex_type().name());
diff --git a/src/java/org/apache/cassandra/config/ColumnDefinition.java b/src/java/org/apache/cassandra/config/ColumnDefinition.java
index 54f5b09ae6..abb0c7ff75 100644
--- a/src/java/org/apache/cassandra/config/ColumnDefinition.java
+++ b/src/java/org/apache/cassandra/config/ColumnDefinition.java
@@ -31,6 +31,7 @@ import org.apache.avro.util.Utf8;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.thrift.ColumnDef;
import org.apache.cassandra.thrift.IndexType;
+import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
public class ColumnDefinition {
@@ -103,8 +104,7 @@ public class ColumnDefinition {
public static ColumnDefinition fromColumnDef(ColumnDef thriftColumnDef) throws ConfigurationException
{
- validateIndexType(thriftColumnDef);
- return new ColumnDefinition(thriftColumnDef.name, thriftColumnDef.validation_class, thriftColumnDef.index_type, thriftColumnDef.index_name);
+ return new ColumnDefinition(ByteBufferUtil.clone(thriftColumnDef.name), thriftColumnDef.validation_class, thriftColumnDef.index_type, thriftColumnDef.index_name);
}
public static ColumnDefinition fromColumnDef(org.apache.cassandra.avro.ColumnDef avroColumnDef) throws ConfigurationException
@@ -123,10 +123,7 @@ public class ColumnDefinition {
Map cds = new TreeMap();
for (ColumnDef thriftColumnDef : thriftDefs)
- {
- validateIndexType(thriftColumnDef);
- cds.put(thriftColumnDef.name, fromColumnDef(thriftColumnDef));
- }
+ cds.put(ByteBufferUtil.clone(thriftColumnDef.name), fromColumnDef(thriftColumnDef));
return Collections.unmodifiableMap(cds);
}
@@ -146,12 +143,6 @@ public class ColumnDefinition {
return Collections.unmodifiableMap(cds);
}
- public static void validateIndexType(org.apache.cassandra.thrift.ColumnDef thriftColumnDef) throws ConfigurationException
- {
- if ((thriftColumnDef.index_name != null) && (thriftColumnDef.index_type == null))
- throw new ConfigurationException("index_name cannot be set if index_type is not also set");
- }
-
public static void validateIndexType(org.apache.cassandra.avro.ColumnDef avroColumnDef) throws ConfigurationException
{
if ((avroColumnDef.index_name != null) && (avroColumnDef.index_type == null))
diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index 5968a62405..7f59afc5c2 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -816,11 +816,6 @@ public class DatabaseDescriptor
return conf.rpc_port;
}
- public static int getReplicationFactor(String table)
- {
- return tables.get(table).replicationFactor;
- }
-
public static long getRpcTimeout()
{
return conf.rpc_timeout_in_ms;
diff --git a/src/java/org/apache/cassandra/db/Column.java b/src/java/org/apache/cassandra/db/Column.java
index b61ee42f49..38abb152f5 100644
--- a/src/java/org/apache/cassandra/db/Column.java
+++ b/src/java/org/apache/cassandra/db/Column.java
@@ -210,6 +210,12 @@ public class Column implements IColumn
return result;
}
+ @Override
+ public IColumn deepCopy()
+ {
+ return new Column(ByteBufferUtil.clone(name), ByteBufferUtil.clone(value), timestamp);
+ }
+
public String getString(AbstractType comparator)
{
StringBuilder sb = new StringBuilder();
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyType.java b/src/java/org/apache/cassandra/db/ColumnFamilyType.java
index 9f66b41346..d250ecd9b9 100644
--- a/src/java/org/apache/cassandra/db/ColumnFamilyType.java
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyType.java
@@ -25,11 +25,12 @@ public enum ColumnFamilyType
Standard,
Super;
- public final static ColumnFamilyType create(String name)
+ public static ColumnFamilyType create(String name)
{
try
{
- return name == null ? null : ColumnFamilyType.valueOf(name);
+ // TODO thrift optional parameter in CfDef is leaking down here which it shouldn't
+ return name == null ? ColumnFamilyType.Standard : ColumnFamilyType.valueOf(name);
}
catch (IllegalArgumentException e)
{
diff --git a/src/java/org/apache/cassandra/db/DeletedColumn.java b/src/java/org/apache/cassandra/db/DeletedColumn.java
index 5e7c7ec3f6..7af5af5190 100644
--- a/src/java/org/apache/cassandra/db/DeletedColumn.java
+++ b/src/java/org/apache/cassandra/db/DeletedColumn.java
@@ -20,6 +20,7 @@ package org.apache.cassandra.db;
import java.nio.ByteBuffer;
+import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -55,4 +56,10 @@ public class DeletedColumn extends Column
{
return value.getInt(value.position()+value.arrayOffset() );
}
+
+ @Override
+ public IColumn deepCopy()
+ {
+ return new DeletedColumn(ByteBufferUtil.clone(name), ByteBufferUtil.clone(value), timestamp);
+ }
}
diff --git a/src/java/org/apache/cassandra/db/ExpiringColumn.java b/src/java/org/apache/cassandra/db/ExpiringColumn.java
index 5496a45e3e..e587618ef6 100644
--- a/src/java/org/apache/cassandra/db/ExpiringColumn.java
+++ b/src/java/org/apache/cassandra/db/ExpiringColumn.java
@@ -24,6 +24,7 @@ import java.security.MessageDigest;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.io.util.DataOutputBuffer;
+import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.log4j.Logger;
/**
@@ -104,6 +105,12 @@ public class ExpiringColumn extends Column
return localExpirationTime;
}
+ @Override
+ public IColumn deepCopy()
+ {
+ return new ExpiringColumn(ByteBufferUtil.clone(name), ByteBufferUtil.clone(value), timestamp, timeToLive, localExpirationTime);
+ }
+
@Override
public String getString(AbstractType comparator)
{
diff --git a/src/java/org/apache/cassandra/db/HintedHandOffManager.java b/src/java/org/apache/cassandra/db/HintedHandOffManager.java
index dac514549f..c83bb646c3 100644
--- a/src/java/org/apache/cassandra/db/HintedHandOffManager.java
+++ b/src/java/org/apache/cassandra/db/HintedHandOffManager.java
@@ -24,6 +24,7 @@ import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeoutException;
@@ -122,7 +123,7 @@ public class HintedHandOffManager
rm.add(cf);
Message message = rm.makeRowMutationMessage();
IWriteResponseHandler responseHandler = WriteResponseHandler.create(endpoint);
- MessagingService.instance.sendRR(message, new InetAddress[] { endpoint }, responseHandler);
+ MessagingService.instance.sendRR(message, Arrays.asList(endpoint), responseHandler);
try
{
responseHandler.get();
diff --git a/src/java/org/apache/cassandra/db/IColumn.java b/src/java/org/apache/cassandra/db/IColumn.java
index d7f9d3d642..61bb6783c3 100644
--- a/src/java/org/apache/cassandra/db/IColumn.java
+++ b/src/java/org/apache/cassandra/db/IColumn.java
@@ -46,6 +46,9 @@ public interface IColumn
public int getLocalDeletionTime(); // for tombstone GC, so int is sufficient granularity
public String getString(AbstractType comparator);
+ /** clones the column, making copies of any underlying byte buffers */
+ IColumn deepCopy();
+
/**
* For a simple column, live == !isMarkedForDelete.
* For a supercolumn, live means it has at least one subcolumn whose timestamp is greater than the
diff --git a/src/java/org/apache/cassandra/db/RowMutation.java b/src/java/org/apache/cassandra/db/RowMutation.java
index 426b6ca61b..8365bd556e 100644
--- a/src/java/org/apache/cassandra/db/RowMutation.java
+++ b/src/java/org/apache/cassandra/db/RowMutation.java
@@ -41,6 +41,7 @@ import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.thrift.ColumnOrSuperColumn;
import org.apache.cassandra.thrift.Deletion;
import org.apache.cassandra.thrift.Mutation;
+import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.commons.lang.StringUtils;
@@ -317,6 +318,21 @@ public class RowMutation
rm.preserializedBuffer = raw;
return rm;
}
+
+ public RowMutation deepCopy()
+ {
+ RowMutation rm = new RowMutation(table_, ByteBufferUtil.clone(key_));
+
+ for (Map.Entry e : modifications_.entrySet())
+ {
+ ColumnFamily cf = e.getValue().cloneMeShallow();
+ for (Map.Entry ce : e.getValue().getColumnsMap().entrySet())
+ cf.addColumn(ce.getValue().deepCopy());
+ rm.modifications_.put(e.getKey(), cf);
+ }
+
+ return rm;
+ }
}
class RowMutationSerializer implements ICompactSerializer
diff --git a/src/java/org/apache/cassandra/db/SuperColumn.java b/src/java/org/apache/cassandra/db/SuperColumn.java
index c74bf371fd..0d5def4485 100644
--- a/src/java/org/apache/cassandra/db/SuperColumn.java
+++ b/src/java/org/apache/cassandra/db/SuperColumn.java
@@ -24,6 +24,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.Collection;
+import java.util.Map;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -31,6 +32,7 @@ import java.util.concurrent.atomic.AtomicLong;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.io.ICompactSerializer2;
import org.apache.cassandra.io.util.DataOutputBuffer;
+import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -286,6 +288,20 @@ public class SuperColumn implements IColumn, IColumnContainer
this.localDeletionTime.set(localDeleteTime);
this.markedForDeleteAt.set(timestamp);
}
+
+ public IColumn deepCopy()
+ {
+ SuperColumn sc = new SuperColumn(ByteBufferUtil.clone(name_), this.getComparator());
+ sc.localDeletionTime = localDeletionTime;
+ sc.markedForDeleteAt = markedForDeleteAt;
+
+ for(Map.Entry c : columns_.entrySet())
+ {
+ sc.addColumn(c.getValue().deepCopy());
+ }
+
+ return sc;
+ }
public IColumn reconcile(IColumn c)
{
diff --git a/src/java/org/apache/cassandra/locator/AbstractReplicationStrategy.java b/src/java/org/apache/cassandra/locator/AbstractReplicationStrategy.java
index 9ec15c114b..1f997cbafc 100644
--- a/src/java/org/apache/cassandra/locator/AbstractReplicationStrategy.java
+++ b/src/java/org/apache/cassandra/locator/AbstractReplicationStrategy.java
@@ -125,10 +125,9 @@ public abstract class AbstractReplicationStrategy
return WriteResponseHandler.create(writeEndpoints, hintedEndpoints, consistencyLevel, table);
}
- // instance method so test subclasses can override it
- int getReplicationFactor()
+ public int getReplicationFactor()
{
- return DatabaseDescriptor.getReplicationFactor(table);
+ return DatabaseDescriptor.getTableDefinition(table).replicationFactor;
}
/**
diff --git a/src/java/org/apache/cassandra/net/MessagingService.java b/src/java/org/apache/cassandra/net/MessagingService.java
index 5da901c3bb..883eb0a3f7 100644
--- a/src/java/org/apache/cassandra/net/MessagingService.java
+++ b/src/java/org/apache/cassandra/net/MessagingService.java
@@ -29,9 +29,7 @@ import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.ServerSocketChannel;
import java.security.MessageDigest;
-import java.util.EnumMap;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@@ -226,7 +224,7 @@ public class MessagingService implements MessagingServiceMBean
* @return an reference to an IAsyncResult which can be queried for the
* response
*/
- public String sendRR(Message message, InetAddress[] to, IAsyncCallback cb)
+ public String sendRR(Message message, Collection to, IAsyncCallback cb)
{
String messageId = message.getMessageId();
addCallback(cb, messageId);
@@ -273,18 +271,16 @@ public class MessagingService implements MessagingServiceMBean
* suggest that a timeout occured to the invoker of the send().
* @return an reference to message id used to match with the result
*/
- public String sendRR(Message[] messages, InetAddress[] to, IAsyncCallback cb)
+ public String sendRR(Message[] messages, List to, IAsyncCallback cb)
{
- if ( messages.length != to.length )
- {
+ if (messages.length != to.size())
throw new IllegalArgumentException("Number of messages and the number of endpoints need to be same.");
- }
String groupId = GuidGenerator.guid();
addCallback(cb, groupId);
for ( int i = 0; i < messages.length; ++i )
{
messages[i].setMessageId(groupId);
- sendOneWay(messages[i], to[i]);
+ sendOneWay(messages[i], to.get(i));
}
return groupId;
}
diff --git a/src/java/org/apache/cassandra/service/ConsistencyChecker.java b/src/java/org/apache/cassandra/service/ConsistencyChecker.java
index a30b67ff93..f1bc2f2af7 100644
--- a/src/java/org/apache/cassandra/service/ConsistencyChecker.java
+++ b/src/java/org/apache/cassandra/service/ConsistencyChecker.java
@@ -156,7 +156,6 @@ class ConsistencyChecker implements Runnable
static class DataRepairHandler implements IAsyncCallback
{
- private final Collection responses_ = new LinkedBlockingQueue();
private final ReadResponseResolver readResponseResolver_;
private final int majority_;
@@ -167,7 +166,6 @@ class ConsistencyChecker implements Runnable
// wrap localRow in a response Message so it doesn't need to be special-cased in the resolver
ReadResponse readResponse = new ReadResponse(localRow);
Message fakeMessage = new Message(FBUtilities.getLocalAddress(), StorageService.Verb.INTERNAL_RESPONSE, ArrayUtils.EMPTY_BYTE_ARRAY);
- responses_.add(fakeMessage);
readResponseResolver_.injectPreProcessed(fakeMessage, readResponse);
}
@@ -176,15 +174,14 @@ class ConsistencyChecker implements Runnable
{
if (logger_.isDebugEnabled())
logger_.debug("Received response in DataRepairHandler : " + message.toString());
- responses_.add(message);
readResponseResolver_.preprocess(message);
- if (responses_.size() == majority_)
+ if (readResponseResolver_.getMessageCount() == majority_)
{
Runnable runnable = new WrappedRunnable()
{
public void runMayThrow() throws IOException, DigestMismatchException
{
- readResponseResolver_.resolve(responses_);
+ readResponseResolver_.resolve();
}
};
// give remaining replicas until timeout to reply and get added to responses_
diff --git a/src/java/org/apache/cassandra/service/DatacenterQuorumResponseHandler.java b/src/java/org/apache/cassandra/service/DatacenterQuorumResponseHandler.java
index 6267df94e9..0180d51dda 100644
--- a/src/java/org/apache/cassandra/service/DatacenterQuorumResponseHandler.java
+++ b/src/java/org/apache/cassandra/service/DatacenterQuorumResponseHandler.java
@@ -21,6 +21,9 @@ package org.apache.cassandra.service;
*/
+import java.net.InetAddress;
+import java.util.Collection;
+import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.cassandra.config.DatabaseDescriptor;
@@ -29,6 +32,7 @@ import org.apache.cassandra.locator.IEndpointSnitch;
import org.apache.cassandra.locator.NetworkTopologyStrategy;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.thrift.ConsistencyLevel;
+import org.apache.cassandra.thrift.UnavailableException;
import org.apache.cassandra.utils.FBUtilities;
/**
@@ -49,14 +53,14 @@ public class DatacenterQuorumResponseHandler extends QuorumResponseHandler
@Override
public void response(Message message)
{
- responses.add(message); // we'll go ahead and resolve a reply from anyone, even if it's not from this dc
+ resolver.preprocess(message);
int n;
n = localdc.equals(snitch.getDatacenter(message.getFrom()))
? localResponses.decrementAndGet()
: localResponses.get();
- if (n == 0 && responseResolver.isDataPresent(responses))
+ if (n == 0 && resolver.isDataPresent())
{
condition.signal();
}
@@ -68,4 +72,18 @@ public class DatacenterQuorumResponseHandler extends QuorumResponseHandler
NetworkTopologyStrategy stategy = (NetworkTopologyStrategy) Table.open(table).getReplicationStrategy();
return (stategy.getReplicationFactor(localdc) / 2) + 1;
}
+
+ @Override
+ public void assureSufficientLiveNodes(Collection endpoints) throws UnavailableException
+ {
+ int localEndpoints = 0;
+ for (InetAddress endpoint : endpoints)
+ {
+ if (localdc.equals(snitch.getDatacenter(endpoint)))
+ localEndpoints++;
+ }
+
+ if(localEndpoints < blockfor)
+ throw new UnavailableException();
+ }
}
diff --git a/src/java/org/apache/cassandra/service/IResponseResolver.java b/src/java/org/apache/cassandra/service/IResponseResolver.java
index 0b5b54f64e..ea61705455 100644
--- a/src/java/org/apache/cassandra/service/IResponseResolver.java
+++ b/src/java/org/apache/cassandra/service/IResponseResolver.java
@@ -34,8 +34,10 @@ public interface IResponseResolver {
* repairs . Hence you need to derive a response resolver based on your
* needs from this interface.
*/
- public T resolve(Collection responses) throws DigestMismatchException, IOException;
- public boolean isDataPresent(Collection responses);
+ public T resolve() throws DigestMismatchException, IOException;
+ public boolean isDataPresent();
public void preprocess(Message message);
+ public Iterable getMessages();
+ public int getMessageCount();
}
diff --git a/src/java/org/apache/cassandra/service/QuorumResponseHandler.java b/src/java/org/apache/cassandra/service/QuorumResponseHandler.java
index c21aac2e42..9d7c7fdf02 100644
--- a/src/java/org/apache/cassandra/service/QuorumResponseHandler.java
+++ b/src/java/org/apache/cassandra/service/QuorumResponseHandler.java
@@ -18,19 +18,20 @@
package org.apache.cassandra.service;
+import java.io.IOException;
+import java.net.InetAddress;
import java.util.Collection;
-import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.io.IOException;
import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.db.Table;
import org.apache.cassandra.net.IAsyncCallback;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.thrift.ConsistencyLevel;
+import org.apache.cassandra.thrift.UnavailableException;
import org.apache.cassandra.utils.SimpleCondition;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,19 +39,20 @@ public class QuorumResponseHandler implements IAsyncCallback
{
protected static final Logger logger = LoggerFactory.getLogger( QuorumResponseHandler.class );
protected final SimpleCondition condition = new SimpleCondition();
- protected final Collection responses = new LinkedBlockingQueue();;
- protected IResponseResolver responseResolver;
+ protected final IResponseResolver resolver;
private final long startTime;
- protected int blockfor;
+ protected final int blockfor;
/**
* Constructor when response count has to be calculated and blocked for.
*/
- public QuorumResponseHandler(IResponseResolver responseResolver, ConsistencyLevel consistencyLevel, String table)
+ public QuorumResponseHandler(IResponseResolver resolver, ConsistencyLevel consistencyLevel, String table)
{
this.blockfor = determineBlockFor(consistencyLevel, table);
- this.responseResolver = responseResolver;
+ this.resolver = resolver;
this.startTime = System.currentTimeMillis();
+
+ logger.debug("QuorumResponseHandler blocking for {} responses", blockfor);
}
public T get() throws TimeoutException, DigestMismatchException, IOException
@@ -71,35 +73,31 @@ public class QuorumResponseHandler implements IAsyncCallback
if (!success)
{
StringBuilder sb = new StringBuilder("");
- for (Message message : responses)
+ for (Message message : resolver.getMessages())
{
sb.append(message.getFrom());
}
- throw new TimeoutException("Operation timed out - received only " + responses.size() + " responses from " + sb.toString() + " .");
+ throw new TimeoutException("Operation timed out - received only " + resolver.getMessageCount() + " responses from " + sb.toString() + " .");
}
}
finally
{
- for (Message response : responses)
+ for (Message response : resolver.getMessages())
{
MessagingService.removeRegisteredCallback(response.getMessageId());
}
}
- return responseResolver.resolve(responses);
+ return resolver.resolve();
}
public void response(Message message)
{
- responses.add(message);
- responseResolver.preprocess(message);
- if (responses.size() < blockfor) {
+ resolver.preprocess(message);
+ if (resolver.getMessageCount() < blockfor)
return;
- }
- if (responseResolver.isDataPresent(responses))
- {
+ if (resolver.isDataPresent())
condition.signal();
- }
}
public int determineBlockFor(ConsistencyLevel consistencyLevel, String table)
@@ -110,11 +108,17 @@ public class QuorumResponseHandler implements IAsyncCallback
case ANY:
return 1;
case QUORUM:
- return (DatabaseDescriptor.getReplicationFactor(table) / 2) + 1;
+ return (Table.open(table).getReplicationStrategy().getReplicationFactor() / 2) + 1;
case ALL:
- return DatabaseDescriptor.getReplicationFactor(table);
+ return Table.open(table).getReplicationStrategy().getReplicationFactor();
default:
- throw new UnsupportedOperationException("invalid consistency level: " + table.toString());
+ throw new UnsupportedOperationException("invalid consistency level: " + consistencyLevel);
}
}
+
+ public void assureSufficientLiveNodes(Collection endpoints) throws UnavailableException
+ {
+ if (endpoints.size() < blockfor)
+ throw new UnavailableException();
+ }
}
diff --git a/src/java/org/apache/cassandra/service/RangeSliceResponseResolver.java b/src/java/org/apache/cassandra/service/RangeSliceResponseResolver.java
index bec0c1296d..8b98019a74 100644
--- a/src/java/org/apache/cassandra/service/RangeSliceResponseResolver.java
+++ b/src/java/org/apache/cassandra/service/RangeSliceResponseResolver.java
@@ -21,6 +21,7 @@ package org.apache.cassandra.service;
import java.io.IOException;
import java.net.InetAddress;
import java.util.*;
+import java.util.concurrent.LinkedBlockingQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -45,6 +46,7 @@ public class RangeSliceResponseResolver implements IResponseResolver>
private static final Logger logger_ = LoggerFactory.getLogger(RangeSliceResponseResolver.class);
private final String table;
private final List sources;
+ protected final Collection responses = new LinkedBlockingQueue();;
public RangeSliceResponseResolver(String table, List sources)
{
@@ -53,7 +55,7 @@ public class RangeSliceResponseResolver implements IResponseResolver>
this.table = table;
}
- public List resolve(Collection responses) throws DigestMismatchException, IOException
+ public List resolve() throws DigestMismatchException, IOException
{
CollatingIterator collator = new CollatingIterator(new Comparator>()
{
@@ -110,11 +112,12 @@ public class RangeSliceResponseResolver implements IResponseResolver>
public void preprocess(Message message)
{
+ responses.add(message);
}
- public boolean isDataPresent(Collection responses)
+ public boolean isDataPresent()
{
- return responses.size() >= sources.size();
+ return !responses.isEmpty();
}
private static class RowIterator extends AbstractIterator>
@@ -134,4 +137,14 @@ public class RangeSliceResponseResolver implements IResponseResolver>
return iter.hasNext() ? new Pair(iter.next(), source) : endOfData();
}
}
+
+ public Iterable getMessages()
+ {
+ return responses;
+ }
+
+ public int getMessageCount()
+ {
+ return responses.size();
+ }
}
diff --git a/src/java/org/apache/cassandra/service/ReadResponseResolver.java b/src/java/org/apache/cassandra/service/ReadResponseResolver.java
index 4a4d658b64..440f163776 100644
--- a/src/java/org/apache/cassandra/service/ReadResponseResolver.java
+++ b/src/java/org/apache/cassandra/service/ReadResponseResolver.java
@@ -58,14 +58,14 @@ public class ReadResponseResolver implements IResponseResolver
* repair request should be scheduled.
*
*/
- public Row resolve(Collection responses) throws DigestMismatchException, IOException
+ public Row resolve() throws DigestMismatchException, IOException
{
if (logger_.isDebugEnabled())
- logger_.debug("resolving " + responses.size() + " responses");
+ logger_.debug("resolving " + results.size() + " responses");
long startTime = System.currentTimeMillis();
- List versions = new ArrayList(responses.size());
- List endpoints = new ArrayList(responses.size());
+ List versions = new ArrayList();
+ List endpoints = new ArrayList();
DecoratedKey key = null;
ByteBuffer digest = FBUtilities.EMPTY_BYTE_BUFFER;
boolean isDigestQuery = false;
@@ -76,11 +76,10 @@ public class ReadResponseResolver implements IResponseResolver
* query exists then we need to compare the digest with
* the digest of the data that is received.
*/
- for (Message message : responses)
- {
- ReadResponse result = results.get(message);
- if (result == null)
- continue; // arrived after quorum already achieved
+ for (Map.Entry entry : results.entrySet())
+ {
+ ReadResponse result = entry.getValue();
+ Message message = entry.getKey();
if (result.isDigestQuery())
{
digest = result.digest();
@@ -187,6 +186,8 @@ public class ReadResponseResolver implements IResponseResolver
try
{
ReadResponse result = ReadResponse.serializer().deserialize(new DataInputStream(bufIn));
+ if (logger_.isDebugEnabled())
+ logger_.debug("Preprocessed {} response", result.isDigestQuery() ? "digest" : "data");
results.put(message, result);
}
catch (IOException e)
@@ -201,16 +202,23 @@ public class ReadResponseResolver implements IResponseResolver
results.put(message, result);
}
- public boolean isDataPresent(Collection responses)
+ public boolean isDataPresent()
{
- for (Message message : responses)
+ for (ReadResponse result : results.values())
{
- ReadResponse result = results.get(message);
- if (result == null)
- continue; // arrived concurrently
if (!result.isDigestQuery())
return true;
}
return false;
}
+
+ public Iterable getMessages()
+ {
+ return results.keySet();
+ }
+
+ public int getMessageCount()
+ {
+ return results.size();
+ }
}
diff --git a/src/java/org/apache/cassandra/service/StorageProxy.java b/src/java/org/apache/cassandra/service/StorageProxy.java
index 30104916d8..a1e97d06b8 100644
--- a/src/java/org/apache/cassandra/service/StorageProxy.java
+++ b/src/java/org/apache/cassandra/service/StorageProxy.java
@@ -201,7 +201,7 @@ public class StorageProxy implements StorageProxyMBean
{
public void runMayThrow() throws IOException
{
- rm.apply();
+ rm.deepCopy().apply();
responseHandler.response(null);
}
};
@@ -315,7 +315,7 @@ public class StorageProxy implements StorageProxyMBean
private static List strongRead(List commands, ConsistencyLevel consistency_level) throws IOException, UnavailableException, TimeoutException
{
List> quorumResponseHandlers = new ArrayList>();
- List commandEndpoints = new ArrayList();
+ List> commandEndpoints = new ArrayList>();
List rows = new ArrayList();
// send out read requests
@@ -328,25 +328,25 @@ public class StorageProxy implements StorageProxyMBean
Message messageDigestOnly = readMessageDigestOnly.makeReadMessage();
InetAddress dataPoint = StorageService.instance.findSuitableEndpoint(command.table, command.key);
- List endpointList = StorageService.instance.getLiveNaturalEndpoints(command.table, command.key);
+ List endpoints = StorageService.instance.getLiveNaturalEndpoints(command.table, command.key);
- InetAddress[] endpoints = new InetAddress[endpointList.size()];
- Message messages[] = new Message[endpointList.size()];
+ AbstractReplicationStrategy rs = Table.open(command.table).getReplicationStrategy();
+ QuorumResponseHandler handler = rs.getQuorumResponseHandler(new ReadResponseResolver(command.table), consistency_level);
+ handler.assureSufficientLiveNodes(endpoints);
+
+ Message messages[] = new Message[endpoints.size()];
// data-request message is sent to dataPoint, the node that will actually get
// the data for us. The other replicas are only sent a digest query.
int n = 0;
- for (InetAddress endpoint : endpointList)
+ for (InetAddress endpoint : endpoints)
{
Message m = endpoint.equals(dataPoint) ? message : messageDigestOnly;
- endpoints[n] = endpoint;
messages[n++] = m;
if (logger.isDebugEnabled())
logger.debug("strongread reading " + (m == message ? "data" : "digest") + " for " + command + " from " + m.getMessageId() + "@" + endpoint);
}
- AbstractReplicationStrategy rs = Table.open(command.table).getReplicationStrategy();
- QuorumResponseHandler quorumResponseHandler = rs.getQuorumResponseHandler(new ReadResponseResolver(command.table), consistency_level);
- MessagingService.instance.sendRR(messages, endpoints, quorumResponseHandler);
- quorumResponseHandlers.add(quorumResponseHandler);
+ MessagingService.instance.sendRR(messages, endpoints, handler);
+ quorumResponseHandlers.add(handler);
commandEndpoints.add(endpoints);
}
@@ -370,14 +370,14 @@ public class StorageProxy implements StorageProxyMBean
catch (DigestMismatchException ex)
{
AbstractReplicationStrategy rs = Table.open(command.table).getReplicationStrategy();
- QuorumResponseHandler qrhRepair = rs.getQuorumResponseHandler(new ReadResponseResolver(command.table), ConsistencyLevel.QUORUM);
+ QuorumResponseHandler handler = rs.getQuorumResponseHandler(new ReadResponseResolver(command.table), consistency_level);
if (logger.isDebugEnabled())
logger.debug("Digest mismatch:", ex);
Message messageRepair = command.makeReadMessage();
- MessagingService.instance.sendRR(messageRepair, commandEndpoints.get(i), qrhRepair);
+ MessagingService.instance.sendRR(messageRepair, commandEndpoints.get(i), handler);
if (repairResponseHandlers == null)
repairResponseHandlers = new ArrayList>();
- repairResponseHandlers.add(qrhRepair);
+ repairResponseHandlers.add(handler);
}
}
@@ -499,7 +499,7 @@ public class StorageProxy implements StorageProxyMBean
final Message msg = new Message(FBUtilities.getLocalAddress(), StorageService.Verb.SCHEMA_CHECK, ArrayUtils.EMPTY_BYTE_ARRAY);
final CountDownLatch latch = new CountDownLatch(liveHosts.size());
// an empty message acts as a request to the SchemaCheckVerbHandler.
- MessagingService.instance.sendRR(msg, liveHosts.toArray(new InetAddress[]{}), new IAsyncCallback()
+ MessagingService.instance.sendRR(msg, liveHosts, new IAsyncCallback()
{
public void response(Message msg)
{
@@ -774,7 +774,7 @@ public class StorageProxy implements StorageProxyMBean
logger.debug("Starting to send truncate messages to hosts {}", allEndpoints);
Truncation truncation = new Truncation(keyspace, cfname);
Message message = truncation.makeTruncationMessage();
- MessagingService.instance.sendRR(message, allEndpoints.toArray(new InetAddress[]{}), responseHandler);
+ MessagingService.instance.sendRR(message, allEndpoints, responseHandler);
// Wait for all
logger.debug("Sent all truncate messages, now waiting for {} responses", blockFor);
diff --git a/src/java/org/apache/cassandra/service/StorageService.java b/src/java/org/apache/cassandra/service/StorageService.java
index 7471aaf55d..2bb83f9838 100644
--- a/src/java/org/apache/cassandra/service/StorageService.java
+++ b/src/java/org/apache/cassandra/service/StorageService.java
@@ -1738,7 +1738,7 @@ public class StorageService implements IEndpointStateChangeSubscriber, StorageSe
for (String table : DatabaseDescriptor.getNonSystemTables())
{
// if the replication factor is 1 the data is lost so we shouldn't wait for confirmation
- if (DatabaseDescriptor.getReplicationFactor(table) == 1)
+ if (Table.open(table).getReplicationStrategy().getReplicationFactor() == 1)
continue;
// get all ranges that change ownership (that is, a node needs
diff --git a/src/java/org/apache/cassandra/service/WriteResponseHandler.java b/src/java/org/apache/cassandra/service/WriteResponseHandler.java
index 3ea15c97f4..95e5e3f362 100644
--- a/src/java/org/apache/cassandra/service/WriteResponseHandler.java
+++ b/src/java/org/apache/cassandra/service/WriteResponseHandler.java
@@ -26,6 +26,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.db.Table;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.thrift.ConsistencyLevel;
import org.apache.cassandra.thrift.UnavailableException;
@@ -93,9 +94,9 @@ public class WriteResponseHandler extends AbstractWriteResponseHandler
}
// at most one node per range can bootstrap at a time, and these will be added to the write until
// bootstrap finishes (at which point we no longer need to write to the old ones).
- assert 1 <= blockFor && blockFor <= 2 * DatabaseDescriptor.getReplicationFactor(table)
+ assert 1 <= blockFor && blockFor <= 2 * Table.open(table).getReplicationStrategy().getReplicationFactor()
: String.format("invalid response count %d for replication factor %d",
- blockFor, DatabaseDescriptor.getReplicationFactor(table));
+ blockFor, Table.open(table).getReplicationStrategy().getReplicationFactor());
return blockFor;
}
diff --git a/src/java/org/apache/cassandra/thrift/CassandraServer.java b/src/java/org/apache/cassandra/thrift/CassandraServer.java
index 028fb6e169..e8d659c465 100644
--- a/src/java/org/apache/cassandra/thrift/CassandraServer.java
+++ b/src/java/org/apache/cassandra/thrift/CassandraServer.java
@@ -138,6 +138,7 @@ public class CassandraServer implements Cassandra.Iface
}
catch (TimeoutException e)
{
+ logger.debug("... timed out");
throw new TimedOutException();
}
catch (IOException e)
@@ -260,8 +261,7 @@ public class CassandraServer implements Cassandra.Iface
public List get_slice(ByteBuffer key, ColumnParent column_parent, SlicePredicate predicate, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException
{
- if (logger.isDebugEnabled())
- logger.debug("get_slice");
+ logger.debug("get_slice");
state().hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
return multigetSliceInternal(state().getKeyspace(), Collections.singletonList(key), column_parent, predicate, consistency_level).get(key);
@@ -270,8 +270,7 @@ public class CassandraServer implements Cassandra.Iface
public Map> multiget_slice(List keys, ColumnParent column_parent, SlicePredicate predicate, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException
{
- if (logger.isDebugEnabled())
- logger.debug("multiget_slice");
+ logger.debug("multiget_slice");
state().hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
@@ -309,8 +308,7 @@ public class CassandraServer implements Cassandra.Iface
public ColumnOrSuperColumn get(ByteBuffer key, ColumnPath column_path, ConsistencyLevel consistency_level)
throws InvalidRequestException, NotFoundException, UnavailableException, TimedOutException
{
- if (logger.isDebugEnabled())
- logger.debug("get");
+ logger.debug("get");
state().hasColumnFamilyAccess(column_path.column_family, Permission.READ);
String keyspace = state().getKeyspace();
@@ -338,8 +336,7 @@ public class CassandraServer implements Cassandra.Iface
public int get_count(ByteBuffer key, ColumnParent column_parent, SlicePredicate predicate, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException
{
- if (logger.isDebugEnabled())
- logger.debug("get_count");
+ logger.debug("get_count");
state().hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
@@ -349,8 +346,7 @@ public class CassandraServer implements Cassandra.Iface
public Map multiget_count(List keys, ColumnParent column_parent, SlicePredicate predicate, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException
{
- if (logger.isDebugEnabled())
- logger.debug("multiget_count");
+ logger.debug("multiget_count");
state().hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
String keyspace = state().getKeyspace();
@@ -367,8 +363,7 @@ public class CassandraServer implements Cassandra.Iface
public void insert(ByteBuffer key, ColumnParent column_parent, Column column, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException
{
- if (logger.isDebugEnabled())
- logger.debug("insert");
+ logger.debug("insert");
state().hasColumnFamilyAccess(column_parent.column_family, Permission.WRITE);
@@ -391,8 +386,7 @@ public class CassandraServer implements Cassandra.Iface
public void batch_mutate(Map>> mutation_map, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException
{
- if (logger.isDebugEnabled())
- logger.debug("batch_mutate");
+ logger.debug("batch_mutate");
List cfamsSeen = new ArrayList();
@@ -428,8 +422,7 @@ public class CassandraServer implements Cassandra.Iface
public void remove(ByteBuffer key, ColumnPath column_path, long timestamp, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TimedOutException
{
- if (logger.isDebugEnabled())
- logger.debug("remove");
+ logger.debug("remove");
state().hasColumnFamilyAccess(column_path.column_family, Permission.WRITE);
@@ -450,11 +443,12 @@ public class CassandraServer implements Cassandra.Iface
try
{
- StorageProxy.mutate(mutations, consistency_level);
+ StorageProxy.mutate(mutations, consistency_level);
}
catch (TimeoutException e)
{
- throw new TimedOutException();
+ logger.debug("... timed out");
+ throw new TimedOutException();
}
}
finally
@@ -482,8 +476,7 @@ public class CassandraServer implements Cassandra.Iface
public List get_range_slices(ColumnParent column_parent, SlicePredicate predicate, KeyRange range, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TException, TimedOutException
{
- if (logger.isDebugEnabled())
- logger.debug("range_slice");
+ logger.debug("range_slice");
String keyspace = state().getKeyspace();
state().hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
@@ -521,6 +514,7 @@ public class CassandraServer implements Cassandra.Iface
}
catch (TimeoutException e)
{
+ logger.debug("... timed out");
throw new TimedOutException();
}
catch (IOException e)
@@ -546,8 +540,7 @@ public class CassandraServer implements Cassandra.Iface
public List get_indexed_slices(ColumnParent column_parent, IndexClause index_clause, SlicePredicate column_predicate, ConsistencyLevel consistency_level) throws InvalidRequestException, UnavailableException, TimedOutException, TException
{
- if (logger.isDebugEnabled())
- logger.debug("scan");
+ logger.debug("scan");
state().hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
String keyspace = state().getKeyspace();
@@ -566,6 +559,7 @@ public class CassandraServer implements Cassandra.Iface
}
catch (TimeoutException e)
{
+ logger.debug("... timed out");
throw new TimedOutException();
}
return thriftifyKeySlices(rows, column_parent, column_predicate);
@@ -703,6 +697,7 @@ public class CassandraServer implements Cassandra.Iface
public String system_add_column_family(CfDef cf_def) throws InvalidRequestException, TException
{
+ logger.debug("add_column_family");
state().hasColumnFamilyListAccess(Permission.WRITE);
ThriftValidation.validateCfDef(cf_def);
try
@@ -726,6 +721,7 @@ public class CassandraServer implements Cassandra.Iface
public String system_drop_column_family(String column_family) throws InvalidRequestException, TException
{
+ logger.debug("drop_column_family");
state().hasColumnFamilyListAccess(Permission.WRITE);
try
@@ -749,6 +745,7 @@ public class CassandraServer implements Cassandra.Iface
public String system_add_keyspace(KsDef ks_def) throws InvalidRequestException, TException
{
+ logger.debug("add_keyspace");
state().hasKeyspaceListAccess(Permission.WRITE);
// generate a meaningful error if the user setup keyspace and/or column definition incorrectly
@@ -765,6 +762,7 @@ public class CassandraServer implements Cassandra.Iface
Collection cfDefs = new ArrayList(ks_def.cf_defs.size());
for (CfDef cfDef : ks_def.cf_defs)
{
+ ThriftValidation.validateCfDef(cfDef);
cfDefs.add(convertToCFMetaData(cfDef));
}
@@ -792,6 +790,7 @@ public class CassandraServer implements Cassandra.Iface
public String system_drop_keyspace(String keyspace) throws InvalidRequestException, TException
{
+ logger.debug("drop_keyspace");
state().hasKeyspaceListAccess(Permission.WRITE);
try
@@ -816,6 +815,7 @@ public class CassandraServer implements Cassandra.Iface
/** update an existing keyspace, but do not allow column family modifications. */
public String system_update_keyspace(KsDef ks_def) throws InvalidRequestException, TException
{
+ logger.debug("update_keyspace");
state().hasKeyspaceListAccess(Permission.WRITE);
ThriftValidation.validateTable(ks_def.name);
@@ -848,6 +848,7 @@ public class CassandraServer implements Cassandra.Iface
public String system_update_column_family(CfDef cf_def) throws InvalidRequestException, TException
{
+ logger.debug("update_column_family");
state().hasColumnFamilyListAccess(Permission.WRITE);
if (cf_def.keyspace == null || cf_def.name == null)
diff --git a/src/java/org/apache/cassandra/thrift/ThriftValidation.java b/src/java/org/apache/cassandra/thrift/ThriftValidation.java
index 57db71be41..062ba6af79 100644
--- a/src/java/org/apache/cassandra/thrift/ThriftValidation.java
+++ b/src/java/org/apache/cassandra/thrift/ThriftValidation.java
@@ -383,14 +383,20 @@ public class ThriftValidation
{
try
{
+ ColumnFamilyType cfType = ColumnFamilyType.create(cf_def.column_type);
+ if (cfType == null)
+ throw new InvalidRequestException("invalid column type " + cf_def.column_type);
+
DatabaseDescriptor.getComparator(cf_def.comparator_type);
DatabaseDescriptor.getComparator(cf_def.subcomparator_type);
DatabaseDescriptor.getComparator(cf_def.default_validation_class);
+ if (cfType != ColumnFamilyType.Super && cf_def.subcomparator_type != null)
+ throw new InvalidRequestException("subcomparator_type is invalid for standard columns");
if (cf_def.column_metadata == null)
return;
- AbstractType comparator = cf_def.subcomparator_type == null
+ AbstractType comparator = cfType == ColumnFamilyType.Standard
? DatabaseDescriptor.getComparator(cf_def.comparator_type)
: DatabaseDescriptor.getComparator(cf_def.subcomparator_type);
for (ColumnDef c : cf_def.column_metadata)
@@ -406,6 +412,12 @@ public class ThriftValidation
throw new InvalidRequestException(String.format("Column name %s is not valid for comparator %s",
FBUtilities.bytesToHex(c.name), cf_def.comparator_type));
}
+
+ if ((c.index_name != null) && (c.index_type == null))
+ throw new ConfigurationException("index_name cannot be set without index_type");
+
+ if (cfType == ColumnFamilyType.Super && c.index_type != null)
+ throw new InvalidRequestException("Secondary indexes are not supported on supercolumns");
}
}
catch (ConfigurationException e)
diff --git a/src/java/org/apache/cassandra/utils/ByteBufferUtil.java b/src/java/org/apache/cassandra/utils/ByteBufferUtil.java
index a96a33001f..80fdd229ef 100644
--- a/src/java/org/apache/cassandra/utils/ByteBufferUtil.java
+++ b/src/java/org/apache/cassandra/utils/ByteBufferUtil.java
@@ -60,8 +60,8 @@ import java.nio.charset.Charset;
* }
*
*/
-public class ByteBufferUtil {
-
+public class ByteBufferUtil
+{
public static int compareUnsigned(ByteBuffer o1, ByteBuffer o2)
{
return FBUtilities.compareUnsigned(o1.array(), o2.array(), o1.arrayOffset()+o1.position(), o2.arrayOffset()+o2.position(), o1.limit()+o1.arrayOffset(), o2.limit()+o2.arrayOffset());
@@ -98,4 +98,14 @@ public class ByteBufferUtil {
throw new RuntimeException(e);
}
}
+
+ public static ByteBuffer clone(ByteBuffer o)
+ {
+ ByteBuffer clone = ByteBuffer.allocate(o.remaining());
+ o.mark();
+ clone.put(o);
+ o.reset();
+ clone.flip();
+ return clone;
+ }
}
diff --git a/test/unit/org/apache/cassandra/cli/CliTest.java b/test/unit/org/apache/cassandra/cli/CliTest.java
index 50d4a01b7d..6b74e78f35 100644
--- a/test/unit/org/apache/cassandra/cli/CliTest.java
+++ b/test/unit/org/apache/cassandra/cli/CliTest.java
@@ -36,11 +36,13 @@ public class CliTest extends CleanupHelper
// please add new statements here so they could be auto-runned by this test.
private String[] statements = {
"use TestKeySpace;",
- "create column family CF1 with comparator=UTF8Type and column_metadata=[{ column_name:world, validation_class:IntegerType, index_type:0, index_name:IdxName }, { column_name:world2, validation_class:LongType, index_type:0, index_name:LongIdxName}];",
+ "create column family CF1 with comparator=UTF8Type and column_metadata=[{ column_name:world, validation_class:IntegerType, index_type:0, index_name:IdxName }, { column_name:world2, validation_class:LongType, index_type:KEYS, index_name:LongIdxName}];",
"set CF1[hello][world] = 123848374878933948398384;",
"get CF1[hello][world];",
"set CF1[hello][world2] = 15;",
"get CF1 where world2 = long(15);",
+ "get cF1 where world2 = long(15);",
+ "get Cf1 where world2 = long(15);",
"set CF1['hello'][time_spent_uuid] = timeuuid(a8098c1a-f86e-11da-bd1a-00112444be1e);",
"create column family CF2 with comparator=IntegerType;",
"set CF2['key'][98349387493847748398334] = 'some text';",
diff --git a/test/unit/org/apache/cassandra/dht/BootStrapperTest.java b/test/unit/org/apache/cassandra/dht/BootStrapperTest.java
index 7fc460203a..d42e87c270 100644
--- a/test/unit/org/apache/cassandra/dht/BootStrapperTest.java
+++ b/test/unit/org/apache/cassandra/dht/BootStrapperTest.java
@@ -33,6 +33,7 @@ import org.junit.Test;
import com.google.common.collect.Multimap;
+import org.apache.cassandra.db.Table;
import org.apache.cassandra.gms.ApplicationState;
import org.apache.cassandra.gms.IFailureDetectionEventListener;
import org.apache.cassandra.gms.IFailureDetector;
@@ -146,7 +147,7 @@ public class BootStrapperTest extends CleanupHelper
final int[] clusterSizes = new int[] { 1, 3, 5, 10, 100};
for (String table : DatabaseDescriptor.getNonSystemTables())
{
- int replicationFactor = DatabaseDescriptor.getReplicationFactor(table);
+ int replicationFactor = Table.open(table).getReplicationStrategy().getReplicationFactor();
for (int clusterSize : clusterSizes)
if (clusterSize >= replicationFactor)
testSourceTargetComputation(table, clusterSize, replicationFactor);
diff --git a/test/unit/org/apache/cassandra/locator/OldNetworkTopologyStrategyTest.java b/test/unit/org/apache/cassandra/locator/OldNetworkTopologyStrategyTest.java
index c42d7c3e2c..288491d3a8 100644
--- a/test/unit/org/apache/cassandra/locator/OldNetworkTopologyStrategyTest.java
+++ b/test/unit/org/apache/cassandra/locator/OldNetworkTopologyStrategyTest.java
@@ -30,11 +30,12 @@ import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
-import org.apache.cassandra.config.DatabaseDescriptor;
+
+import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.dht.BigIntegerToken;
import org.apache.cassandra.dht.Token;
-public class OldNetworkTopologyStrategyTest
+public class OldNetworkTopologyStrategyTest extends SchemaLoader
{
private List endpointTokens;
private List keyTokens;
@@ -71,7 +72,7 @@ public class OldNetworkTopologyStrategyTest
expectedResults.put("25", buildResult("254.0.0.4", "254.0.0.1", "254.0.0.2"));
expectedResults.put("35", buildResult("254.0.0.1", "254.0.0.2", "254.0.0.3"));
- runTestForReplicatedTables(strategy);
+ testGetEndpoints(strategy, keyTokens.toArray(new Token[0]));
}
/**
@@ -96,7 +97,7 @@ public class OldNetworkTopologyStrategyTest
expectedResults.put("25", buildResult("254.0.0.4", "254.1.0.3", "254.0.0.1"));
expectedResults.put("35", buildResult("254.0.0.1", "254.1.0.3", "254.0.0.2"));
- runTestForReplicatedTables(strategy);
+ testGetEndpoints(strategy, keyTokens.toArray(new Token[0]));
}
/**
@@ -122,16 +123,7 @@ public class OldNetworkTopologyStrategyTest
expectedResults.put("25", buildResult("254.1.0.4", "254.0.0.1", "254.0.0.2"));
expectedResults.put("35", buildResult("254.0.0.1", "254.0.1.3", "254.1.0.4"));
- runTestForReplicatedTables(strategy);
- }
-
- private void runTestForReplicatedTables(AbstractReplicationStrategy strategy) throws UnknownHostException
- {
- for (String table : DatabaseDescriptor.getNonSystemTables())
- {
- if (DatabaseDescriptor.getReplicationFactor(table) == 3)
- testGetEndpoints(strategy, keyTokens.toArray(new Token[0]), table);
- }
+ testGetEndpoints(strategy, keyTokens.toArray(new Token[0]));
}
private ArrayList buildResult(String... addresses) throws UnknownHostException
@@ -156,7 +148,7 @@ public class OldNetworkTopologyStrategyTest
tmd.updateNormalToken(endpointToken, ep);
}
- private void testGetEndpoints(AbstractReplicationStrategy strategy, Token[] keyTokens, String table) throws UnknownHostException
+ private void testGetEndpoints(AbstractReplicationStrategy strategy, Token[] keyTokens) throws UnknownHostException
{
for (Token keyToken : keyTokens)
{
diff --git a/test/unit/org/apache/cassandra/locator/SimpleStrategyTest.java b/test/unit/org/apache/cassandra/locator/SimpleStrategyTest.java
index f53680c783..56d7089ac4 100644
--- a/test/unit/org/apache/cassandra/locator/SimpleStrategyTest.java
+++ b/test/unit/org/apache/cassandra/locator/SimpleStrategyTest.java
@@ -94,7 +94,7 @@ public class SimpleStrategyTest extends CleanupHelper
for (int i = 0; i < keyTokens.length; i++)
{
List endpoints = strategy.getNaturalEndpoints(keyTokens[i]);
- assertEquals(DatabaseDescriptor.getReplicationFactor(table), endpoints.size());
+ assertEquals(strategy.getReplicationFactor(), endpoints.size());
List correctEndpoints = new ArrayList();
for (int j = 0; j < endpoints.size(); j++)
correctEndpoints.add(hosts.get((i + j + 1) % hosts.size()));
@@ -140,7 +140,7 @@ public class SimpleStrategyTest extends CleanupHelper
StorageService.calculatePendingRanges(strategy, table);
- int replicationFactor = DatabaseDescriptor.getReplicationFactor(table);
+ int replicationFactor = strategy.getReplicationFactor();
for (int i = 0; i < keyTokens.length; i++)
{
diff --git a/test/unit/org/apache/cassandra/service/AntiEntropyServiceTest.java b/test/unit/org/apache/cassandra/service/AntiEntropyServiceTest.java
index ccbdcc0157..7402233469 100644
--- a/test/unit/org/apache/cassandra/service/AntiEntropyServiceTest.java
+++ b/test/unit/org/apache/cassandra/service/AntiEntropyServiceTest.java
@@ -171,7 +171,7 @@ public class AntiEntropyServiceTest extends CleanupHelper
public void testGetNeighborsPlusOne() throws Throwable
{
// generate rf+1 nodes, and ensure that all nodes are returned
- Set expected = addTokens(1 + DatabaseDescriptor.getReplicationFactor(tablename));
+ Set expected = addTokens(1 + Table.open(tablename).getReplicationStrategy().getReplicationFactor());
expected.remove(FBUtilities.getLocalAddress());
assertEquals(expected, AntiEntropyService.getNeighbors(tablename));
}
@@ -182,7 +182,7 @@ public class AntiEntropyServiceTest extends CleanupHelper
TokenMetadata tmd = StorageService.instance.getTokenMetadata();
// generate rf*2 nodes, and ensure that only neighbors specified by the ARS are returned
- addTokens(2 * DatabaseDescriptor.getReplicationFactor(tablename));
+ addTokens(2 * Table.open(tablename).getReplicationStrategy().getReplicationFactor());
AbstractReplicationStrategy ars = Table.open(tablename).getReplicationStrategy();
Set expected = new HashSet();
for (Range replicaRange : ars.getAddressRanges().get(FBUtilities.getLocalAddress()))
diff --git a/test/unit/org/apache/cassandra/service/ConsistencyLevelTest.java b/test/unit/org/apache/cassandra/service/ConsistencyLevelTest.java
new file mode 100644
index 0000000000..2ebb5bf395
--- /dev/null
+++ b/test/unit/org/apache/cassandra/service/ConsistencyLevelTest.java
@@ -0,0 +1,166 @@
+package org.apache.cassandra.service;
+/*
+ *
+ * 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.
+ *
+ */
+
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.common.collect.HashMultimap;
+import org.junit.Test;
+
+import org.apache.cassandra.CleanupHelper;
+import org.apache.cassandra.Util;
+import org.apache.cassandra.config.ConfigurationException;
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.db.Row;
+import org.apache.cassandra.dht.IPartitioner;
+import org.apache.cassandra.dht.RandomPartitioner;
+import org.apache.cassandra.dht.Token;
+import org.apache.cassandra.locator.AbstractReplicationStrategy;
+import org.apache.cassandra.locator.SimpleSnitch;
+import org.apache.cassandra.locator.TokenMetadata;
+import org.apache.cassandra.thrift.ConsistencyLevel;
+import org.apache.cassandra.thrift.UnavailableException;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class ConsistencyLevelTest extends CleanupHelper
+{
+ @Test
+ public void testReadWriteConsistencyChecks() throws Exception
+ {
+ StorageService ss = StorageService.instance;
+ final int RING_SIZE = 3;
+
+ TokenMetadata tmd = ss.getTokenMetadata();
+ tmd.clearUnsafe();
+ IPartitioner partitioner = new RandomPartitioner();
+
+ ss.setPartitionerUnsafe(partitioner);
+
+ ArrayList endpointTokens = new ArrayList();
+ ArrayList keyTokens = new ArrayList();
+ List hosts = new ArrayList();
+
+ Util.createInitialRing(ss, partitioner, endpointTokens, keyTokens, hosts, RING_SIZE);
+
+ HashMultimap hintedNodes = HashMultimap.create();
+
+
+ AbstractReplicationStrategy strategy;
+
+ for (String table : DatabaseDescriptor.getNonSystemTables())
+ {
+ strategy = getStrategy(table, tmd);
+ StorageService.calculatePendingRanges(strategy, table);
+ int replicationFactor = strategy.getReplicationFactor();
+ if (replicationFactor < 2)
+ continue;
+
+ for (ConsistencyLevel c : ConsistencyLevel.values())
+ {
+
+ if (c == ConsistencyLevel.EACH_QUORUM || c == ConsistencyLevel.LOCAL_QUORUM)
+ continue;
+
+ for (int i = 0; i < replicationFactor; i++)
+ {
+ hintedNodes.clear();
+
+ for (int j = 0; j < i; j++)
+ {
+ hintedNodes.put(hosts.get(j), hosts.get(j));
+ }
+
+ IWriteResponseHandler writeHandler = strategy.getWriteResponseHandler(hosts, hintedNodes, c);
+
+ QuorumResponseHandler readHandler = strategy.getQuorumResponseHandler(new ReadResponseResolver(table), c);
+
+ boolean isWriteUnavailable = false;
+ boolean isReadUnavailable = false;
+ try
+ {
+ writeHandler.assureSufficientLiveNodes();
+ }
+ catch (UnavailableException e)
+ {
+ isWriteUnavailable = true;
+ }
+
+ try
+ {
+ readHandler.assureSufficientLiveNodes(hintedNodes.asMap().keySet());
+ }
+ catch (UnavailableException e)
+ {
+ isReadUnavailable = true;
+ }
+
+ //these should always match (in this kind of test)
+ assertTrue(isWriteUnavailable == isReadUnavailable);
+
+ switch (c)
+ {
+ case ALL:
+ if (isWriteUnavailable)
+ assertTrue(hintedNodes.size() < replicationFactor);
+ else
+ assertTrue(hintedNodes.size() >= replicationFactor);
+
+ break;
+ case ONE:
+ case ANY:
+ if (isWriteUnavailable)
+ assertTrue(hintedNodes.size() == 0);
+ else
+ assertTrue(hintedNodes.size() > 0);
+ break;
+ case QUORUM:
+ if (isWriteUnavailable)
+ assertTrue(hintedNodes.size() < (replicationFactor / 2 + 1));
+ else
+ assertTrue(hintedNodes.size() >= (replicationFactor / 2 + 1));
+ break;
+ default:
+ fail("Unhandled CL: " + c);
+
+ }
+ }
+ }
+ return;
+ }
+
+ fail("Test requires at least one table with RF > 1");
+ }
+
+ private AbstractReplicationStrategy getStrategy(String table, TokenMetadata tmd) throws ConfigurationException
+ {
+ return AbstractReplicationStrategy.createReplicationStrategy(table,
+ "org.apache.cassandra.locator.SimpleStrategy",
+ tmd,
+ new SimpleSnitch(),
+ null);
+ }
+
+}
diff --git a/test/unit/org/apache/cassandra/service/MoveTest.java b/test/unit/org/apache/cassandra/service/MoveTest.java
index ba75a22d93..90b80ef7c3 100644
--- a/test/unit/org/apache/cassandra/service/MoveTest.java
+++ b/test/unit/org/apache/cassandra/service/MoveTest.java
@@ -92,7 +92,7 @@ public class MoveTest extends CleanupHelper
strategy = getStrategy(table, tmd);
for (Token token : keyTokens)
{
- int replicationFactor = DatabaseDescriptor.getReplicationFactor(table);
+ int replicationFactor = strategy.getReplicationFactor();
HashSet actual = new HashSet(tmd.getWriteEndpoints(token, table, strategy.calculateNaturalEndpoints(token, tmd)));
HashSet expected = new HashSet();
@@ -217,7 +217,7 @@ public class MoveTest extends CleanupHelper
}
// just to be sure that things still work according to the old tests, run them:
- if (DatabaseDescriptor.getReplicationFactor(table) != 3)
+ if (strategy.getReplicationFactor() != 3)
continue;
// tokens 5, 15 and 25 should go three nodes
for (int i=0; i<3; ++i)
@@ -334,7 +334,7 @@ public class MoveTest extends CleanupHelper
assertTrue(expectedEndpoints.get(table).get(keyTokens.get(i)).containsAll(endpoints));
}
- if (DatabaseDescriptor.getReplicationFactor(table) != 3)
+ if (strategy.getReplicationFactor() != 3)
continue;
// leave this stuff in to guarantee the old tests work the way they were supposed to.
// tokens 5, 15 and 25 should go three nodes