You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by oy...@apache.org on 2008/02/25 11:58:29 UTC
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...
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";
}