Repository: cassandra
Updated Branches:
  refs/heads/trunk 47863bc32 -> fa592a46b


Fix merging schemas with re-dropped keyspaces

patch by Aleksey Yeschenko; reviewed by Pavel Yaskevich for
CASSANDRA-7256


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/0393c306
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/0393c306
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/0393c306

Branch: refs/heads/trunk
Commit: 0393c306331e6b46182b0693ff192be13d20698e
Parents: b7adf98
Author: Aleksey Yeschenko <alek...@apache.org>
Authored: Fri Oct 10 17:32:58 2014 +0300
Committer: Aleksey Yeschenko <alek...@apache.org>
Committed: Fri Oct 10 17:32:58 2014 +0300

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../org/apache/cassandra/db/DefsTables.java     | 186 ++++++++-----------
 2 files changed, 78 insertions(+), 109 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/0393c306/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index bc12402..b633e48 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 2.0.11:
+ * Fix merging schemas with re-dropped keyspaces (CASSANDRA-7256)
  * Fix counters in supercolumns during live upgrades from 1.2 (CASSANDRA-7188)
  * Notify DT subscribers when a column family is truncated (CASSANDRA-8088)
  * Add sanity check of $JAVA on startup (CASSANDRA-7676)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0393c306/src/java/org/apache/cassandra/db/DefsTables.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/DefsTables.java 
b/src/java/org/apache/cassandra/db/DefsTables.java
index bc2b36d..35eecc0 100644
--- a/src/java/org/apache/cassandra/db/DefsTables.java
+++ b/src/java/org/apache/cassandra/db/DefsTables.java
@@ -21,6 +21,7 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.*;
 
+import com.google.common.base.Function;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.MapDifference;
 import com.google.common.collect.Maps;
@@ -189,135 +190,102 @@ public class DefsTables
         Schema.instance.updateVersionAndAnnounce();
     }
 
-    private static Set<String> mergeKeyspaces(Map<DecoratedKey, ColumnFamily> 
old, Map<DecoratedKey, ColumnFamily> updated)
+    private static Set<String> mergeKeyspaces(Map<DecoratedKey, ColumnFamily> 
before, Map<DecoratedKey, ColumnFamily> after)
     {
-        // calculate the difference between old and new states (note that 
entriesOnlyLeft() will be always empty)
-        MapDifference<DecoratedKey, ColumnFamily> diff = Maps.difference(old, 
updated);
-
-        /**
-         * At first step we check if any new keyspaces were added.
-         */
-        for (Map.Entry<DecoratedKey, ColumnFamily> entry : 
diff.entriesOnlyOnRight().entrySet())
-        {
-            ColumnFamily ksAttrs = entry.getValue();
-
-            // we don't care about nested ColumnFamilies here because those 
are going to be processed separately
-            if (!(ksAttrs.getColumnCount() == 0))
-                addKeyspace(KSMetaData.fromSchema(new Row(entry.getKey(), 
entry.getValue()), Collections.<CFMetaData>emptyList()));
-        }
-
-        /**
-         * At second step we check if there were any keyspaces re-created, in 
this context
-         * re-created means that they were previously deleted but still exist 
in the low-level schema as empty keys
+        List<Row> created = new ArrayList<>();
+        List<String> altered = new ArrayList<>();
+        Set<String> dropped = new HashSet<>();
+
+        /*
+         * - we don't care about entriesOnlyOnLeft() or entriesInCommon(), 
because only the changes are of interest to us
+         * - of all entriesOnlyOnRight(), we only care about ones that have 
live columns; it's possible to have a ColumnFamily
+         *   there that only has the top-level deletion, if:
+         *      a) a pushed DROP KEYSPACE change for a keyspace hadn't ever 
made it to this node in the first place
+         *      b) a pulled dropped keyspace that got dropped before it could 
find a way to this node
+         * - of entriesDiffering(), we don't care about the scenario where 
both pre and post-values have zero live columns:
+         *   that means that a keyspace had been recreated and dropped, and 
the recreated keyspace had never found a way
+         *   to this node
          */
+        MapDifference<DecoratedKey, ColumnFamily> diff = 
Maps.difference(before, after);
 
