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