Author: oysteing
Date: Fri Nov 16 18:54:30 2007
New Revision: 595900
URL: http://svn.apache.org/viewvc?rev=595900&view=rev
Log: (empty)
Added:
db/derby/code/trunk/java/engine/org/apache/derby/impl/services/replication/ReplicationLogger.java
Modified:
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/services/replication/net/ReplicationMessageReceive.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/MessageId.java
db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
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=595900&r1=595899&r2=595900&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
Fri Nov 16 18:54:30 2007
@@ -27,7 +27,6 @@
import org.apache.derby.iapi.store.raw.RawStoreFactory;
import org.apache.derby.iapi.store.raw.log.LogFactory;
-import org.apache.derby.iapi.store.raw.data.DataFactory;
/**
* <p>
@@ -50,10 +49,18 @@
/* Strings used as keys in the Properties objects*/
+ /** Property key to specify which host to listen to */
+ public static final String SLAVE_HOST =
+ Property.PROPERTY_RUNTIME_PREFIX + "replication.slave.slavehost";
+
/** Property key to specify which port to listen to */
public static final String SLAVE_PORT =
Property.PROPERTY_RUNTIME_PREFIX + "replication.slave.slaveport";
+ /** Property key to specify the name of the database */
+ public static final String SLAVE_DB =
+ Property.PROPERTY_RUNTIME_PREFIX + "replication.slave.dbname";
+
/** Property key to specify replication mode */
public static final String REPLICATION_MODE =
Property.PROPERTY_RUNTIME_PREFIX + "replication.slave.mode";
@@ -78,8 +85,12 @@
*
* @param rawStore The RawStoreFactory for the database
* @param logFac The LogFactory ensuring recoverability for this database
+ *
+ * @exception StandardException Thrown if the slave could not be
+ * started.
*/
- public void startSlave(RawStoreFactory rawStore, LogFactory logFac);
+ public void startSlave(RawStoreFactory rawStore, LogFactory logFac)
+ throws StandardException;
/**
* Will perform all work that is needed to stop replication
Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/services/replication/ReplicationLogger.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/services/replication/ReplicationLogger.java?rev=595900&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/services/replication/ReplicationLogger.java
(added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/services/replication/ReplicationLogger.java
Fri Nov 16 18:54:30 2007
@@ -0,0 +1,71 @@
+/*
+
+ Derby - Class
+ org.apache.derby.impl.services.replication.ReplicationLogger
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to you under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ */
+
+package org.apache.derby.impl.services.replication;
+
+import org.apache.derby.iapi.reference.MessageId;
+import org.apache.derby.iapi.services.context.ErrorStringBuilder;
+import org.apache.derby.iapi.services.monitor.Monitor;
+
+public abstract class ReplicationLogger {
+
+ /** Whether or not to print log messages to derby.log. Defaults to
+ * true, but can be set to false with derby property
+ * "derby.replication.logerrormessages=true"
+ */
+ // TODO: make this configurable through the aforementioned
+ // property
+ private static final boolean LOG_REPLICATION_MESSAGES = true;
+
+
+ /**
+ * Print error message and the stack trace of the throwable to the
+ * log (usually derby.log) provided that LOG_REPLICATION_MESSAGES
+ * is true. If LOG_REPLICATION_MESSAGES is false, nothing is
+ * logged.
+ *
+ * @param msgId The error message id
+ * @param t Error trace starts from this error
+ * @param dbname The name of the replicated database
+ */
+ protected void logError(String msgId, Throwable t, String dbname) {
+
+ if (LOG_REPLICATION_MESSAGES) {
+
+ Monitor.logTextMessage(MessageId.REPLICATION_ERROR_BEGIN);
+
+ if (msgId != null) {
+ Monitor.logTextMessage(msgId, dbname);
+ }
+
+ if (t != null) {
+ ErrorStringBuilder esb =
+ new ErrorStringBuilder(Monitor.getStream().getHeader());
+ esb.stackTrace(t);
+ Monitor.logMessage(esb.get().toString());
+ esb.reset();
+ }
+ Monitor.logTextMessage(MessageId.REPLICATION_ERROR_END);
+ }
+ }
+
+}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/services/replication/net/ReplicationMessageReceive.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/services/replication/net/ReplicationMessageReceive.java?rev=595900&r1=595899&r2=595900&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/services/replication/net/ReplicationMessageReceive.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/services/replication/net/ReplicationMessageReceive.java
Fri Nov 16 18:54:30 2007
@@ -60,12 +60,17 @@
* @param portNumber an integer that contains the port number of the
* slave to replicate to.
*
- * @throws UnknownHostException If an exception occurs while trying to
- * resolve the host name.
+ * @throws StandardException If an exception occurs while trying to
+ * resolve the host name.
*/
public ReplicationMessageReceive(String hostName, int portNumber)
- throws UnknownHostException {
- slaveAddress = new SlaveAddress(hostName, portNumber);
+ throws StandardException {
+ try {
+ slaveAddress = new SlaveAddress(hostName, portNumber);
+ } catch (UnknownHostException uhe) {
+ throw StandardException.newException
+ (SQLState.REPLICATION_CONNECTION_EXCEPTION, uhe);
+ }
}
/**
@@ -73,6 +78,12 @@
* for connections from the master and verify compatibility
* with the database version of the master.
*
+ * @param timeout The amount of time, in milliseconds, this method
+ * will wait for a connection to be established. If no connection
+ * has been established before the timeout, a
+ * PrivilegedExceptionAction is raised with cause
+ * java.net.SocketTimeoutException
+ *
* @throws PrivilegedActionException if an exception occurs while trying
* to open a connection.
*
@@ -84,7 +95,7 @@
* @throws StandardException if an incompatible database version is found.
*
*/
- public void initConnection() throws
+ public void initConnection(int timeout) throws
PrivilegedActionException,
IOException,
StandardException,
@@ -93,6 +104,7 @@
//Contains the <code>ServerSocket</code> used to listen for
//connections from the replication master.
final ServerSocket serverSocket = createServerSocket();
+ serverSocket.setSoTimeout(timeout);
//Start listening on the socket and accepting the connection
Socket client =
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=595900&r1=595899&r2=595900&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
Fri Nov 16 18:54:30 2007
@@ -23,15 +23,23 @@
package org.apache.derby.impl.services.replication.slave;
import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.reference.MessageId;
import org.apache.derby.iapi.reference.SQLState;
import org.apache.derby.iapi.services.monitor.ModuleControl;
import org.apache.derby.iapi.services.monitor.ModuleSupportable;
+import org.apache.derby.iapi.services.monitor.Monitor;
import org.apache.derby.iapi.store.raw.RawStoreFactory;
import org.apache.derby.iapi.store.raw.log.LogFactory;
+import org.apache.derby.impl.store.raw.log.LogToFile;
+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;
import org.apache.derby.iapi.services.replication.slave.SlaveFactory;
+import java.io.EOFException;
+import java.net.SocketTimeoutException;
import java.util.Properties;
/**
@@ -48,15 +56,39 @@
*
* @see SlaveFactory
*/
-public class SlaveController implements SlaveFactory, ModuleControl,
- ModuleSupportable {
+public class SlaveController extends ReplicationLogger
+ implements SlaveFactory, ModuleControl, ModuleSupportable {
+
+
+ // How long to wait for a connection to be established with the
+ // master before timing out. Note that this is done so that we can
+ // detect if replication slave mode has been stopped. If
+ // replication mode has not been stopped, a new attempt is made to
+ // set up the connection.
+ // TODO: make this configurable through a property
+ private static final int DEFAULT_SOCKET_TIMEOUT = 1000; // 1 second
private RawStoreFactory rawStoreFactory;
- private LogFactory logFactory;
- // waiting for code to go into trunk:
- // private NetworkReceive connection;
+ private LogToFile logToFile;
+ private ReplicationMessageReceive receiver;
+ private String slavehost;
private int slaveport;
+ private String dbname; // The name of the replicated database
+
+ // Whether or not replication slave mode is still on. Will be set
+ // to false when slave replication is shut down. The value of this
+ // variable is checked after every timeout when trying to set up a
+ // connection to the master, and by the thread that applies log
+ // chunks received from the master.
+ private volatile boolean inReplicationSlaveMode = true;
+
+ // Used to parse chunks of log records received from the master.
+ private ReplicationLogScan logScan;
+
+ // Thread that listens for log chunk messages from the master, and
+ // applies these to the local log
+ private SlaveLogReceiverThread logReceiverThread;
/**
* Empty constructor required by Monitor.bootServiceModule
@@ -71,8 +103,6 @@
* Used by Monitor.bootServiceModule to start the service. It will
* set up basic variables
*
- * Not implemented yet
- *
* @param create Currently ignored
* @param properties Properties used to start the service in the
* correct mode
@@ -82,15 +112,14 @@
public void boot(boolean create, Properties properties)
throws StandardException {
+ slavehost = properties.getProperty(SlaveFactory.SLAVE_HOST);
+
String port = properties.getProperty(SlaveFactory.SLAVE_PORT);
if (port != null) {
slaveport = new Integer(port).intValue();
}
- // Added when Network Service has been committed to trunk
- // connection = new NetworkReceive();
-
- System.out.println("SlaveController booted");
+ dbname = properties.getProperty(SlaveFactory.SLAVE_DB);
}
/**
@@ -137,28 +166,54 @@
/**
* Start slave replication. This method establishes a network
* connection with the associated replication master and starts a
- * daemon that applies operations received from the master (in the
+ * thread that applies operations received from the master (in the
* form of log records) to the local slave database.
*
- * Not implemented yet
- *
* @param rawStore The RawStoreFactory for the database
- * @param logFac The LogFactory ensuring recoverability for this database
+ * @param logFac The LogFactory ensuring recoverability for this
+ * database
+ *
+ * @exception StandardException Thrown if the slave could not be
+ * started.
*/
- public void startSlave(RawStoreFactory rawStore, LogFactory logFac) {
- // Added when Network Service has been committed to trunk:
- // connection.connect(); // sets up a network connection to the slave
+ public void startSlave(RawStoreFactory rawStore, LogFactory logFac)
+ throws StandardException {
+
+ // Retry to setup a connection with the master until a
+ // connection has been established or until we are no longer
+ // in replication slave mode
+ receiver = new ReplicationMessageReceive(slavehost, slaveport);
+ while (!setupConnection()) {
+ if (!inReplicationSlaveMode) {
+ // If we get here, another thread has called
+ // stopSlave() while we waited for a connection with
+ // the master. The thread shutting the slave down will
+ // clean up anything we did during setupConnection, so
+ // simply return.
+ return;
+ }
+ }
+
+ // Setup the log scan used to parse chunks of log received
+ // from the master
+ logScan = new ReplicationLogScan();
rawStoreFactory = rawStore;
- logFactory = logFac;
- // Add code that initializes replication by setting up a
- // network connection with the master, receiving the database
- // from the master, make a DaemonService for applying log
- // records etc. Repliation should be up and running when this
- // method returns.
+ try {
+ logToFile = (LogToFile)logFac;
+ } catch (ClassCastException cce) {
+ // Since there are only two implementing classes of
+ // LogFactory, the class type has to be ReadOnly if it is
+ // not LogToFile.
+ throw StandardException.newException(
+ SQLState.CANNOT_REPLICATE_READONLY_DATABASE);
+ }
+
+ logToFile.initializeReplicationSlaveRole();
+ startLogReceiverThread();
- System.out.println("SlaveController started");
+ Monitor.logTextMessage(MessageId.REPLICATION_SLAVE_STARTED, dbname);
}
/**
@@ -167,7 +222,10 @@
* Not implemented yet
*/
public void stopSlave() {
- System.out.println("SlaveController stopped");
+ inReplicationSlaveMode = false;
+
+ // todo: shutdown slave
+ Monitor.logTextMessage(MessageId.REPLICATION_SLAVE_STOPPED, dbname);
}
/**
@@ -200,7 +258,7 @@
// this database. The database can be connected to after this.
// // complete recovery of the database
- // logFactory.setReplicationMode(false);
+ // logToFile.setReplicationMode(false);
// Added when Network Service has been committed to trunk:
// connection.shutdown();
@@ -208,5 +266,216 @@
System.out.println("SlaveController failover");
}
+ ////////////////////////////////////////////////////////////
+ // Private Methods //
+ ////////////////////////////////////////////////////////////
+
+ /**
+ * Establish a connection with the replication master. Listens for
+ * a connection on the slavehost/port for DEFAULT_SOCKET_TIMEOUT
+ * milliseconds.
+ *
+ * @return true if a connection has been set up with the master,
+ * false if the connection attempt timed out.
+ *
+ * @exception StandardException if an unexpected exception occured
+ * that prevented a connection with the master.
+ */
+ private boolean setupConnection() throws StandardException {
+
+ try {
+ // timeout to check if still in replication slave mode
+ receiver.initConnection(DEFAULT_SOCKET_TIMEOUT);
+ return true; // will not reach this if timeout
+ } catch (StandardException se) {
+ throw se;
+ } catch (Exception e) {
+ // SocketTimeoutException is wrapped in
+ // PrivilegedActionException.
+ Throwable cause = e.getCause();
+ if (cause instanceof SocketTimeoutException) {
+ // Timeout!
+ return false;
+ } else {
+ throw StandardException.newException
+ (SQLState.REPLICATION_CONNECTION_EXCEPTION, e, dbname);
+ }
+ }
+ }
+
+ /**
+ * Write the reason for the lost connection to the log (derby.log)
+ * and reconnect with the master. Once the network is up and
+ * running, a new LogReceiverThread is started. The method returns
+ * without doing anything if inReplicationSlaveMode=false, which
+ * means that stopSlave() has been called by another thread.
+ *
+ * @param eofe The reason the connection to the master was lost
+ */
+
+ private void handleDisconnect(EOFException eofe) {
+ if (!inReplicationSlaveMode) {
+ return;
+ }
+
+ logError(MessageId.REPLICATION_SLAVE_LOST_CONN, eofe, dbname);
+
+ try {
+ while (!setupConnection()) {
+ if (!inReplicationSlaveMode) {
+ // stopSlave may have been called, turning
+ // replication slave mode off. Simply return if
+ // that is the case. The thread that called
+ // stopSlave will clean up everything.
+ return;
+ }
+ }
+
+ startLogReceiverThread();
+ } catch (StandardException se) {
+ handleFatalException(se);
+ }
+ }
+
+ /**
+ * Starts the LogReceiverThread that will listen for chunks of log
+ * records from the master and apply the log records to the local
+ * log file.
+ */
+ private void startLogReceiverThread() {
+ logReceiverThread = new SlaveLogReceiverThread();
+ logReceiverThread.start();
+ }
+
+ /**
+ * Handles fatal errors for slave replication functionality. These
+ * are errors that requires us to stop replication. Calling this
+ * method has the following effects:
+ *
+ * 1) Debug messages are written to the log file (usually
+ * derby.log) if ReplicationLogger#LOG_REPLICATION_MESSAGES is
+ * true.
+ *
+ * 2) If the network connection is up, the master is notified of
+ * the problem.
+ *
+ * 3) All slave replication functionality is stopped, and the
+ * database is then shut down without being booted.
+ *
+ * The method will return without doing anything if
+ * inReplicationSlaveMode=false, meaning that stopSlave has been
+ * called.
+ *
+ * @param e The fatal exception that is the reason for calling
+ * this method
+ */
+ private void handleFatalException(Exception e) {
+ // If inReplicationSlaveMode is false, the stopSlave method in
+ // this controller has already been called. If so, we ignore
+ // this fatal error.
+ if (!inReplicationSlaveMode) {
+ return;
+ }
+
+ logError(MessageId.REPLICATION_FATAL_ERROR, e, dbname);
+
+ // todo: notify master of the problem
+ // todo: rawStoreFactory.stopReplicationSlave();
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Inner Class - Thread used to apply chunks of log received from master //
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Thread that listens for incoming messages from the master and
+ * applies chunks of log records to the local log files.
+ */
+ private class SlaveLogReceiverThread extends Thread {
+ public void run() {
+ // Debug only - println will be removed
+ System.out.println("Started log receiver thread");
+ try {
+ ReplicationMessage message;
+ while (inReplicationSlaveMode) {
+ message = receiver.readMessage();
+
+ switch (message.getType()){
+ case ReplicationMessage.TYPE_LOG:
+ byte[] logChunk = (byte[])message.getMessage();
+ handleLogChunk(logChunk);
+ break;
+ default:
+ // debug; will be removed
+ System.out.println("Not handling non-log messages yet "
+ +"- got a type "+message.getType());
+ break;
+ }
+ }
+
+ } catch (EOFException eofe) {
+ // Network connection with master has been lost.
+ handleDisconnect(eofe);
+ } catch (StandardException se) {
+ handleFatalException(se);
+ } catch (Exception e) {
+ // Exceptions not caused by disconnect are unexpected,
+ // and therefore fatal
+ StandardException se =
+ StandardException.newException
+ (SQLState.REPLICATION_UNEXPECTED_EXCEPTION, e);
+ handleFatalException(se);
+ }
+ }
+
+ /**
+ * Parses a chunk of log received from the master, and applies
+ * the individual log records to the local log file.
+ *
+ * @param logChunk A chunk of log records received from the
+ * master
+ * @exception StandardException If the chunk of log records
+ * could not be parsed or the local log file is out of synch
+ * with the master log file.
+ */
+ private void handleLogChunk(byte[] logChunk)
+ throws StandardException{
+ logScan.init(logChunk);
+
+ while (logScan.next()){
+ if (logScan.isLogFileSwitch()) {
+ logToFile.switchLogFile();
+ } else {
+
+ long localInstant = logToFile.
+ appendLogRecord(logScan.getData(),
+ 0,
+ logScan.getDataLength(),
+ null,
+ 0,
+ 0);
+
+ // If the log instant of the received log does not
+ // match with the local log instant, the log
+ // records are not written to the same physical
+ // location in the log files. This is fatal since
+ // log records are identified by their physical
+ // location in the log files.
+ if (logScan.getInstant() != localInstant) {
+ throw StandardException.newException
+ (SQLState.REPLICATION_LOG_OUT_OF_SYNCH,
+ dbname,
+ new Long(logScan.getInstant()),
+ new Long(localInstant));
+
+ }
+ }
+ }
+ }
+ }
+ ///////////////////////////////////////////////////////////
+ // END Inner Class //
+ ///////////////////////////////////////////////////////////
}
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=595900&r1=595899&r2=595900&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 Fri
Nov 16 18:54:30 2007
@@ -357,9 +357,9 @@
// switchLogFile (both in slave replication mode and after the
// database has been fully booted) when a new log file is
// allocated.
- long logFileNumber = -1; // current log file number
- // other than during boot and recovery time,
- // and by initializeSlaveReplication if in
+ long logFileNumber = -1; // current log file number.
+ // Other than during boot and recovery time,
+ // and during initializeReplicationSlaveRole if in
// slave replication mode,
// logFileNumber is only changed by
// switchLogFile, which is synchronized.
@@ -1904,7 +1904,7 @@
<P>MT - log factory is single threaded thru a log file switch, the log
is frozen for the duration of the switch
*/
- private void switchLogFile() throws StandardException
+ public void switchLogFile() throws StandardException
{
boolean switchedOver = false;
@@ -2838,7 +2838,7 @@
<p> When the database is in slave replication mode only:
Assumes that only recover() will call this method after
- initializeSlaveReplication() has been called, and until slave
+ initializeReplicationSlaveRole() has been called, and until slave
replication has ended. If this changes, the current
implementation will fail.</p>
@@ -2849,25 +2849,35 @@
throws IOException, StandardException
{
- // <SLAVE REPLICATION CODE> When in slave replication mode,
- // the recovery processing will not start until after the
- // first call to switchLogRecord, at which time
- // allowedToReadFileNumber will be set to one less than the
- // current log file number. Before recovery is allowed to
- // start, log scans will be allowed unrestricted access to the
- // log files through this method. This is needed because
- // boot() and initializeSlaveReplication() use this method to
- // find the log end. When the recovery thread is allowed to
- // start (i.e., allowedToReadFileNumber != -1), it will use
- // this method to read log files. Every time it tries to read
- // a new log file, a check is performed to see if the thread
- // is allowed to read it. If not, it has to wait either until
- // this database is no longer in slave replication mode or
- // until it is allowed to read that file. Currently, only
- // recover() uses this method (through openForwardsScan) up to
- // the point where the database has been fully recovered. If
- // this changes (i.e. another thread also needs access to the
- // log files while in slave mode), this code will not work.
+ // <SLAVE REPLICATION CODE>
+ //
+ // When in slave replication mode, the recovery processing
+ // will not start until after the first call to
+ // switchLogRecord, at which time allowedToReadFileNumber will
+ // be set to one less than the current log file number. The
+ // call to switchLogRecord comes as a result of the
+ // SlaveController appending log records received from the
+ // master. This implies that the initialization steps (boot
+ // and initializeReplicationSlaveRole) have completed.
+ //
+ // Before recovery processing is started, log scans will be
+ // allowed unrestricted access to the log files through this
+ // method. This is needed because boot() and
+ // initializeReplicationSlaveRole() use this method to find
+ // the log end. Once the recovery thread is allowed to start
+ // processing (i.e., allowedToReadFileNumber != -1), it will
+ // use this method to read log files. From this point on, this
+ // method will not return until allowedToReadFileNumber =>
+ // filenumber. In other words, while in replication slave
+ // mode, the method is blocking until allowedToReadFileNumber
+ // is high enough to read the requested log file.
+ //
+ // Currently, only recover() uses this method (through
+ // openForwardsScan) up to the point where the database has
+ // been fully recovered. The database cannot fully recover
+ // until it is no longer in slave mode. If this changes (i.e.
+ // another thread also needs access to the log files while in
+ // slave mode), this code will not work.
if (inReplicationSlaveMode && (allowedToReadFileNumber != -1)) {
synchronized (slaveRecoveryMonitor) {
// Recheck inReplicationSlaveMode == true because it
@@ -3604,7 +3614,7 @@
@exception StandardException Log Full.
*/
- protected long appendLogRecord(byte[] data, int offset, int length,
+ public long appendLogRecord(byte[] data, int offset, int length,
byte[] optionalData, int optionalDataOffset, int optionalDataLength)
throws StandardException
{
@@ -5093,13 +5103,14 @@
* logFileNumber may occur if this is changed.
*
* @exception StandardException Standard Derby error policy
- * @exception IOException If a log file cannot be opened
*/
- public void initializeSlaveReplication()
- throws StandardException, IOException{
+ public void initializeReplicationSlaveRole()
+ throws StandardException{
- if (!inReplicationSlaveMode) {
- return;
+ if (SanityManager.DEBUG) {
+ SanityManager.ASSERT(!inReplicationSlaveMode,
+ "This method should only be used when"
+ + " in slave replication mode");
}
/*
@@ -5107,48 +5118,56 @@
* end position in that file
*/
- // Find the log file with the highest file number on disk
- while (getLogFileAtBeginning(logFileNumber+1) != null) {
- logFileNumber++;
- }
-
- // Scan the highest log file to find it's end.
- long startInstant =
- LogCounter.makeLogInstantAsLong(logFileNumber,
- LOG_FILE_HEADER_SIZE);
- long logEndInstant = LOG_FILE_HEADER_SIZE;
-
- StreamLogScan scanOfHighestLogFile =
- (StreamLogScan) openForwardsScan(startInstant, (LogInstant)null);
- ArrayInputStream scanInputStream = new ArrayInputStream();
- while(scanOfHighestLogFile.getNextRecord(scanInputStream, null, 0)
- != null){
- logEndInstant = scanOfHighestLogFile.getLogRecordEnd();
- }
+ try {
+ // Find the log file with the highest file number on disk
+ while (getLogFileAtBeginning(logFileNumber+1) != null) {
+ logFileNumber++;
+ }
- endPosition = LogCounter.getLogFilePosition(logEndInstant);
+ // Scan the highest log file to find it's end.
+ long startInstant =
+ LogCounter.makeLogInstantAsLong(logFileNumber,
+ LOG_FILE_HEADER_SIZE);
+ long logEndInstant = LOG_FILE_HEADER_SIZE;
+
+ StreamLogScan scanOfHighestLogFile =
+ (StreamLogScan) openForwardsScan(startInstant,
+ (LogInstant)null);
+ ArrayInputStream scanInputStream = new ArrayInputStream();
+ while(scanOfHighestLogFile.getNextRecord(scanInputStream, null, 0)
+ != null){
+ logEndInstant = scanOfHighestLogFile.getLogRecordEnd();
+ }
- // endPosition and logFileNumber now point to the end of the
- // highest log file. This is where a new log record should be
- // appended.
+ endPosition = LogCounter.getLogFilePosition(logEndInstant);
- /*
- * Open the highest log file and make sure log records are
- * appended at the end of it
- */
+ // endPosition and logFileNumber now point to the end of the
+ // highest log file. This is where a new log record should be
+ // appended.
+
+ /*
+ * Open the highest log file and make sure log records are
+ * appended at the end of it
+ */
+
+ StorageRandomAccessFile logFile = null;
+ if(isWriteSynced) {
+ logFile = openLogFileInWriteMode(
+ getLogFileName(logFileNumber));
+ } else {
+ logFile = privRandomAccessFile(getLogFileName(logFileNumber),
+ "rw");
+ }
+ logOut = new LogAccessFile(this, logFile, logBufferSize);
- StorageRandomAccessFile logFile = null;
- if(isWriteSynced) {
- logFile = openLogFileInWriteMode(getLogFileName(logFileNumber));
- } else {
- logFile = privRandomAccessFile(getLogFileName(logFileNumber),
- "rw");
+ lastFlush = endPosition;
+ logFile.seek(endPosition); // append log records at the end of
+ // the file
+
+ } catch (IOException ioe) {
+ throw StandardException.newException
+ (SQLState.REPLICATION_UNEXPECTED_EXCEPTION, ioe);
}
- logOut = new LogAccessFile(this, logFile, logBufferSize);
-
- lastFlush = endPosition;
- logFile.seek(endPosition); // append log records at the end of
- // the file
}
/**
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=595900&r1=595899&r2=595900&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 Fri Nov 16 18:54:30
2007
@@ -4738,6 +4738,21 @@
<text>Unexpected error during replication network communication initiation.</text>
</msg>
+ <msg>
+ <name>XRE04</name>
+ <text>Could not establish a connection to the master of the replicated
database '{0}'.</text>
+ <arg>dbname</arg>
+ </msg>
+
+ <msg>
+ <name>XRE05</name>
+ <text>The log received from the master is not in synch with the local
log for replicated database '{0}'. The received log instant is {1}, whereas the local instant
is {2}. This is FATAL for replication - replication will be stopped.</text>
+ <arg>dbname</arg>
+ <arg>masterinstant</arg>
+ <arg>slaveinstant</arg>
+ </msg>
+
+
</family>
<family>
@@ -7518,6 +7533,45 @@
</msg>
</family>
+
+
+ <family>
+ <title>Undocumented Replication Messages from MessageId.java</title>
+ <msg>
+ <name>R001</name>
+ <text>-------- BEGIN REPLICATION ERROR MESSAGE ---------</text>
+ </msg>
+
+ <msg>
+ <name>R002</name>
+ <text>--------- END REPLICATION ERROR MESSAGE ----------</text>
+ </msg>
+
+ <msg>
+ <name>R003</name>
+ <text>Replication slave role started for database '{0}'.</text>
+ <arg>dbname</arg>
+ </msg>
+
+ <msg>
+ <name>R004</name>
+ <text>Replication slave role was stopped for database '{0}'.</text>
+ <arg>dbname</arg>
+ </msg>
+
+ <msg>
+ <name>R005</name>
+ <text>Replication slave got a fatal error for database '{0}'. Replication
will be stopped.</text>
+ <arg>dbname</arg>
+ </msg>
+
+ <msg>
+ <name>R006</name>
+ <text>Lost connection with the replication master of database '{0}'.</text>
+ <arg>dbname</arg>
+ </msg>
+ </family>
+
<family>
Modified: db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/MessageId.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/MessageId.java?rev=595900&r1=595899&r2=595900&view=diff
==============================================================================
--- db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/MessageId.java
(original)
+++ db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/MessageId.java
Fri Nov 16 18:54:30 2007
@@ -170,5 +170,14 @@
*/
String SERVICE_PROPERTIES_DONT_EDIT = "M001"; // Tell user not to edit service.properties
+ /*
+ * Replication
+ */
+ String REPLICATION_ERROR_BEGIN = "R001";
+ String REPLICATION_ERROR_END = "R002";
+ String REPLICATION_SLAVE_STARTED = "R003";
+ String REPLICATION_SLAVE_STOPPED = "R004";
+ String REPLICATION_FATAL_ERROR = "R005";
+ String REPLICATION_SLAVE_LOST_CONN = "R006";
}
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=595900&r1=595899&r2=595900&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
Fri Nov 16 18:54:30 2007
@@ -1761,5 +1761,7 @@
String REPLICATION_LOG_CORRUPTED = "XRE01";
String REPLICATION_MASTER_SLAVE_VERSION_MISMATCH = "XRE02";
String REPLICATION_UNEXPECTED_EXCEPTION = "XRE03";
+ String REPLICATION_CONNECTION_EXCEPTION = "XRE04";
+ String REPLICATION_LOG_OUT_OF_SYNCH = "XRE05";
}
|