-        Map<DecoratedKey, MapDifference.ValueDifference<ColumnFamily>> 
modifiedEntries = diff.entriesDiffering();
-
-        // instead of looping over all modified entries and skipping processed 
keys all the time
-        // we would rather store "left to process" items and iterate over them 
removing already met keys
-        List<DecoratedKey> leftToProcess = new 
ArrayList<DecoratedKey>(modifiedEntries.size());
-
-        for (Map.Entry<DecoratedKey, 
MapDifference.ValueDifference<ColumnFamily>> entry : modifiedEntries.entrySet())
-        {
-            ColumnFamily prevValue = entry.getValue().leftValue();
-            ColumnFamily newValue = entry.getValue().rightValue();
-
-            if (prevValue.getColumnCount() == 0)
-            {
-                addKeyspace(KSMetaData.fromSchema(new Row(entry.getKey(), 
newValue), Collections.<CFMetaData>emptyList()));
-                continue;
-            }
-
-            leftToProcess.add(entry.getKey());
-        }
-
-        if (leftToProcess.size() == 0)
-            return Collections.emptySet();
-
-        /**
-         * At final step we updating modified keyspaces and saving keyspaces 
drop them later
-         */
-
-        Set<String> keyspacesToDrop = new HashSet<String>();
+        for (Map.Entry<DecoratedKey, ColumnFamily> entry : 
diff.entriesOnlyOnRight().entrySet())
+            if (entry.getValue().getColumnCount() > 0)
+                created.add(new Row(entry.getKey(), entry.getValue()));
 
-        for (DecoratedKey key : leftToProcess)
+        for (Map.Entry<DecoratedKey, 
MapDifference.ValueDifference<ColumnFamily>> entry : 
diff.entriesDiffering().entrySet())
         {
-            MapDifference.ValueDifference<ColumnFamily> valueDiff = 
modifiedEntries.get(key);
+            String keyspaceName = 
AsciiType.instance.compose(entry.getKey().key);
 
-            ColumnFamily newState = valueDiff.rightValue();
+            ColumnFamily pre  = entry.getValue().leftValue();
+            ColumnFamily post = entry.getValue().rightValue();
 
-            if (newState.getColumnCount() == 0)
-                keyspacesToDrop.add(AsciiType.instance.getString(key.key));
-            else
-                updateKeyspace(KSMetaData.fromSchema(new Row(key, newState), 
Collections.<CFMetaData>emptyList()));
+            if (pre.getColumnCount() > 0 && post.getColumnCount() > 0)
+                altered.add(keyspaceName);
+            else if (pre.getColumnCount() > 0)
+                dropped.add(keyspaceName);
+            else if (post.getColumnCount() > 0) // a (re)created keyspace
+                created.add(new Row(entry.getKey(), post));
         }
 
-        return keyspacesToDrop;
+        for (Row row : created)
+            addKeyspace(KSMetaData.fromSchema(row, 
Collections.<CFMetaData>emptyList()));
+        for (String name : altered)
+            updateKeyspace(name);
+        return dropped;
     }
 
-    private static void mergeColumnFamilies(Map<DecoratedKey, ColumnFamily> 
old, Map<DecoratedKey, ColumnFamily> updated)
+    // see the comments for mergeKeyspaces()
+    private static void mergeColumnFamilies(Map<DecoratedKey, ColumnFamily> 
before, Map<DecoratedKey, ColumnFamily> after)
     {
-        // calculate the difference between old and new states (note that 
entriesOnlyLeft() will be always empty)
-        MapDifference<DecoratedKey, ColumnFamily> diff = Maps.difference(old, 
updated);
-
-        // check if any new Keyspaces with ColumnFamilies were added.
-        for (Map.Entry<DecoratedKey, ColumnFamily> entry : 
diff.entriesOnlyOnRight().entrySet())
-        {
-            ColumnFamily cfAttrs = entry.getValue();
+        List<CFMetaData> created = new ArrayList<>();
+        List<CFMetaData> altered = new ArrayList<>();
+        List<CFMetaData> dropped = new ArrayList<>();
 
-            if (!(cfAttrs.getColumnCount() == 0))
-            {
-               Map<String, CFMetaData> cfDefs = 
KSMetaData.deserializeColumnFamilies(new Row(entry.getKey(), cfAttrs));
-
-                for (CFMetaData cfDef : cfDefs.values())
-                    addColumnFamily(cfDef);
-            }
-        }
+        MapDifference<DecoratedKey, ColumnFamily> diff = 
Maps.difference(before, after);
 
-        // deal with modified ColumnFamilies (remember that all of the 
keyspace nested ColumnFamilies are put to the single row)
-        Map<DecoratedKey, MapDifference.ValueDifference<ColumnFamily>> 
modifiedEntries = diff.entriesDiffering();
+        for (Map.Entry<DecoratedKey, ColumnFamily> entry : 
diff.entriesOnlyOnRight().entrySet())
+            if (entry.getValue().getColumnCount() > 0)
+                created.addAll(KSMetaData.deserializeColumnFamilies(new 
Row(entry.getKey(), entry.getValue())).values());
 
