Author: gdusbabek
Date: Mon Jun  7 13:38:18 2010
New Revision: 952219

URL: http://svn.apache.org/viewvc?rev=952219&view=rev
Log:
detect partitioner changes and fail fast. patch by gdusbabek, reviewed by 
jbellis.

Modified:
    
cassandra/branches/cassandra-0.6/src/java/org/apache/cassandra/avro/CassandraDaemon.java
    
cassandra/branches/cassandra-0.6/src/java/org/apache/cassandra/db/SystemTable.java
    
cassandra/branches/cassandra-0.6/src/java/org/apache/cassandra/thrift/CassandraDaemon.java
    
cassandra/branches/cassandra-0.6/test/unit/org/apache/cassandra/service/MoveTest.java

Modified: 
cassandra/branches/cassandra-0.6/src/java/org/apache/cassandra/avro/CassandraDaemon.java
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.6/src/java/org/apache/cassandra/avro/CassandraDaemon.java?rev=952219&r1=952218&r2=952219&view=diff
==============================================================================
--- 
cassandra/branches/cassandra-0.6/src/java/org/apache/cassandra/avro/CassandraDaemon.java
 (original)
+++ 
cassandra/branches/cassandra-0.6/src/java/org/apache/cassandra/avro/CassandraDaemon.java
 Mon Jun  7 13:38:18 2010
@@ -27,6 +27,7 @@ import org.apache.avro.ipc.SocketServer;
 import org.apache.avro.specific.SpecificResponder;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.db.CompactionManager;
+import org.apache.cassandra.db.SystemTable;
 import org.apache.cassandra.db.Table;
 import org.apache.cassandra.db.commitlog.CommitLog;
 import org.apache.cassandra.service.StorageService;
@@ -71,6 +72,17 @@ public class CassandraDaemon {
                 }
             }
         });
+        
+        // check the system table for mismatched partitioner.
+        try
+        {
+            SystemTable.checkHealth();
+        }
+        catch (IOException e)
+        {
+            logger.error("Fatal exception during initialization", e);
+            System.exit(100);
+        }
 
         // initialize keyspaces
         for (String table : DatabaseDescriptor.getTables())

Modified: 
cassandra/branches/cassandra-0.6/src/java/org/apache/cassandra/db/SystemTable.java
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.6/src/java/org/apache/cassandra/db/SystemTable.java?rev=952219&r1=952218&r2=952219&view=diff
==============================================================================
--- 
cassandra/branches/cassandra-0.6/src/java/org/apache/cassandra/db/SystemTable.java
 (original)
+++ 
cassandra/branches/cassandra-0.6/src/java/org/apache/cassandra/db/SystemTable.java
 Mon Jun  7 13:38:18 2010
@@ -18,6 +18,8 @@
 
 package org.apache.cassandra.db;
 
+import java.io.File;
+import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.io.IOError;
@@ -37,6 +39,7 @@ import org.apache.cassandra.config.Datab
 import java.net.InetAddress;
 import java.util.SortedSet;
 import java.util.TreeSet;
+import java.util.concurrent.ExecutionException;
 
 public class SystemTable
 {
@@ -48,6 +51,7 @@ public class SystemTable
     private static final byte[] TOKEN = utf8("Token");
     private static final byte[] GENERATION = utf8("Generation");
     private static final byte[] CLUSTERNAME = utf8("ClusterName");
+    private static final byte[] PARTITIONER = utf8("Partioner");
     private static StorageMetadata metadata;
 
     private static byte[] utf8(String str)
@@ -104,6 +108,67 @@ public class SystemTable
         metadata.setToken(token);
     }
     
