You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by jb...@apache.org on 2009/11/26 00:16:58 UTC
svn commit: r884333 - in /incubator/cassandra/trunk:
src/java/org/apache/cassandra/db/ src/java/org/apache/cassandra/service/
test/system/ test/unit/org/apache/cassandra/service/
Author: jbellis
Date: Wed Nov 25 23:16:58 2009
New Revision: 884333
URL: http://svn.apache.org/viewvc?rev=884333&view=rev
Log:
allow serializing null CF; add get_range_slice test exercising this
patch by jbellis; tested by Dan Di Spaltro for CASSANDRA-578
Added:
incubator/cassandra/trunk/test/unit/org/apache/cassandra/service/ReadResponseResolverTest.java
Modified:
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamily.java
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilySerializer.java
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/Row.java
incubator/cassandra/trunk/src/java/org/apache/cassandra/service/ConsistencyManager.java
incubator/cassandra/trunk/src/java/org/apache/cassandra/service/ReadResponseResolver.java
incubator/cassandra/trunk/src/java/org/apache/cassandra/service/StorageProxy.java
incubator/cassandra/trunk/test/system/test_server.py
Modified: incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamily.java
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamily.java?rev=884333&r1=884332&r2=884333&view=diff
==============================================================================
--- incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamily.java (original)
+++ incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamily.java Wed Nov 25 23:16:58 2009
@@ -103,7 +103,7 @@
return (columnSerializer_ instanceof SuperColumnSerializer) ? ((SuperColumnSerializer)columnSerializer_).getComparator() : null;
}
- ColumnFamily cloneMe()
+ public ColumnFamily cloneMe()
{
ColumnFamily cf = cloneMeShallow();
cf.columns_ = columns_.clone();
@@ -399,6 +399,9 @@
public void resolve(ColumnFamily cf)
{
+ // Row _does_ allow null CF objects :( seems a necessary evil for efficiency
+ if (cf == null)
+ return;
delete(cf);
addAll(cf);
}
Modified: incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilySerializer.java
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilySerializer.java?rev=884333&r1=884332&r2=884333&view=diff
==============================================================================
--- incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilySerializer.java (original)
+++ incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilySerializer.java Wed Nov 25 23:16:58 2009
@@ -55,6 +55,12 @@
{
try
{
+ if (columnFamily == null)
+ {
+ dos.writeUTF(""); // not a legal CF name
+ return;
+ }
+
dos.writeUTF(columnFamily.name());
dos.writeUTF(columnFamily.type_);
dos.writeUTF(columnFamily.getComparatorName());
@@ -95,7 +101,10 @@
public ColumnFamily deserialize(DataInput dis) throws IOException
{
- ColumnFamily cf = deserializeFromSSTableNoColumns(dis.readUTF(), dis.readUTF(), readComparator(dis), readComparator(dis), dis);
+ String cfName = dis.readUTF();
+ if (cfName.isEmpty())
+ return null;
+ ColumnFamily cf = deserializeFromSSTableNoColumns(cfName, dis.readUTF(), readComparator(dis), readComparator(dis), dis);
deserializeColumns(dis, cf);
return cf;
}
Modified: incubator/cassandra/trunk/src/java/org/apache/cassandra/db/Row.java
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/db/Row.java?rev=884333&r1=884332&r2=884333&view=diff
==============================================================================
--- incubator/cassandra/trunk/src/java/org/apache/cassandra/db/Row.java (original)
+++ incubator/cassandra/trunk/src/java/org/apache/cassandra/db/Row.java Wed Nov 25 23:16:58 2009
@@ -44,6 +44,8 @@
public Row(String key, ColumnFamily cf)
{
+ assert key != null;
+ // cf may be null, indicating no data
this.key = key;
this.cf = cf;
}
@@ -53,9 +55,19 @@
* what that means is that if there are any differences between the 2 rows then
* this function will make the current row take the latest changes.
*/
- public void resolve(Row other)
+ public Row resolve(Row other)
{
+ if (cf == null)
+ return other;
cf.resolve(other.cf);
+ return this;
+ }
+
+ public ColumnFamily diff (Row other)
+ {
+ if (cf == null)
+ return other.cf;
+ return cf.diff(other.cf);
}
public byte[] digest()
@@ -69,7 +81,8 @@
{
throw new AssertionError(e);
}
- cf.updateDigest(digest);
+ if (cf != null)
+ cf.updateDigest(digest);
return digest.digest();
}
Modified: incubator/cassandra/trunk/src/java/org/apache/cassandra/service/ConsistencyManager.java
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/service/ConsistencyManager.java?rev=884333&r1=884332&r2=884333&view=diff
==============================================================================
--- incubator/cassandra/trunk/src/java/org/apache/cassandra/service/ConsistencyManager.java (original)
+++ incubator/cassandra/trunk/src/java/org/apache/cassandra/service/ConsistencyManager.java Wed Nov 25 23:16:58 2009
@@ -100,7 +100,7 @@
DataRepairHandler(int responseCount, IResponseResolver<Row> readResponseResolver)
{
readResponseResolver_ = readResponseResolver;
- majority_ = (responseCount >> 1) + 1;
+ majority_ = (responseCount / 2) + 1;
}
public void response(Message message)
Modified: incubator/cassandra/trunk/src/java/org/apache/cassandra/service/ReadResponseResolver.java
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/service/ReadResponseResolver.java?rev=884333&r1=884332&r2=884333&view=diff
==============================================================================
--- incubator/cassandra/trunk/src/java/org/apache/cassandra/service/ReadResponseResolver.java (original)
+++ incubator/cassandra/trunk/src/java/org/apache/cassandra/service/ReadResponseResolver.java Wed Nov 25 23:16:58 2009
@@ -63,8 +63,7 @@
public Row resolve(List<Message> responses) throws DigestMismatchException, IOException
{
long startTime = System.currentTimeMillis();
- Row resolved = null;
- List<Row> rowList = new ArrayList<Row>();
+ List<Row> rows = new ArrayList<Row>();
List<InetAddress> endPoints = new ArrayList<InetAddress>();
String key = null;
byte[] digest = new byte[0];
@@ -89,7 +88,7 @@
}
else
{
- rowList.add(result.row());
+ rows.add(result.row());
endPoints.add(response.getFrom());
key = result.row().key;
}
@@ -98,7 +97,7 @@
// If there is a mismatch then throw an exception so that read repair can happen.
if (isDigestQuery)
{
- for (Row row : rowList)
+ for (Row row : rows)
{
if (!Arrays.equals(row.digest(), digest))
{
@@ -109,25 +108,14 @@
}
}
- /* If the rowList is empty then we had some exception above. */
- if (rowList.size() == 0)
- {
- return resolved;
- }
-
- /* Now calculate the resolved row */
- resolved = new Row(key, rowList.get(0).cf);
- for (Row other : rowList.subList(1, rowList.size()))
- {
- resolved.resolve(other);
- }
+ Row resolved = resolveSuperset(rows);
// At this point we have the return row;
// Now we need to calculate the difference so that we can schedule read repairs
- for (int i = 0; i < rowList.size(); i++)
+ for (int i = 0; i < rows.size(); i++)
{
// since retRow is the resolved row it can be used as the super set
- ColumnFamily diffCf = rowList.get(i).cf.diff(resolved.cf);
+ ColumnFamily diffCf = rows.get(i).diff(resolved);
if (diffCf == null) // no repair needs to happen
continue;
// create the row mutation message based on the diff and schedule a read repair
@@ -141,6 +129,27 @@
return resolved;
}
+ static Row resolveSuperset(List<Row> rows)
+ {
+ assert rows.size() > 0;
+ Row resolved = null;
+ for (Row row : rows)
+ {
+ if (row.cf != null)
+ {
+ resolved = new Row(row.key, row.cf.cloneMe());
+ break;
+ }
+ }
+ if (resolved == null)
+ return rows.get(0);
+ for (Row row : rows)
+ {
+ resolved = resolved.resolve(row);
+ }
+ return resolved;
+ }
+
public boolean isDataPresent(List<Message> responses)
{
boolean isDataPresent = false;
Modified: incubator/cassandra/trunk/src/java/org/apache/cassandra/service/StorageProxy.java
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/service/StorageProxy.java?rev=884333&r1=884332&r2=884333&view=diff
==============================================================================
--- incubator/cassandra/trunk/src/java/org/apache/cassandra/service/StorageProxy.java (original)
+++ incubator/cassandra/trunk/src/java/org/apache/cassandra/service/StorageProxy.java Wed Nov 25 23:16:58 2009
@@ -608,8 +608,10 @@
Map<String, Collection<IColumn>> results = new TreeMap<String, Collection<IColumn>>();
for (Row row : allRows)
{
- // for now, assume only one cf per row, since that is all we can specify in the Command.
- results.put(row.key, row.cf.getSortedColumns());
+ if (row.cf == null)
+ results.put(row.key, Collections.<IColumn>emptyList());
+ else
+ results.put(row.key, row.cf.getSortedColumns());
}
rangeStats.add(System.currentTimeMillis() - startTime);
return results;
Modified: incubator/cassandra/trunk/test/system/test_server.py
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/test/system/test_server.py?rev=884333&r1=884332&r2=884333&view=diff
==============================================================================
--- incubator/cassandra/trunk/test/system/test_server.py (original)
+++ incubator/cassandra/trunk/test/system/test_server.py Wed Nov 25 23:16:58 2009
@@ -542,6 +542,15 @@
client.insert('Keyspace1', key, ColumnPath('Standard1', column=cname), 'v-' + cname, 0, ConsistencyLevel.ONE)
cp = ColumnParent('Standard1')
+ # test empty slice
+ result = client.get_range_slice("Keyspace1", cp, SlicePredicate(column_names=['col1', 'col3']), 'key6', '', 1, ConsistencyLevel.ONE)
+ assert len(result) == 0
+
+ # test empty columns
+ result = client.get_range_slice("Keyspace1", cp, SlicePredicate(column_names=['a']), 'key2', '', 1, ConsistencyLevel.ONE)
+ assert len(result) == 1
+ assert len(result[0].columns) == 0
+
# test column_names predicate
result = client.get_range_slice("Keyspace1", cp, SlicePredicate(column_names=['col1', 'col3']), 'key2', 'key4', 5, ConsistencyLevel.ONE)
assert len(result) == 3
Added: incubator/cassandra/trunk/test/unit/org/apache/cassandra/service/ReadResponseResolverTest.java
URL: http://svn.apache.org/viewvc/incubator/cassandra/trunk/test/unit/org/apache/cassandra/service/ReadResponseResolverTest.java?rev=884333&view=auto
==============================================================================
--- incubator/cassandra/trunk/test/unit/org/apache/cassandra/service/ReadResponseResolverTest.java (added)
+++ incubator/cassandra/trunk/test/unit/org/apache/cassandra/service/ReadResponseResolverTest.java Wed Nov 25 23:16:58 2009
@@ -0,0 +1,90 @@
+package org.apache.cassandra.service;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+
+import org.apache.cassandra.db.ColumnFamily;
+import org.apache.cassandra.db.Row;
+import static org.apache.cassandra.db.TableTest.assertColumns;
+import static org.apache.cassandra.Util.column;
+import static junit.framework.Assert.assertNull;
+
+public class ReadResponseResolverTest
+{
+ @Test
+ public void testResolveSupersetNewer()
+ {
+ ColumnFamily cf1 = ColumnFamily.create("Keyspace1", "Standard1");
+ cf1.addColumn(column("c1", "v1", 0));
+ Row row1 = new Row("key1", cf1);
+
+ ColumnFamily cf2 = ColumnFamily.create("Keyspace1", "Standard1");
+ cf2.addColumn(column("c1", "v2", 1));
+ Row row2 = new Row("key1", cf2);
+
+ Row resolved = ReadResponseResolver.resolveSuperset(Arrays.asList(row1, row2));
+ assertColumns(resolved.cf, "c1");
+ assertColumns(row1.diff(resolved), "c1");
+ assertNull(row2.diff(resolved));
+ }
+
+ @Test
+ public void testResolveSupersetDisjoint()
+ {
+ ColumnFamily cf1 = ColumnFamily.create("Keyspace1", "Standard1");
+ cf1.addColumn(column("c1", "v1", 0));
+ Row row1 = new Row("key1", cf1);
+
+ ColumnFamily cf2 = ColumnFamily.create("Keyspace1", "Standard1");
+ cf2.addColumn(column("c2", "v2", 1));
+ Row row2 = new Row("key1", cf2);
+
+ Row resolved = ReadResponseResolver.resolveSuperset(Arrays.asList(row1, row2));
+ assertColumns(resolved.cf, "c1", "c2");
+ assertColumns(row1.diff(resolved), "c2");
+ assertColumns(row2.diff(resolved), "c1");
+ }
+
+ @Test
+ public void testResolveSupersetNullOne()
+ {
+ Row row1 = new Row("key1", null);
+
+ ColumnFamily cf2 = ColumnFamily.create("Keyspace1", "Standard1");
+ cf2.addColumn(column("c2", "v2", 1));
+ Row row2 = new Row("key1", cf2);
+
+ Row resolved = ReadResponseResolver.resolveSuperset(Arrays.asList(row1, row2));
+ assertColumns(resolved.cf, "c2");
+ assertColumns(row1.diff(resolved), "c2");
+ assertNull(row2.diff(resolved));
+ }
+
+ @Test
+ public void testResolveSupersetNullTwo()
+ {
+ ColumnFamily cf1 = ColumnFamily.create("Keyspace1", "Standard1");
+ cf1.addColumn(column("c1", "v1", 0));
+ Row row1 = new Row("key1", cf1);
+
+ Row row2 = new Row("key1", null);
+
+ Row resolved = ReadResponseResolver.resolveSuperset(Arrays.asList(row1, row2));
+ assertColumns(resolved.cf, "c1");
+ assertNull(row1.diff(resolved));
+ assertColumns(row2.diff(resolved), "c1");
+ }
+
+ @Test
+ public void testResolveSupersetNullBoth()
+ {
+ Row row1 = new Row("key1", null);
+ Row row2 = new Row("key1", null);
+
+ Row resolved = ReadResponseResolver.resolveSuperset(Arrays.asList(row1, row2));
+ assertNull(resolved.cf);
+ assertNull(row1.diff(resolved));
+ assertNull(row2.diff(resolved));
+ }
+}