1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
|
/*
Derby - Class org.apache.derbyTesting.functionTests.tests.replicationTests.Derby5937SlaveShutdownTest
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.derbyTesting.functionTests.tests.replicationTests;
import java.io.File;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import junit.framework.Test;
import org.apache.derbyTesting.functionTests.util.PrivilegedFileOpsForTests;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseTestCase;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.JDBCDataSource;
import org.apache.derbyTesting.junit.NetworkServerTestSetup;
import org.apache.derbyTesting.junit.SecurityManagerSetup;
import org.apache.derbyTesting.junit.TestConfiguration;
/**
* <p>
* Regression test case for DERBY-5937. After fail-over, the slave database
* will leak a file handle for the active log file.
* </p>
*
* <p>
* The test case will set up replication between a master database and a
* slave database, perform fail-over to the slave, and then shut down the
* slave database. Finally, it attempts to delete the slave database. On
* Windows, this fails if DERBY-5937 is not fixed, because one of the log
* files is still held open, and Windows doesn't allow deletion of open
* files.
* </p>
*/
public class Derby5937SlaveShutdownTest extends BaseJDBCTestCase {
private static final String MASTER_DB = "d5937-master-db";
private static final String SLAVE_DB = "d5937-slave-db";
private static final String FAILOVER_SUCCESS = "XRE20";
private static final String DB_SHUTDOWN_SUCCESS = "08006";
private static final long RETRY_INTERVAL = 50L;
public Derby5937SlaveShutdownTest(String name) {
super(name);
}
public static Test suite() {
//DERBY-5975 test fails intermittently with weme causing a hang.
// Likely a jvm issue, so don't run on that OS...
if (BaseTestCase.isJ9Platform())
{
Test test = new BaseTestSuite("Derby5937SlaveShutdownTest");
return test;
}
Class klass = Derby5937SlaveShutdownTest.class;
// The default security policy doesn't allow derby.jar to do
// networking, which is needed for replication, so install a custom
// policy for this test.
return new SecurityManagerSetup(
TestConfiguration.singleUseDatabaseDecorator(
TestConfiguration.embeddedSuite(klass), MASTER_DB),
klass.getName().replace('.', '/') + ".policy", true);
}
public void testSlaveFailoverLeak() throws Exception {
// First establish a connection so that the database is created.
getConnection().close();
// Then shut down the database cleanly so that it can be used
// to seed the replication slave.
final TestConfiguration config = TestConfiguration.getCurrent();
config.shutdownDatabase();
// Copy the database to the slave.
final String masterDb = config.getDatabasePath(MASTER_DB);
final String slaveDb = config.getDatabasePath(SLAVE_DB);
PrivilegedFileOpsForTests.copy(new File(masterDb), new File(slaveDb));
// And start the slave.
DataSource startSlaveDS = JDBCDataSource.getDataSource(SLAVE_DB);
JDBCDataSource.setBeanProperty(startSlaveDS, "connectionAttributes",
"startSlave=true;slaveHost=" + config.getHostName() +
";slavePort=" + config.getPort());
SlaveThread slave = new SlaveThread(startSlaveDS);
slave.start();
// Start the master. This will fail until the slave is up, so do
// it in a loop until successful or time runs out.
DataSource startMasterDS = JDBCDataSource.getDataSource();
JDBCDataSource.setBeanProperty(startMasterDS, "connectionAttributes",
"startMaster=true;slaveHost=" + config.getHostName() +
";slavePort=" + config.getPort());
long giveUp =
System.currentTimeMillis() + NetworkServerTestSetup.getWaitTime();
Connection c = null;
while (c == null) {
try {
c = startMasterDS.getConnection();
} catch (SQLException sqle) {
slave.checkError(); // Exit early if the slave has failed
if (System.currentTimeMillis() > giveUp) {
fail("Master won't start", sqle);
} else {
println("Retrying after startMaster failed with: " + sqle);
Thread.sleep(RETRY_INTERVAL);
}
}
}
c.close();
// Wait for the slave thread to complete, which it will do once
// it's connected to the master.
slave.join();
slave.checkError();
// Perform fail-over.
DataSource failoverDS = JDBCDataSource.getDataSource();
JDBCDataSource.setBeanProperty(
failoverDS, "connectionAttributes", "failover=true");
try {
failoverDS.getConnection();
fail("failover should receive exception");
} catch (SQLException sqle) {
assertSQLState(FAILOVER_SUCCESS, sqle);
}
// Shut down the slave database. This will fail until failover is
// complete, so do it in a loop until successful or time runs out.
giveUp =
System.currentTimeMillis() + NetworkServerTestSetup.getWaitTime();
DataSource slaveShutdownDS = JDBCDataSource.getDataSource(SLAVE_DB);
JDBCDataSource.setBeanProperty(
slaveShutdownDS, "shutdownDatabase", "shutdown");
while (true) {
try {
slaveShutdownDS.getConnection();
fail("Shutdown of slave database didn't throw an exception");
} catch (SQLException sqle) {
if (DB_SHUTDOWN_SUCCESS.equals(sqle.getSQLState())) {
// The expected shutdown exception was thrown. Break out
// of the loop.
break;
} else if (System.currentTimeMillis() > giveUp) {
fail("Could not shut down slave database", sqle);
} else {
println("Retrying after failover failed with: " + sqle);
Thread.sleep(RETRY_INTERVAL);
}
}
}
// This call used to fail on Windows because one of the log files
// was still open.
assertDirectoryDeleted(new File(slaveDb));
}
/**
* Helper thread which starts a replication slave and blocks until the
* slave is connected to a master database.
*/
private class SlaveThread extends Thread {
private final DataSource ds;
private volatile Throwable error;
SlaveThread(DataSource ds) {
this.ds = ds;
}
public void run() {
try {
run_();
} catch (Throwable t) {
error = t;
}
}
private void run_() throws Exception {
println("Slave thread started.");
try {
ds.getConnection();
fail("startSlave should throw exception");
} catch (SQLException sqle) {
assertSQLState("XRE08", sqle);
}
}
void checkError() {
if (error != null) {
fail("Slave thread failed", error);
}
}
}
}
|