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/01/14 11:54:35 UTC
svn commit: r611759 - in /db/derby/code/trunk/java/engine/org/apache/derby:
./ iapi/services/replication/slave/ impl/db/ impl/services/monitor/
impl/store/raw/log/
Author: oysteing
Date: Mon Jan 14 02:54:33 2008
New Revision: 611759
URL: http://svn.apache.org/viewvc?rev=611759&view=rev
Log:
DERBY-3184: Replication: Connection attempts to a database in slave mode must fail (Contributed by Jorgen Lovland)
The attached patch, v2a, adds a new implementation of Database to Derby. The new implementation is called SlaveDatabase and is booted if startSlave=true is specified in the connection url. The following files are modified:
A java/engine/org/apache/derby/impl/db/SlaveDatabase.java
M java/engine/org/apache/derby/modules.properties
The new Database implementation "SlaveDatabase" to Derby.
M java/engine/org/apache/derby/impl/db/BasicDatabase.java
Removed the old "if (inReplicationSlaveMode) throw exception" code. This is now handled in SlaveDatabase.
M java/engine/org/apache/derby/impl/store/raw/log/LogToFile.java
Adds slave replication pre mode
M java/engine/org/apache/derby/iapi/services/replication/slave/SlaveFactory.java
Minor changes
M java/engine/org/apache/derby/impl/services/monitor/UpdateServiceProperties.java
Make StorageFactory volatile to make sure concurrent threads detect that it is set.
Added:
db/derby/code/trunk/java/engine/org/apache/derby/impl/db/SlaveDatabase.java (with props)
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/db/BasicDatabase.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/UpdateServiceProperties.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/modules.properties
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=611759&r1=611758&r2=611759&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 Jan 14 02:54:33 2008
@@ -23,7 +23,6 @@
package org.apache.derby.iapi.services.replication.slave;
import org.apache.derby.iapi.error.StandardException;
-import org.apache.derby.iapi.reference.Property;
import org.apache.derby.iapi.store.raw.RawStoreFactory;
import org.apache.derby.iapi.store.raw.log.LogFactory;
@@ -51,11 +50,11 @@
/** Property key to specify the name of the database */
public static final String SLAVE_DB =
- Property.PROPERTY_RUNTIME_PREFIX + "replication.slave.dbname";
+ "replication.slave.dbname";
/** Property key to specify replication mode */
public static final String REPLICATION_MODE =
- Property.PROPERTY_RUNTIME_PREFIX + "replication.slave.mode";
+ "replication.slave.mode";
/* Strings used as values in the Properties objects */
@@ -64,8 +63,21 @@
* booted in asynchronous replication mode.
*/
public static final String SLAVE_MODE =
- Property.PROPERTY_RUNTIME_PREFIX + "slavemode";
+ "slavemode";
+ /**
+ * Property value used to indicate that the service should be
+ * booted in slave replication pre mode. The reason for having a
+ * slave pre mode is that when slave replication is started, we
+ * need to boot the database twice: Once to check authentication
+ * and authorization, and a second time to put the database in
+ * slave mode. It is imperative that the disk image of log files
+ * remain unmodified by the first boot since the master and slave
+ * log files have to be identical when slave mode starts. Booting
+ * in SLAVE_PRE_MODE ensures that the log files remain unmodified.
+ */
+ public static final String SLAVE_PRE_MODE =
+ "slavepremode";
/* Required methods */
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=611759&r1=611758&r2=611759&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 Jan 14 02:54:33 2008
@@ -111,7 +111,7 @@
public class BasicDatabase implements ModuleControl, ModuleSupportable, PropertySetCallback, Database, JarReader
{
- private boolean active;
+ protected boolean active;
private AuthenticationService authenticationService;
protected AccessFactory af;
protected PropertyFactory pf;
@@ -132,7 +132,6 @@
private DateFormat timeFormat;
private DateFormat timestampFormat;
private UUID myUUID;
- private boolean inReplicationSlaveMode = false;
protected boolean lastToBoot; // is this class last to boot
@@ -141,20 +140,25 @@
*/
public boolean canSupport(Properties startParams) {
- return Monitor.isDesiredCreateType(startParams, getEngineType());
+ boolean supported =
+ Monitor.isDesiredCreateType(startParams, getEngineType());
+
+ if (supported) {
+ String repliMode =
+ startParams.getProperty(SlaveFactory.REPLICATION_MODE);
+ if (repliMode != null &&
+ !repliMode.equals(SlaveFactory.SLAVE_PRE_MODE)) {
+ supported = false;
+ }
+ }
+
+ return supported;
}
public void boot(boolean create, Properties startParams)
throws StandardException
{
- // Database is booted in replication slave mode. Make sure
- // other clients are not able to connect
- String slave = startParams.getProperty(SlaveFactory.REPLICATION_MODE);
- if (slave != null && slave.equals(SlaveFactory.SLAVE_MODE)) {
- inReplicationSlaveMode = true;
- }
-
ModuleFactory monitor = Monitor.getMonitor();
if (create)
{
@@ -291,13 +295,6 @@
public LanguageConnectionContext setupConnection(ContextManager cm, String user, String drdaID, String dbname)
throws StandardException {
- if (inReplicationSlaveMode) {
- // do not allow connections to a database that is
- // currently in replication slave move
- throw StandardException.newException(
- SQLState.CANNOT_CONNECT_TO_DB_IN_SLAVE_MODE, dbname);
- }
-
TransactionController tc = getConnectionTransaction(cm);
cm.setLocaleFinder(this);
@@ -346,7 +343,7 @@
DatabaseContext dc = new DatabaseContextImpl(cm, this);
}
- public final AuthenticationService getAuthenticationService() {
+ public AuthenticationService getAuthenticationService() {
// Expected to find one - Sanity check being done at
// DB boot-up.
Added: 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=611759&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/db/SlaveDatabase.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/db/SlaveDatabase.java Mon Jan 14 02:54:33 2008
@@ -0,0 +1,212 @@
+/*
+
+ Derby - Class org.apache.derby.impl.db.SlaveDatabase
+
+ 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.db;
+
+import org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.jdbc.AuthenticationService;
+import org.apache.derby.iapi.services.context.ContextManager;
+import org.apache.derby.iapi.services.context.ContextService;
+import org.apache.derby.iapi.services.monitor.Monitor;
+import org.apache.derby.iapi.services.replication.slave.SlaveFactory;
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
+import org.apache.derby.impl.services.monitor.UpdateServiceProperties;
+
+import java.util.Properties;
+
+/**
+ * SlaveDatabase is an instance of Database, and is booted instead of
+ * BasicDatabase if this database will have the replication slave
+ * role. SlaveDatabase differs from BasicDatabase in the following
+ * ways:
+ *
+ * 1: When starting a non-replicated database (i.e., BasicDatabase),
+ * only one thread is used to start all modules of the database.
+ * When booted in slave mode, the thread that boots the store
+ * module will be blocked during log recovery. To remedy this,
+ * SlaveDatabase runs the boot method of BasicDatabase in a
+ * separate thread. This ensures that the connection attempt that
+ * started slave replication mode will not hang.
+ *
+ * 2: While the database is in replication slave mode, the
+ * authentication services are not available because these require
+ * that the store module has been booted first. Calling
+ * getAuthenticationService when in slave mode will raise an
+ * exception.
+ *
+ * 3: While the database is in replication slave mode, connections are
+ * not accepted since the database cannot process transaction
+ * requests. Calling setupConnection when in slave mode will raise
+ * an exception.
+ *
+ * 4: If the failover command has been executed for this database, it
+ * is no longer in replication slave mode. When this has
+ * happened, SlaveDatabase works exactly as BasicDatabase.
+ */
+
+public class SlaveDatabase extends BasicDatabase {
+ /** True until SlaveDatabaseBootThread has successfully booted the
+ * database. Does not happen until the failover command has been
+ * executed for this database */
+ private volatile boolean inReplicationSlaveMode;
+
+ /////////////////////////////
+ // ModuleControl interface //
+ /////////////////////////////
+ /**
+ * Determines whether this Database implementation should be used
+ * to boot the database.
+ * @param startParams The properties used to decide if
+ * SlaveDatabase is the correct implementation of Database for the
+ * database to be booted.
+ * @return true if the database is updatable (not read-only) and
+ * replication slave mode is specified in startParams
+ */
+ public boolean canSupport(Properties startParams) {
+
+ boolean supported =
+ Monitor.isDesiredCreateType(startParams, getEngineType());
+ if (supported) {
+ String repliMode =
+ startParams.getProperty(SlaveFactory.REPLICATION_MODE);
+ if (repliMode == null ||
+ !repliMode.equals(SlaveFactory.SLAVE_MODE)) {
+ supported = false;
+ }
+ }
+
+ return supported;
+ }
+
+ public void boot(boolean create, Properties startParams)
+ throws StandardException {
+
+ inReplicationSlaveMode = true;
+
+ // SlaveDatabaseBootThread is an internal class
+ SlaveDatabaseBootThread dbBootThread =
+ new SlaveDatabaseBootThread(create, startParams);
+ new Thread(dbBootThread).start();
+
+ try {
+ // We cannot claim to be booted until the storage factory
+ // has been set in the startParams because
+ // TopService.bootModule (the caller of this method) uses
+ // the storage factory object. The storage factory is set
+ // in RawStore.boot, and we have to wait for this to
+ // happen.
+ UpdateServiceProperties usp =
+ (UpdateServiceProperties) startParams;
+ while (usp.getStorageFactory() == null){
+ Thread.sleep(500);
+ }
+ } catch (Exception e) {
+ //Todo: report exception to derby.log
+ }
+
+ // This module has now been booted (hence active=true) even
+ // though submodules like store and authentication may not
+ // have completed their boot yet. We deal with that by raising
+ // an error on attempts to use these
+ active=true;
+ }
+
+ /////////////////////
+ // Class interface //
+ /////////////////////
+ public SlaveDatabase() {
+ }
+
+ ////////////////////////
+ // Database interface //
+ ////////////////////////
+ public LanguageConnectionContext setupConnection(ContextManager cm,
+ String user,
+ String drdaID,
+ String dbname)
+ throws StandardException {
+
+ if (inReplicationSlaveMode) {
+ // do not allow connections to a database that is
+ // currently in replication slave move
+ throw StandardException.newException(
+ SQLState.CANNOT_CONNECT_TO_DB_IN_SLAVE_MODE, dbname);
+ }
+ return super.setupConnection(cm, user, drdaID, dbname);
+ }
+
+ public AuthenticationService getAuthenticationService() {
+ if (inReplicationSlaveMode) {
+ // Cannot get authentication service for a database that
+ // is currently in replication slave move
+ // Todo: throw exception
+ }
+ return super.getAuthenticationService();
+ }
+
+ /////////////////
+ // Inner Class //
+ /////////////////
+ /**
+ * Thread that boots the slave database. Will be blocked in
+ * LogFactory.recover until database is no longer in slave
+ * replication mode.
+ */
+ private class SlaveDatabaseBootThread implements Runnable {
+
+ private boolean create;
+ private Properties params;
+
+ public SlaveDatabaseBootThread(boolean create, Properties startParams){
+ this.create = create;
+ params = startParams;
+ }
+
+ public void run() {
+
+ // The thread needs a ContextManager since two threads
+ // cannot share a context
+ ContextManager bootThreadCm;
+ try {
+
+ bootThreadCm = ContextService.getFactory().newContextManager();
+ ContextService.getFactory().
+ setCurrentContextManager(bootThreadCm);
+
+ bootBasicDatabase(create, params); // will be blocked
+
+ } catch (StandardException se) {
+ //todo - report exception
+ } finally {
+ inReplicationSlaveMode = false;
+ //todo: tear down context
+ }
+ }
+ }
+
+ private void bootBasicDatabase(boolean create, Properties params)
+ throws StandardException {
+ // This call will be blocked while slave replication mode is
+ // active
+ super.boot(create, params);
+ }
+}
Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/db/SlaveDatabase.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/UpdateServiceProperties.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/UpdateServiceProperties.java?rev=611759&r1=611758&r2=611759&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/UpdateServiceProperties.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/services/monitor/UpdateServiceProperties.java Mon Jan 14 02:54:33 2008
@@ -38,7 +38,7 @@
private PersistentService serviceType;
private String serviceName;
- private WritableStorageFactory storageFactory;
+ private volatile WritableStorageFactory storageFactory;
/*
Fix for bug 3668: Following would allow user to change properties while in the session
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=611759&r1=611758&r2=611759&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 Jan 14 02:54:33 2008
@@ -425,6 +425,11 @@
// initialized if this Derby has the SLAVE role for this database
private boolean inReplicationSlaveMode = false;
+ /** True if the database has been booted in replication slave pre
+ * mode, effectively turning off writes to the log file.
+ * @see SlaveFactory */
+ private boolean inReplicationSlavePreMode = false;
+
private Object slaveRecoveryMonitor; // for synchronization in slave mode
// The highest log file number the recovery thread is allowed to
@@ -3012,6 +3017,8 @@
if (mode != null && mode.equals(SlaveFactory.SLAVE_MODE)) {
inReplicationSlaveMode = true;
slaveRecoveryMonitor = new Object();
+ } else if (mode != null && mode.equals(SlaveFactory.SLAVE_PRE_MODE)) {
+ inReplicationSlavePreMode = true;
}
dataDirectory = startParams.getProperty(PersistentService.ROOT);
@@ -3621,6 +3628,13 @@
byte[] optionalData, int optionalDataOffset, int optionalDataLength)
throws StandardException
{
+ if (inReplicationSlavePreMode) {
+ // Return the *current* end of log without adding the log
+ // record to the log file. Effectively, this call to
+ // appendLogRecord does not do anything
+ return LogCounter.makeLogInstantAsLong(logFileNumber, endPosition);
+ }
+
long instant;
boolean testIncompleteLogWrite = false;
@@ -5039,17 +5053,6 @@
throws StandardException {
this.masterFactory = masterFactory;
synchronized(this) {
- // checkpoint followed by flushAll ensures that all data
- // and log are written on disk. After this, the database
- // can be safely copied to the slave location provided
- // that no clients perform operations on the database
- // before a connection has been established with the
- // slave. Note: this is a hack that will be removed once
- // the repliation functionality is able to send the
- // database from master to slave using the network
- // connection.
- rawStoreFactory.checkpoint();
- flushAll();
inReplicationMasterMode = true;
logOut.setReplicationMasterRole(masterFactory);
}
@@ -5082,6 +5085,9 @@
}
/**
+ * Initializes logOut so that log received from the replication
+ * master can be appended to the log file.
+ *
* Normally, logOut (the file log records are appended to) is set
* up as part of the recovery process. When the database is booted
* in replication slave mode, however, recovery will not get to
@@ -5113,7 +5119,7 @@
throws StandardException{
if (SanityManager.DEBUG) {
- SanityManager.ASSERT(!inReplicationSlaveMode,
+ SanityManager.ASSERT(inReplicationSlaveMode,
"This method should only be used when"
+ " in slave replication mode");
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/modules.properties
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/modules.properties?rev=611759&r1=611758&r2=611759&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/modules.properties (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/modules.properties Mon Jan 14 02:54:33 2008
@@ -220,6 +220,9 @@
derby.module.database=org.apache.derby.impl.db.BasicDatabase
cloudscape.config.database=all
+derby.module.database.slave=org.apache.derby.impl.db.SlaveDatabase
+cloudscape.config.database.slave=all
+
derby.module.lf=org.apache.derby.impl.sql.GenericLanguageFactory
cloudscape.config.lf=all