db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From oyste...@apache.org
Subject svn commit: r630806 - in /db/derby/code/trunk/java: engine/org/apache/derby/database/ engine/org/apache/derby/iapi/services/replication/slave/ engine/org/apache/derby/impl/db/ engine/org/apache/derby/impl/jdbc/ engine/org/apache/derby/impl/services/rep...
Date Mon, 25 Feb 2008 10:58:29 GMT
Author: oysteing
Date: Mon Feb 25 02:58:21 2008
New Revision: 630806

URL: http://svn.apache.org/viewvc?rev=630806&view=rev
Log:
DERBY-3205:Add failover functionality to the slave-side.
Contributed by Jorgen Loland

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/database/Database.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/replication/slave/SlaveFactory.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/db/BasicDatabase.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/db/SlaveDatabase.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/services/replication/slave/SlaveController.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/log/LogToFile.java
    db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
    db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/database/Database.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/database/Database.java?rev=630806&r1=630805&r2=630806&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/database/Database.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/database/Database.java Mon Feb 25 02:58:21
2008
@@ -114,6 +114,12 @@
     public void failover(String dbname) throws SQLException;
 
     /**
+     * Returns true if this database is in replication slave mode,
+     * false otherwise
+     */
+    public boolean isInSlaveMode();
+
+    /**
      * Stop the replication slave role for the given database.
      * 
      * @exception SQLException Thrown on error

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/replication/slave/SlaveFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/replication/slave/SlaveFactory.java?rev=630806&r1=630805&r2=630806&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/replication/slave/SlaveFactory.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/replication/slave/SlaveFactory.java
Mon Feb 25 02:58:21 2008
@@ -116,7 +116,8 @@
     /**
      * <p>
      * Used to turn this slave instance of the database into a normal
-     * instance that clients can connect to. This is typically done in
+     * instance that clients can connect to, assuming that the
+     * connection with the master is down. This is typically done in
      * cases where a fatal error has happened on the master instance
      * of the database, or when the master database is unreachable due
      * to network problems.
@@ -135,10 +136,12 @@
      * replication strategy used by the MasterFactory.
      * </p>
      *
+     * @exception StandardException Thrown if slave is connected with
+     * master
      * @see org.apache.derby.iapi.services.replication.master.MasterFactory
      * @see org.apache.derby.impl.services.replication.master.MasterController#flushedTo
      */
-    public void failover();
+    public void failover() throws StandardException;
     
     /**
      * Check whether or not slave replication mode has been

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/db/BasicDatabase.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/db/BasicDatabase.java?rev=630806&r1=630805&r2=630806&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/db/BasicDatabase.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/db/BasicDatabase.java Mon Feb 25
02:58:21 2008
@@ -405,6 +405,10 @@
         throw PublicAPI.wrapStandardException(se);
     }
     
+    public boolean isInSlaveMode() {
+        return false;
+    }
+    
     /**
      * @see org.apache.derby.database.Database#failover(String dbname).
      */

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/db/SlaveDatabase.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/db/SlaveDatabase.java?rev=630806&r1=630805&r2=630806&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/db/SlaveDatabase.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/db/SlaveDatabase.java Mon Feb 25
02:58:21 2008
@@ -170,6 +170,10 @@
     ////////////////////////
     // Database interface //
     ////////////////////////
+    public boolean isInSlaveMode() {
+        return inReplicationSlaveMode;
+    }
+    
     public LanguageConnectionContext setupConnection(ContextManager cm, 
                                                      String user, 
                                                      String drdaID, 
@@ -249,6 +253,33 @@
         slaveFac = null;
     }
 
+    public void failover(String dbname) throws SQLException {
+        try {
+            if (inReplicationSlaveMode) {
+                slaveFac.failover();
+                // SlaveFactory#failover will make the 
+                // SlaveDatabaseBootThread complete booting of the store 
+                // modules, and inReplicationSlaveMode will then be set to 
+                // false (see SlaveDatabaseBootThread#run). 
+                // Wait until store is completely booted before returning from 
+                // this method
+                while (inReplicationSlaveMode) {
+                    try {
+                        Thread.sleep(500);
+                    } catch (InterruptedException ie) {
+                    // do nothing
+                    }
+                }
+            } else {
+                // If failover is performed on a master that has been a slave 
+                // earlier
+                super.failover(dbname);
+            }
+        } catch (StandardException se) {
+            throw PublicAPI.wrapStandardException(se);
+        }
+    }
+    
     /////////////////
     // Inner Class //
     /////////////////

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java?rev=630806&r1=630805&r2=630806&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java Mon Feb
25 02:58:21 2008
@@ -235,6 +235,25 @@
 											 isHardUpgradeBoot(info));
 			boolean isStartSlaveBoot = isStartReplicationSlaveBoot(info);
 