-        for (DecoratedKey keyspace : modifiedEntries.keySet())
+        for (Map.Entry<DecoratedKey, 
MapDifference.ValueDifference<ColumnFamily>> entry : 
diff.entriesDiffering().entrySet())
         {
-            MapDifference.ValueDifference<ColumnFamily> valueDiff = 
modifiedEntries.get(keyspace);
-
-            ColumnFamily prevValue = valueDiff.leftValue(); // state before 
external modification
-            ColumnFamily newValue = valueDiff.rightValue(); // updated state
+            String keyspaceName = 
AsciiType.instance.compose(entry.getKey().key);
 
-            Row newRow = new Row(keyspace, newValue);
+            ColumnFamily pre  = entry.getValue().leftValue();
+            ColumnFamily post = entry.getValue().rightValue();
 
-            if (prevValue.getColumnCount() == 0) // whole keyspace was deleted 
and now it's re-created
+            if (pre.getColumnCount() > 0 && post.getColumnCount() > 0)
             {
-                for (CFMetaData cfm : 
KSMetaData.deserializeColumnFamilies(newRow).values())
-                    addColumnFamily(cfm);
+                MapDifference<String, CFMetaData> delta =
+                    
Maps.difference(Schema.instance.getKSMetaData(keyspaceName).cfMetaData(),
+                                    KSMetaData.deserializeColumnFamilies(new 
Row(entry.getKey(), post)));
+
+                dropped.addAll(delta.entriesOnlyOnLeft().values());
+                created.addAll(delta.entriesOnlyOnRight().values());
+                Iterables.addAll(altered, 
Iterables.transform(delta.entriesDiffering().values(), new 
Function<MapDifference.ValueDifference<CFMetaData>, CFMetaData>()
+                {
+                    public CFMetaData 
apply(MapDifference.ValueDifference<CFMetaData> pair)
+                    {
+                        return pair.rightValue();
+                    }
+                }));
             }
-            else if (newValue.getColumnCount() == 0) // whole keyspace is 
deleted
+            else if (pre.getColumnCount() > 0)
             {
-                for (CFMetaData cfm : KSMetaData.deserializeColumnFamilies(new 
Row(keyspace, prevValue)).values())
-                    dropColumnFamily(cfm.ksName, cfm.cfName);
+                
dropped.addAll(Schema.instance.getKSMetaData(keyspaceName).cfMetaData().values());
             }
-            else // has modifications in the nested ColumnFamilies, need to 
perform nested diff to determine what was really changed
+            else if (post.getColumnCount() > 0)
             {
-                String ksName = AsciiType.instance.getString(keyspace.key);
-
-                Map<String, CFMetaData> oldCfDefs = new HashMap<String, 
CFMetaData>();
-                for (CFMetaData cfm : 
Schema.instance.getKSMetaData(ksName).cfMetaData().values())
-                    oldCfDefs.put(cfm.cfName, cfm);
-
-                Map<String, CFMetaData> newCfDefs = 
KSMetaData.deserializeColumnFamilies(newRow);
-
-                MapDifference<String, CFMetaData> cfDefDiff = 
Maps.difference(oldCfDefs, newCfDefs);
-
-                for (CFMetaData cfDef : 
cfDefDiff.entriesOnlyOnRight().values())
-                    addColumnFamily(cfDef);
-
-                for (CFMetaData cfDef : cfDefDiff.entriesOnlyOnLeft().values())
-                    dropColumnFamily(cfDef.ksName, cfDef.cfName);
-
-                for (MapDifference.ValueDifference<CFMetaData> cfDef : 
cfDefDiff.entriesDiffering().values())
-                    updateColumnFamily(cfDef.rightValue());
+                created.addAll(KSMetaData.deserializeColumnFamilies(new 
Row(entry.getKey(), post)).values());
             }
         }
+
+        for (CFMetaData cfm : created)
+            addColumnFamily(cfm);
+        for (CFMetaData cfm : altered)
+            updateColumnFamily(cfm.ksName, cfm.cfName);
+        for (CFMetaData cfm : dropped)
+            dropColumnFamily(cfm.ksName, cfm.cfName);
     }
 
     private static void addKeyspace(KSMetaData ksm)
@@ -355,9 +323,9 @@ public class DefsTables
         }
     }
 
-    private static void updateKeyspace(KSMetaData newState)
+    private static void updateKeyspace(String ksName)
     {
-        KSMetaData oldKsm = Schema.instance.getKSMetaData(newState.name);
+        KSMetaData oldKsm = Schema.instance.getKSMetaData(ksName);
         assert oldKsm != null;
         KSMetaData newKsm = KSMetaData.cloneWith(oldKsm.reloadAttributes(), 
oldKsm.cfMetaData().values());
 
@@ -365,14 +333,14 @@ public class DefsTables
 
         if (!StorageService.instance.isClientMode())
         {
-            Keyspace.open(newState.name).createReplicationStrategy(newKsm);
+            Keyspace.open(ksName).createReplicationStrategy(newKsm);
             MigrationManager.instance.notifyUpdateKeyspace(newKsm);
         }
     }
 
-    private static void updateColumnFamily(CFMetaData newState)
+    private static void updateColumnFamily(String ksName, String cfName)
     {
-        CFMetaData cfm = Schema.instance.getCFMetaData(newState.ksName, 
newState.cfName);
+        CFMetaData cfm = Schema.instance.getCFMetaData(ksName, cfName);
         assert cfm != null;
         cfm.reload();
 

Reply via email to