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));
+    }
+}