+            boolean isFailoverMasterBoot = false;
+            boolean isFailoverSlaveBoot = false;
+            if (isReplicationFailover(info)) {
+                // Check that the database has been booted - otherwise throw 
+                // exception.
+                checkDatabaseBooted(database, 
+                                    Attribute.REPLICATION_FAILOVER, 
+                                    tr.getDBName());
+                // The failover command is the same for master and slave 
+                // databases. If the db is not in slave replication mode, we
+                // assume that it is in master mode. If not in any replication 
+                // mode, the connection attempt will fail with an exception
+                if (database.isInSlaveMode()) {
+                    isFailoverSlaveBoot = true;
+                } else {
+                    isFailoverMasterBoot = true;
+                }
+            }
+
 			// Save original properties if we modified them for
 			// two phase encryption or upgrade boot.
 			Properties savedInfo = null;
@@ -275,6 +294,12 @@
             } else if (isInternalShutdownSlaveDatabase(info)) {
                 internalStopReplicationSlave(database, info);
                 return;
+            } else if (isFailoverSlaveBoot) {
+                // failover on slave must be done before tr.startTransaction
+                // because startTrans will try to establish a connection to 
+                // the database through Database.setupConnection, which throws
+                // an exception in slave mode.
+                handleFailoverSlave(database);
             }
 
 			if (database != null)
@@ -348,8 +373,11 @@
 				// Stopping replication master can be done
 				// simultaneously with a database shutdown operation
 				handleStopReplicationMaster(tr, info);
-			} else if (isReplicationFailover(info)) {
-				handleFailover(tr, info);
+			} else if (isFailoverMasterBoot) {
+				// failover on master must be done after tr.startTransaction
+				// because that will establish a connection with the database,
+				// which in turn is used to check authentication/authorization
+				handleFailoverMaster(tr);
 			}
 
 			if (isTwoPhaseEncryptionBoot ||
@@ -482,6 +510,28 @@
 		}
 	}
 
+    /**
+     * Check that a database has already been booted. Throws an exception 
+     * otherwise
+     *
+     * @param database The database that should have been booted
+     * @param operation The operation that requires that the database has 
+     * already been booted, used in the exception message if not booted
+     * @param dbname The name of the database that should have been booted, 
+     * used in the exception message if not booted
+     * @throws java.sql.SQLException thrown if database is not booted
+     */
+    private void checkDatabaseBooted(Database database,
+                                     String operation, 
+                                     String dbname) throws SQLException {
+        if (database == null) {
+            // Do not clear the TransactionResource context. It will
+            // be restored as part of the finally clause of the constructor.
+            this.setInactive();
+            throw newSQLException(SQLState.REPLICATION_DB_NOT_BOOTED, 
+                                  operation, dbname);
+        }
+    }
 
 	/**
 	  Examine the attributes set provided for illegal boot
@@ -736,12 +786,10 @@
         // Cannot get the database by using getTR().getDatabase()
         // because getTR().setDatabase() has not been called in the
         // constructor at this point.
-        if (database == null) {
-            // Do not clear the TransactionResource context. It will
-            // be restored as part of the finally clause of the constructor.
-            this.setInactive();
-            throw newSQLException(SQLState.REPLICATION_NOT_IN_SLAVE_MODE);
-        }
+
+        // Check that the database has been booted - otherwise throw exception
+        checkDatabaseBooted(database, Attribute.REPLICATION_STOP_SLAVE, 
+                            tr.getDBName());
 
         database.stopReplicationSlave();
         // throw an exception to the client
@@ -777,12 +825,11 @@
         // Cannot get the database by using getTR().getDatabase()
         // because getTR().setDatabase() has not been called in the
         // constructor at this point.
-        if (database == null) {
-            // Do not clear the TransactionResource context. It will
-            // be restored as part of the finally clause of the constructor.
-            this.setInactive();
-            throw newSQLException(SQLState.REPLICATION_NOT_IN_SLAVE_MODE);
-        }
+
+        // Check that the database has been booted - otherwise throw exception.
+        checkDatabaseBooted(database, 
+                            Attribute.REPLICATION_INTERNAL_SHUTDOWN_SLAVE,
+                            tr.getDBName());
 
         // We should only get here if the connection is made from
         // inside SlaveDatabase. To verify, we ask SlaveDatabase
@@ -805,7 +852,6 @@
      * 
      * @param tr an instance of TransactionResourceImpl Links the connection 
      *           to the database.
-     * @param p The Attribute set.
      * @throws java.sql.SQLException 1) Thrown upon a authorization failure 
      *                           2) If the failover succeeds, an exception is
      *                              thrown to indicate that the master database
@@ -813,8 +859,7 @@
      *                           3) If a failure occurs during network 
      *                              communication with slave.
      */
