/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved. * */ package repmgrtests; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.io.BufferedReader; import java.io.File; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.sleepycat.db.Database; import com.sleepycat.db.DatabaseConfig; import com.sleepycat.db.DatabaseType; import com.sleepycat.db.Environment; import com.sleepycat.db.EnvironmentConfig; import com.sleepycat.db.EventHandlerAdapter; import com.sleepycat.db.ReplicationManagerAckPolicy; import com.sleepycat.db.ReplicationManagerSiteConfig; import com.sleepycat.db.ReplicationManagerStartPolicy; import com.sleepycat.db.VerboseConfig; /** * A simple test of repmgr allowing a new, seemingly "redundant" * incoming connection to take over, giving up (closing) an existing * connection. */ public class TestRedundantTakeover { private static final String TEST_DIR_NAME = "TESTDIR"; private File testdir; private Process fiddler; private int mgrPort; private int masterPort; private int clientPort; class MyEventHandler extends EventHandlerAdapter { private boolean done = false; private boolean panic = false; private int permFailCount = 0; @Override synchronized public void handleRepStartupDoneEvent() { done = true; notifyAll(); } @Override synchronized public void handleRepPermFailedEvent() { permFailCount++; } synchronized public int getPermFailCount() { return permFailCount; } @Override synchronized public void handlePanicEvent() { done = true; panic = true; notifyAll(); } synchronized void await() throws Exception { while (!done) { wait(); } if (panic) throw new Exception("aborted by panic in DB"); } } @Before public void setUp() throws Exception { testdir = new File(TEST_DIR_NAME); Util.rm_rf(testdir); testdir.mkdir(); if (Boolean.getBoolean("MANUAL_FIDDLER_START")) { masterPort = 6000; clientPort = 6001; mgrPort = 8000; fiddler = null; } else { String mgrPortNum = System.getenv("DB_TEST_FAKE_PORT"); assertNotNull("required DB_TEST_FAKE_PORT environment variable not found", mgrPortNum); mgrPort = Integer.parseInt(mgrPortNum); PortsConfig p = new PortsConfig(3); masterPort = p.getRealPort(0); clientPort = p.getRealPort(1); fiddler = Util.startFiddler(p, getClass().getName(), mgrPort); } } @After public void tearDown() throws Exception { if (fiddler != null) { fiddler.destroy(); } } @Test public void testExchange() throws Exception { EnvironmentConfig masterConfig = makeBasicConfig(); ReplicationManagerSiteConfig site = new ReplicationManagerSiteConfig("localhost", masterPort); site.setLocalSite(true); masterConfig.addReplicationManagerSite(site); MyEventHandler masterMonitor = new MyEventHandler(); masterConfig.setEventHandler(masterMonitor); File masterDir = mkdir("master"); Environment master = new Environment(masterDir, masterConfig); master.replicationManagerStart(3, ReplicationManagerStartPolicy.REP_MASTER); // create client, wait for it to finish sync-ing up // MyEventHandler clientMonitor = new MyEventHandler(); EnvironmentConfig ec = makeClientConfig(clientMonitor, clientPort, masterPort); File clientDir = mkdir("client"); Environment client = new Environment(clientDir, ec); client.replicationManagerStart(1, ReplicationManagerStartPolicy.REP_CLIENT); clientMonitor.await(); // tell fiddler to freeze the connection without killing it // Socket s = new Socket("localhost", mgrPort); OutputStreamWriter w = new OutputStreamWriter(s.getOutputStream()); String path1 = "{" + clientPort + "," + masterPort + "}"; // looks like {6001,6000} w.write("{" + path1 + ",toss_all}\r\n"); w.flush(); BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); assertEquals("ok", br.readLine()); // close client (which will be hidden from master), and try // opening it again client.close(); clientMonitor = new MyEventHandler(); ec = makeClientConfig(clientMonitor, clientPort, masterPort); ec.setRunRecovery(true); client = new Environment(clientDir, ec); client.replicationManagerStart(1, ReplicationManagerStartPolicy.REP_ELECTION); clientMonitor.await(); // verify with fiddler that the old connection was closed. // TODO // do a new live transaction at the master, make sure // that can work. // int initialCount = masterMonitor.getPermFailCount(); DatabaseConfig dc2 = new DatabaseConfig(); dc2.setTransactional(true); dc2.setAllowCreate(true); dc2.setType(DatabaseType.BTREE); Database db2 = master.openDatabase(null, "test2.db", null, dc2); db2.close(); assertEquals(initialCount, masterMonitor.getPermFailCount()); master.close(); client.close(); w.write("shutdown\r\n"); w.flush(); assertEquals("ok", br.readLine()); s.close(); fiddler = null; } private EnvironmentConfig makeClientConfig(MyEventHandler evHandler, int clientPort, int masterPort) throws Exception { EnvironmentConfig ec = makeBasicConfig(); ReplicationManagerSiteConfig site = new ReplicationManagerSiteConfig("localhost", clientPort); site.setLocalSite(true); ec.addReplicationManagerSite(site); site = new ReplicationManagerSiteConfig("localhost", masterPort); site.setBootstrapHelper(true); ec.addReplicationManagerSite(site); ec.setEventHandler(evHandler); return (ec); } public static EnvironmentConfig makeBasicConfig() { EnvironmentConfig ec = new EnvironmentConfig(); ec.setAllowCreate(true); ec.setInitializeCache(true); ec.setInitializeLocking(true); ec.setInitializeLogging(true); ec.setInitializeReplication(true); ec.setTransactional(true); ec.setThreaded(true); ec.setReplicationManagerAckPolicy(ReplicationManagerAckPolicy.ALL); if (Boolean.getBoolean("VERB_REPLICATION")) ec.setVerbose(VerboseConfig.REPLICATION, true); return (ec); } public File mkdir(String dname) { File f = new File(testdir, dname); f.mkdir(); return f; } }