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; /** *

@@ -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 ServerSocket 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 @@

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 @@

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.

@@ -2849,25 +2849,35 @@ throws IOException, StandardException { - // 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. + // + // + // 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 @@ Unexpected error during replication network communication initiation. + + XRE04 + Could not establish a connection to the master of the replicated database '{0}'. + dbname + + + + XRE05 + 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. + dbname + masterinstant + slaveinstant + + + @@ -7518,6 +7533,45 @@ + + + + Undocumented Replication Messages from MessageId.java + + R001 + -------- BEGIN REPLICATION ERROR MESSAGE --------- + + + + R002 + --------- END REPLICATION ERROR MESSAGE ---------- + + + + R003 + Replication slave role started for database '{0}'. + dbname + + + + R004 + Replication slave role was stopped for database '{0}'. + dbname + + + + R005 + Replication slave got a fatal error for database '{0}'. Replication will be stopped. + dbname + + + + R006 + Lost connection with the replication master of database '{0}'. + dbname + + + 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"; }