-    private void handleFailover(TransactionResourceImpl tr,
-                                             Properties p)
+    private void handleFailoverMaster(TransactionResourceImpl tr)
         throws SQLException {
 
         // If authorization is turned on, we need to check if this
@@ -835,6 +880,34 @@
         tr.getDatabase().failover(tr.getDBName());
     }
 
+    /**
+     * Used to perform failover on a database in slave replication
+     * mode. Performs failover, provided that the database is in
+     * replication slave mode and has lost connection with the master
+     * database. If the connection with the master is up, the call to
+     * this method will be refused by raising an exception. The reason
+     * for refusing the failover command if the slave is connected
+     * with the master is that we cannot authenticate the user on the
+     * slave side (because the slave database has not been fully
+     * booted) whereas authentication is not a problem on the master
+     * side. If not refused, this method will apply all operations
+     * received from the master and complete the booting of the
+     * database so that it can be connected to.
+     * 
+     * @param database The database the failover operation will be
+     * performed on
+     * @exception SQLException Thrown on error, if not in replication 
+     * slave mode or if the network connection with the master is not down
+     */
+    private void handleFailoverSlave(Database database)
+        throws SQLException {
+
+        // We cannot check authentication and authorization for
+        // databases in slave mode since the AuthenticationService has
+        // not been booted for the database
+
+        database.failover(getTR().getDBName());
+    }
 	/**
 	 * Remove any encryption or upgarde properties from the given properties
 	 *

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/services/replication/slave/SlaveController.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/services/replication/slave/SlaveController.java?rev=630806&r1=630805&r2=630806&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/services/replication/slave/SlaveController.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/services/replication/slave/SlaveController.java
Mon Feb 25 02:58:21 2008
@@ -35,7 +35,6 @@
 import org.apache.derby.iapi.store.raw.log.LogFactory;
 import org.apache.derby.impl.store.raw.log.LogToFile;
 
-import org.apache.derby.impl.db.SlaveDatabase;
 import org.apache.derby.impl.services.replication.ReplicationLogger;
 import org.apache.derby.impl.services.replication.net.ReplicationMessage;
 import org.apache.derby.impl.services.replication.net.ReplicationMessageReceive;
@@ -75,7 +74,6 @@
     private RawStoreFactory rawStoreFactory;
     private LogToFile logToFile;
     private ReplicationMessageReceive receiver;
-    private SlaveDatabase slaveDb;
 
     private volatile boolean connectedToMaster = false;
     private String slavehost;
@@ -281,37 +279,27 @@
             throws StandardException {
         if (!forcedStop && connectedToMaster){
             throw StandardException.newException(
-                    SQLState.SLAVE_STOP_DENIED_WHILE_CONNECTED);
+                    SQLState.SLAVE_OPERATION_DENIED_WHILE_CONNECTED);
         }
         stopSlave();
     }
 
+    public void failover() throws StandardException {
+        if (connectedToMaster){
+            throw StandardException.newException(
+                SQLState.SLAVE_OPERATION_DENIED_WHILE_CONNECTED);
+        }
+        doFailover();
+    } 
+
     /**
-     * <p>
-     * Used to turn this slave instance of the database into a normal
-     * instance that clients can connect to. This is typically done in
-     * cases where a fatal error has happened on the master instance
-     * of the database, or when the master database is unreachable due
-     * to network problems.
-     * </p>
-     * <p>
-     * By calling failover, this slave instance of the database will
-     * be recovered so that all committed operations that have been
-     * received from the master are reflected here. On the other hand,
-     * operations from transactions where the commit log record has
-     * not been received from the master will not be reflected.
-     * </p>
-     * <p>
-     * Note that even though an operation has been executed (and even
-     * committed) on the master, it is not neccessarily reflected in
-     * the slave instance of the database. This depends on the
-     * replication strategy used by the MasterFactory.
-     * </p>
-     *
-     * @see org.apache.derby.iapi.services.replication.master.MasterFactory
-     * @see org.apache.derby.impl.services.replication.master.MasterController#flushedTo
+     * Performs failover on this database. May be called because a
+     * failover command has been received from the master, or because
+     * a client has requested a failover after the network connection
+     * with the master has been lost.
+     * @see SlaveFactory#failover
      */
