1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002, 2014 Oracle and/or its affiliates. All rights reserved. 5 * 6 */ 7 8 package com.sleepycat.je.rep; 9 10 import static org.junit.Assert.assertEquals; 11 import static org.junit.Assert.assertNull; 12 import static org.junit.Assert.assertTrue; 13 14 import java.io.File; 15 import java.util.UUID; 16 import java.util.concurrent.TimeUnit; 17 18 import org.junit.Before; 19 import org.junit.Test; 20 21 import com.sleepycat.bind.tuple.IntegerBinding; 22 import com.sleepycat.bind.tuple.LongBinding; 23 import com.sleepycat.je.CommitToken; 24 import com.sleepycat.je.Database; 25 import com.sleepycat.je.DatabaseException; 26 import com.sleepycat.je.Durability; 27 import com.sleepycat.je.StatsConfig; 28 import com.sleepycat.je.rep.NodeType; 29 import com.sleepycat.je.rep.ReplicatedEnvironment.State; 30 import com.sleepycat.je.rep.impl.RepTestBase; 31 import com.sleepycat.je.rep.utilint.RepTestUtils; 32 import com.sleepycat.je.rep.utilint.RepTestUtils.RepEnvInfo; 33 import com.sleepycat.je.utilint.VLSN; 34 import com.sleepycat.util.test.SharedTestUtils; 35 36 public class JoinGroupTest extends RepTestBase { 37 38 @Override 39 @Before setUp()40 public void setUp() 41 throws Exception { 42 43 super.setUp(); 44 45 /* Add a secondary node */ 46 repEnvInfo = RepTestUtils.setupExtendEnvInfo(repEnvInfo, 1); 47 repEnvInfo[repEnvInfo.length - 1].getRepConfig().setNodeType( 48 NodeType.SECONDARY); 49 } 50 51 /** 52 * Simulates the scenario where an entire group goes down and is restarted. 53 */ 54 @Test testAllJoinLeaveJoinGroup()55 public void testAllJoinLeaveJoinGroup() 56 throws DatabaseException, 57 InterruptedException { 58 59 createGroup(); 60 ReplicatedEnvironment masterRep = repEnvInfo[0].getEnv(); 61 populateDB(masterRep, TEST_DB_NAME, 100); 62 RepTestUtils.syncGroupToLastCommit(repEnvInfo, repEnvInfo.length); 63 64 /* Shutdown the entire group. */ 65 closeNodes(repEnvInfo); 66 67 /* 68 * Restart the group, using a longer join wait time to allow the 69 * secondary to query the primaries a second time after the election is 70 * complete. See RepNode.MASTER_QUERY_INTERVAL. 71 */ 72 final long masterQueryInterval = 10000; 73 restartNodes(JOIN_WAIT_TIME + masterQueryInterval, repEnvInfo); 74 } 75 76 // Tests repeated opens of the same environment 77 @Test testRepeatedOpen()78 public void testRepeatedOpen() 79 throws UnknownMasterException, DatabaseException { 80 81 /* All nodes have joined. */ 82 createGroup(); 83 84 /* Already joined, rejoin master. */ 85 State state = repEnvInfo[0].getEnv().getState(); 86 assertEquals(State.MASTER, state); 87 88 /* Already joined, rejoin replica, by creating another handle. */ 89 ReplicatedEnvironment r1Handle = new ReplicatedEnvironment 90 (repEnvInfo[1].getEnvHome(), 91 repEnvInfo[1].getRepConfig(), 92 repEnvInfo[1].getEnvConfig()); 93 state = r1Handle.getState(); 94 assertEquals(State.REPLICA, state); 95 r1Handle.close(); 96 } 97 98 @Test testDefaultJoinGroup()99 public void testDefaultJoinGroup() 100 throws UnknownMasterException, 101 DatabaseException { 102 103 createGroup(); 104 ReplicatedEnvironment masterRep = repEnvInfo[0].getEnv(); 105 assertEquals(State.MASTER, masterRep.getState()); 106 leaveGroupAllButMaster(); 107 /* Populates just the master. */ 108 CommitToken ct = populateDB(masterRep, TEST_DB_NAME, 100); 109 110 /* Replicas should have caught up when they re-open their handles. */ 111 for (RepEnvInfo ri : repEnvInfo) { 112 ReplicatedEnvironment rep = 113 (ri.getEnv() == null) ? ri.openEnv() : ri.getEnv(); 114 VLSN repVLSN = RepInternal.getRepImpl(rep). 115 getVLSNIndex().getRange().getLast(); 116 assertTrue(new VLSN(ct.getVLSN()).compareTo(repVLSN) <= 0); 117 } 118 } 119 120 @Test testDefaultJoinGroupHelper()121 public void testDefaultJoinGroupHelper() 122 throws UnknownMasterException, 123 DatabaseException { 124 125 for (int i = 0; i < repEnvInfo.length; i++) { 126 RepEnvInfo ri = repEnvInfo[i]; 127 if ((i + 1) == repEnvInfo.length) { 128 /* Use a non-master helper for the last replicator. */ 129 ReplicationConfig config = 130 RepTestUtils.createRepConfig((short) (i + 1)); 131 String hpPairs = ""; 132 // Skip the master, use all the other nodes 133 for (int j = 1; j < i; j++) { 134 hpPairs += 135 "," + repEnvInfo[j].getRepConfig().getNodeHostPort(); 136 } 137 hpPairs = hpPairs.substring(1); 138 config.setHelperHosts(hpPairs); 139 File envHome = ri.getEnvHome(); 140 ri = repEnvInfo[i] = 141 new RepEnvInfo(envHome, 142 config, 143 RepTestUtils.createEnvConfig 144 (Durability.COMMIT_SYNC)); 145 } 146 ri.openEnv(); 147 State state = ri.getEnv().getState(); 148 assertEquals((i == 0) ? State.MASTER : State.REPLICA, state); 149 } 150 } 151 152 @Test testTimeConsistencyJoinGroup()153 public void testTimeConsistencyJoinGroup() 154 throws UnknownMasterException, 155 DatabaseException{ 156 157 createGroup(); 158 ReplicatedEnvironment masterRep = repEnvInfo[0].getEnv(); 159 assertEquals(State.MASTER, masterRep.getState()); 160 161 leaveGroupAllButMaster(); 162 /* Populates just the master. */ 163 populateDB(masterRep, TEST_DB_NAME, 1000); 164 165 repEnvInfo[1].openEnv 166 (new TimeConsistencyPolicy(1, TimeUnit.MILLISECONDS, 167 RepTestUtils.MINUTE_MS, 168 TimeUnit.MILLISECONDS)); 169 ReplicatedEnvironmentStats stats = 170 repEnvInfo[1].getEnv().getRepStats(StatsConfig.DEFAULT); 171 172 assertEquals(1, stats.getTrackerLagConsistencyWaits()); 173 assertTrue(stats.getTrackerLagConsistencyWaitMs() > 0); 174 } 175 176 @Test testVLSNConsistencyJoinGroup()177 public void testVLSNConsistencyJoinGroup() 178 throws UnknownMasterException, 179 DatabaseException, 180 InterruptedException { 181 182 createGroup(); 183 ReplicatedEnvironment masterRep = repEnvInfo[0].getEnv(); 184 assertEquals(State.MASTER, masterRep.getState()); 185 leaveGroupAllButMaster(); 186 /* Populates just the master. */ 187 populateDB(masterRep, TEST_DB_NAME, 100); 188 UUID uuid = 189 RepInternal.getRepImpl(masterRep).getRepNode().getUUID(); 190 long masterVLSN = RepInternal.getRepImpl(masterRep). 191 getVLSNIndex().getRange().getLast(). 192 getSequence()+2 /* 1 new entry + txn commit record */; 193 194 JoinCommitThread jt = 195 new JoinCommitThread(new CommitToken(uuid,masterVLSN), 196 repEnvInfo[1]); 197 jt.start(); 198 Thread.sleep(5000); 199 // supply the vlsn it's waiting for. Record count MUST sync up with 200 // the expected masterVLSN 201 populateDB(masterRep, TEST_DB_NAME, 1); 202 jt.join(JOIN_WAIT_TIME); 203 204 assertTrue(!jt.isAlive()); 205 assertNull("Join thread exception", jt.testException); 206 } 207 208 /* 209 * Test that a replica using the jdb files copied from the master can join 210 * the group. 211 */ 212 @Test testCopyEnvJoin()213 public void testCopyEnvJoin() 214 throws Throwable { 215 216 createGroup(1); 217 assertTrue(repEnvInfo[0].isMaster()); 218 219 /* Create some data on the master. */ 220 populateDB(repEnvInfo[0].getEnv(), "testDB", 1000); 221 222 /* Close the Environment before copy. */ 223 repEnvInfo[0].closeEnv(); 224 225 /* First check there is no jdb files in the second replica. */ 226 File repEnvHome = repEnvInfo[1].getEnvHome(); 227 File[] envFiles = repEnvHome.listFiles(); 228 for (File envFile : envFiles) { 229 if (envFile.getName().contains(".jdb")) { 230 throw new IllegalStateException 231 ("Replica home should not contain any jdb files"); 232 } 233 } 234 235 /* Copy the jdb files from the master to the replica. */ 236 SharedTestUtils.copyFiles(repEnvInfo[0].getEnvHome(), 237 repEnvInfo[1].getEnvHome()); 238 239 /* Reopen the master. */ 240 repEnvInfo[0].openEnv(); 241 assertTrue(repEnvInfo[0].isMaster()); 242 243 /* Open the replica. */ 244 repEnvInfo[1].openEnv(); 245 assertTrue(repEnvInfo[1].isReplica()); 246 247 /* Read the data to make sure data is correctly copied. */ 248 Database db = 249 repEnvInfo[1].getEnv().openDatabase(null, "testDB", dbconfig); 250 for (int i = 0; i < 1000; i++) { 251 IntegerBinding.intToEntry(i, key); 252 db.get(null, key, data, null); 253 assertEquals(i, (int) LongBinding.entryToLong(data)); 254 } 255 db.close(); 256 } 257 258 /* Utility thread for joining group. */ 259 class JoinCommitThread extends Thread { 260 final RepEnvInfo replicator; 261 final CommitToken commitToken; 262 Exception testException = null; 263 JoinCommitThread(CommitToken commitToken, RepEnvInfo replicator)264 JoinCommitThread(CommitToken commitToken, RepEnvInfo replicator) { 265 this.commitToken = commitToken; 266 this.replicator = replicator; 267 } 268 269 @Override run()270 public void run() { 271 try { 272 ReplicatedEnvironment repenv= replicator.openEnv 273 (new CommitPointConsistencyPolicy(commitToken, 274 RepTestUtils.MINUTE_MS, 275 TimeUnit.MILLISECONDS)); 276 assertEquals(ReplicatedEnvironment.State.REPLICA, 277 repenv.getState()); 278 ReplicatedEnvironmentStats stats = 279 replicator.getEnv().getRepStats(StatsConfig.DEFAULT); 280 281 assertEquals(1, stats.getTrackerVLSNConsistencyWaits()); 282 assertTrue(stats.getTrackerVLSNConsistencyWaitMs() > 0); 283 } catch (UnknownMasterException e) { 284 testException = e; 285 throw new RuntimeException(e); 286 } catch (DatabaseException e) { 287 testException = e; 288 throw new RuntimeException(e); 289 } 290 } 291 } 292 } 293