+
+    /**
+     * One of three things will happen if you try to read the system table:
+     * 1. files are present and you can read them: great
+     * 2. no files are there: great (new node is assumed)
+     * 3. files are present but you can't read them: bad (suspect that the 
partitioner was changed).
+     * @throws IOException
+     */
+    public static void checkHealth() throws IOException
+    {
+        Table table = null;
+        try
+        {
+            table = Table.open(Table.SYSTEM_TABLE);
+        }
+        catch (AssertionError err)
+        {
+            // this happens when a user switches from OPP to RP.
+            IOException ex = new IOException("Could not read system table. Did 
you change partitioners?");
+            ex.initCause(err);
+            throw ex;
+        }
+        
+        SortedSet<byte[]> cols = new TreeSet<byte[]>(new BytesType());
+        cols.add(TOKEN);
+        cols.add(GENERATION);
+        cols.add(PARTITIONER);
+        QueryFilter filter = new NamesQueryFilter(LOCATION_KEY, new 
QueryPath(STATUS_CF), cols);
+        ColumnFamily cf = 
table.getColumnFamilyStore(STATUS_CF).getColumnFamily(filter);
+        
+        if (cf == null)
+        {
+            // this is either a brand new node (there will be no files), or 
the partitioner was changed from RP to OPP.
+            for (String path : 
DatabaseDescriptor.getAllDataFileLocationsForTable("system"))
+            {
+                File[] dbContents = new File(path).listFiles(new 
FilenameFilter()
+                {
+                    public boolean accept(File dir, String name)
+                    {
+                        return name.endsWith(".db");
+                    }
+                }); 
+                if (dbContents.length > 0)
+                    throw new IOException("Found system table files, but they 
couldn't be loaded. Did you change the partitioner?");
+            }   
+            // no system files. data is either in the commit log or this is a 
new node.
+            return;
+        }
+        
+        
+        // token and generation should *always* be there. If either are 
missing, we can assume that the partitioner has
+        // been switched.
+        if (cf.getColumnCount() > 0 && (cf.getColumn(GENERATION) == null || 
cf.getColumn(TOKEN) == null))
+            throw new IOException("Couldn't read system generation or token. 
Did you change the partitioner?");
+        IColumn partitionerCol = cf.getColumn(PARTITIONER);
+        if (partitionerCol != null && 
!DatabaseDescriptor.getPartitioner().getClass().getName().equals(new 
String(partitionerCol.value(), "UTF-8")))
+            throw new IOException("Detected partitioner mismatch! Did you 
change the partitioner?");
+        if (partitionerCol == null)
+            logger.info("Did not see a partitioner in system storage.");
+    }    
+    
     /*
      * This method reads the system table and retrieves the metadata
      * associated with this storage instance. Currently we store the
@@ -125,6 +190,7 @@ public class SystemTable
         columns.add(CLUSTERNAME);
         QueryFilter filter = new NamesQueryFilter(LOCATION_KEY, new 
QueryPath(STATUS_CF), columns);
         ColumnFamily cf = 
table.getColumnFamilyStore(STATUS_CF).getColumnFamily(filter);
+        String partitioner = 
DatabaseDescriptor.getPartitioner().getClass().getName();
 
         IPartitioner p = StorageService.getPartitioner();
         if (cf == null)
@@ -149,8 +215,21 @@ public class SystemTable
             cf.addColumn(new Column(TOKEN, 
p.getTokenFactory().toByteArray(token)));
             cf.addColumn(new Column(GENERATION, 
FBUtilities.toByteArray(generation)));
             cf.addColumn(new Column(CLUSTERNAME, 
DatabaseDescriptor.getClusterName().getBytes()));
+            cf.addColumn(new Column(PARTITIONER, 
partitioner.getBytes("UTF-8")));
             rm.add(cf);
             rm.apply();
+            try
+            {
+                
table.getColumnFamilyStore(SystemTable.STATUS_CF).forceBlockingFlush();
+            }
+            catch (ExecutionException e)
+            {
+                throw new RuntimeException(e);
+            }
+            catch (InterruptedException e)
+            {
+                throw new RuntimeException(e);
+            }
             metadata = new StorageMetadata(token, generation, 
DatabaseDescriptor.getClusterName().getBytes());
             return metadata;
         }
@@ -168,6 +247,7 @@ public class SystemTable
         int gen = Math.max(FBUtilities.byteArrayToInt(generation.value()) + 1, 
(int) (System.currentTimeMillis() / 1000));
 
         IColumn cluster = cf.getColumn(CLUSTERNAME);
+        IColumn partitionerColumn = cf.getColumn(PARTITIONER);
 
         RowMutation rm = new RowMutation(Table.SYSTEM_TABLE, LOCATION_KEY);
         cf = ColumnFamily.create(Table.SYSTEM_TABLE, SystemTable.STATUS_CF);
@@ -186,8 +266,29 @@ public class SystemTable
             cname = DatabaseDescriptor.getClusterName().getBytes();
             logger.info("Saved ClusterName not found. Using " + 
DatabaseDescriptor.getClusterName());
         }
+                
+        if (partitionerColumn == null)
+        {
+            Column c = new Column(PARTITIONER, partitioner.getBytes("UTF-8"));
+            cf.addColumn(c);
+            logger.info("Saved partitioner not found. Using " + partitioner);
+        }
+        
         rm.add(cf);
         rm.apply();
+        try
+        {
+            
table.getColumnFamilyStore(SystemTable.STATUS_CF).forceBlockingFlush();
+        }
+        catch (ExecutionException e)
+        {
+            throw new RuntimeException(e);
+        }
+        catch (InterruptedException e)
+        {
+            throw new RuntimeException(e);
+        }
+        
         metadata = new StorageMetadata(token, gen, cname);
         return metadata;
     }

Modified: 
cassandra/branches/cassandra-0.6/src/java/org/apache/cassandra/thrift/CassandraDaemon.java
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.6/src/java/org/apache/cassandra/thrift/CassandraDaemon.java?rev=952219&r1=952218&r2=952219&view=diff
==============================================================================
--- 
cassandra/branches/cassandra-0.6/src/java/org/apache/cassandra/thrift/CassandraDaemon.java
 (original)
+++ 
cassandra/branches/cassandra-0.6/src/java/org/apache/cassandra/thrift/CassandraDaemon.java
 Mon Jun  7 13:38:18 2010
@@ -27,6 +27,7 @@ import java.util.concurrent.SynchronousQ
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.cassandra.db.SystemTable;
 import org.apache.log4j.Logger;
 import org.apache.log4j.PropertyConfigurator;
 
@@ -86,7 +87,18 @@ public class CassandraDaemon
                 }
             }
         });
-
+        
+        // check the system table for mismatched partitioner.
+        try
+        {
+            SystemTable.checkHealth();
+        }
+        catch (IOException e)
+        {
+            logger.error("Fatal exception during initialization", e);
+            System.exit(100);
+        }
+        
         // initialize keyspaces
         for (String table : DatabaseDescriptor.getTables())
         {

Modified: 
cassandra/branches/cassandra-0.6/test/unit/org/apache/cassandra/service/MoveTest.java
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.6/test/unit/org/apache/cassandra/service/MoveTest.java?rev=952219&r1=952218&r2=952219&view=diff
==============================================================================
--- 
cassandra/branches/cassandra-0.6/test/unit/org/apache/cassandra/service/MoveTest.java
 (original)
+++ 
cassandra/branches/cassandra-0.6/test/unit/org/apache/cassandra/service/MoveTest.java
 Mon Jun  7 13:38:18 2010
@@ -26,6 +26,7 @@ import java.net.UnknownHostException;
 
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Multimap;
+import org.apache.cassandra.CleanupHelper;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.commons.lang.StringUtils;
 import org.junit.Test;
@@ -43,7 +44,7 @@ import org.apache.cassandra.locator.Rack
 import org.apache.cassandra.locator.TokenMetadata;
 import org.apache.cassandra.gms.ApplicationState;
 
-public class MoveTest
+public class MoveTest extends CleanupHelper
 {
     // handy way of creating a mapping of strategies to use in StorageService.
     private static Map<String, AbstractReplicationStrategy> 
createReplacements(AbstractReplicationStrategy strat)


Reply via email to