-    public void failover() {
+    private void doFailover() {
         inReplicationSlaveMode = false;
         logToFile.failoverSlave();
         Monitor.logTextMessage
@@ -490,7 +478,7 @@
                         handleLogChunk(logChunk);
                         break;
                     case ReplicationMessage.TYPE_FAILOVER:
-                        failover();
+                        doFailover();
                         ReplicationMessage ack = new ReplicationMessage
                             (ReplicationMessage.TYPE_ACK, "failover succeeded");
                         receiver.sendMessage(ack);

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/log/LogToFile.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/log/LogToFile.java?rev=630806&r1=630805&r2=630806&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/log/LogToFile.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/log/LogToFile.java Mon
Feb 25 02:58:21 2008
@@ -5228,7 +5228,17 @@
      * and boot the database.
      */
     public void failoverSlave() {
+        if (!stopped) {
+            try {
+                flushAll();
+            } catch (StandardException ex) {
+            // do nothing
+            }
+        }
         inReplicationSlaveMode = false;
+        synchronized (slaveRecoveryMonitor) {
+            slaveRecoveryMonitor.notify();
+        }
     }
 
 	/**

Modified: db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml?rev=630806&r1=630805&r2=630806&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml Mon Feb 25 02:58:21
2008
@@ -4798,6 +4798,13 @@
             </msg>
             
             <msg>
+                <name>XRE11</name>
+                <text>Could not perform operation '{0}' because the database '{1}'
has not been booted.</text>
+                <arg>command</arg>
+                <arg>dbname</arg>
+            </msg>
+
+            <msg>
                 <name>XRE12</name>
                 <text>Replication network protocol error for database '{0}'. Expected
message type '{1}', but received type '{2}'.</text>
                 <arg>dbname</arg>

Modified: db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java?rev=630806&r1=630805&r2=630806&view=diff
==============================================================================
--- db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
(original)
+++ db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
Mon Feb 25 02:58:21 2008
@@ -1775,11 +1775,12 @@
     String REPLICATION_SLAVE_STARTED_OK                            = "XRE08";
     String CANNOT_START_SLAVE_ALREADY_BOOTED                       = "XRE09";
     String REPLICATION_CONFLICTING_ATTRIBUTES                      = "XRE10";
+    String REPLICATION_DB_NOT_BOOTED                               = "XRE11";
     String REPLICATION_UNEXPECTED_MESSAGEID                        = "XRE12";
     String REPLICATION_FAILOVER_SUCCESSFUL                         = "XRE20.D";
     String REPLICATION_FAILOVER_UNSUCCESSFUL                       = "XRE21";
     String REPLICATION_NOT_IN_SLAVE_MODE                           = "XRE40";
-    String SLAVE_STOP_DENIED_WHILE_CONNECTED                       = "XRE41";
+    String SLAVE_OPERATION_DENIED_WHILE_CONNECTED                  = "XRE41";
     String REPLICATION_SLAVE_SHUTDOWN_OK                           = "XRE42";
     String REPLICATION_STOPSLAVE_NOT_INITIATED                     = "XRE43";
 }



Mime
View raw message