View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.replication;
20  
21  import static org.junit.Assert.*;
22  
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.SortedMap;
26  import java.util.SortedSet;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.hadoop.hbase.ServerName;
31  import org.apache.hadoop.hbase.zookeeper.ZKConfig;
32  import org.apache.zookeeper.KeeperException;
33  import org.junit.Before;
34  import org.junit.Test;
35  
36  /**
37   * White box testing for replication state interfaces. Implementations should extend this class, and
38   * initialize the interfaces properly.
39   */
40  public abstract class TestReplicationStateBasic {
41  
42    protected ReplicationQueues rq1;
43    protected ReplicationQueues rq2;
44    protected ReplicationQueues rq3;
45    protected ReplicationQueuesClient rqc;
46    protected String server1 = ServerName.valueOf("hostname1.example.org", 1234, -1L).toString();
47    protected String server2 = ServerName.valueOf("hostname2.example.org", 1234, -1L).toString();
48    protected String server3 = ServerName.valueOf("hostname3.example.org", 1234, -1L).toString();
49    protected ReplicationPeers rp;
50    protected static final String ID_ONE = "1";
51    protected static final String ID_TWO = "2";
52    protected static String KEY_ONE;
53    protected static String KEY_TWO;
54  
55    // For testing when we try to replicate to ourself
56    protected String OUR_ID = "3";
57    protected String OUR_KEY;
58  
59    protected static int zkTimeoutCount;
60    protected static final int ZK_MAX_COUNT = 300;
61    protected static final int ZK_SLEEP_INTERVAL = 100; // millis
62  
63    private static final Log LOG = LogFactory.getLog(TestReplicationStateBasic.class);
64  
65    @Before
66    public void setUp() {
67      zkTimeoutCount = 0;
68    }
69  
70    @Test
71    public void testReplicationQueuesClient() throws ReplicationException, KeeperException {
72      rqc.init();
73      // Test methods with empty state
74      assertEquals(0, rqc.getListOfReplicators().size());
75      assertNull(rqc.getLogsInQueue(server1, "qId1"));
76      assertNull(rqc.getAllQueues(server1));
77  
78      /*
79       * Set up data Two replicators: -- server1: three queues with 0, 1 and 2 log files each --
80       * server2: zero queues
81       */
82      rq1.init(server1);
83      rq2.init(server2);
84      rq1.addLog("qId1", "trash");
85      rq1.removeLog("qId1", "trash");
86      rq1.addLog("qId2", "filename1");
87      rq1.addLog("qId3", "filename2");
88      rq1.addLog("qId3", "filename3");
89      rq2.addLog("trash", "trash");
90      rq2.removeQueue("trash");
91  
92      List<String> reps = rqc.getListOfReplicators();
93      assertEquals(2, reps.size());
94      assertTrue(server1, reps.contains(server1));
95      assertTrue(server2, reps.contains(server2));
96  
97      assertNull(rqc.getLogsInQueue("bogus", "bogus"));
98      assertNull(rqc.getLogsInQueue(server1, "bogus"));
99      assertEquals(0, rqc.getLogsInQueue(server1, "qId1").size());
100     assertEquals(1, rqc.getLogsInQueue(server1, "qId2").size());
101     assertEquals("filename1", rqc.getLogsInQueue(server1, "qId2").get(0));
102 
103     assertNull(rqc.getAllQueues("bogus"));
104     assertEquals(0, rqc.getAllQueues(server2).size());
105     List<String> list = rqc.getAllQueues(server1);
106     assertEquals(3, list.size());
107     assertTrue(list.contains("qId2"));
108     assertTrue(list.contains("qId3"));
109   }
110 
111   @Test
112   public void testReplicationQueues() throws ReplicationException {
113     rq1.init(server1);
114     rq2.init(server2);
115     rq3.init(server3);
116     //Initialize ReplicationPeer so we can add peers (we don't transfer lone queues)
117     rp.init();
118 
119     // 3 replicators should exist
120     assertEquals(3, rq1.getListOfReplicators().size());
121     rq1.removeQueue("bogus");
122     rq1.removeLog("bogus", "bogus");
123     rq1.removeAllQueues();
124     assertNull(rq1.getAllQueues());
125     assertEquals(0, rq1.getLogPosition("bogus", "bogus"));
126     assertNull(rq1.getLogsInQueue("bogus"));
127     assertEquals(0, rq1.claimQueues(ServerName.valueOf("bogus", 1234, -1L).toString()).size());
128 
129     rq1.setLogPosition("bogus", "bogus", 5L);
130 
131     populateQueues();
132 
133     assertEquals(3, rq1.getListOfReplicators().size());
134     assertEquals(0, rq2.getLogsInQueue("qId1").size());
135     assertEquals(5, rq3.getLogsInQueue("qId5").size());
136     assertEquals(0, rq3.getLogPosition("qId1", "filename0"));
137     rq3.setLogPosition("qId5", "filename4", 354L);
138     assertEquals(354L, rq3.getLogPosition("qId5", "filename4"));
139 
140     assertEquals(5, rq3.getLogsInQueue("qId5").size());
141     assertEquals(0, rq2.getLogsInQueue("qId1").size());
142     assertEquals(0, rq1.getAllQueues().size());
143     assertEquals(1, rq2.getAllQueues().size());
144     assertEquals(5, rq3.getAllQueues().size());
145 
146     assertEquals(0, rq3.claimQueues(server1).size());
147     assertEquals(2, rq3.getListOfReplicators().size());
148 
149     SortedMap<String, SortedSet<String>> queues = rq2.claimQueues(server3);
150     assertEquals(5, queues.size());
151     assertEquals(1, rq2.getListOfReplicators().size());
152 
153     // Try to claim our own queues
154     assertEquals(0, rq2.claimQueues(server2).size());
155 
156     assertEquals(6, rq2.getAllQueues().size());
157 
158     rq2.removeAllQueues();
159 
160     assertEquals(0, rq2.getListOfReplicators().size());
161   }
162 
163   @Test
164   public void testHfileRefsReplicationQueues() throws ReplicationException, KeeperException {
165     rp.init();
166     rq1.init(server1);
167     rqc.init();
168 
169     List<String> files1 = new ArrayList<String>(3);
170     files1.add("file_1");
171     files1.add("file_2");
172     files1.add("file_3");
173     assertNull(rqc.getReplicableHFiles(ID_ONE));
174     assertEquals(0, rqc.getAllPeersFromHFileRefsQueue().size());
175     rp.addPeer(ID_ONE, new ReplicationPeerConfig().setClusterKey(KEY_ONE), null);
176     rq1.addHFileRefs(ID_ONE, files1);
177     assertEquals(1, rqc.getAllPeersFromHFileRefsQueue().size());
178     assertEquals(3, rqc.getReplicableHFiles(ID_ONE).size());
179     List<String> files2 = new ArrayList<>(files1);
180     String removedString = files2.remove(0);
181     rq1.removeHFileRefs(ID_ONE, files2);
182     assertEquals(1, rqc.getReplicableHFiles(ID_ONE).size());
183     files2 = new ArrayList<>(1);
184     files2.add(removedString);
185     rq1.removeHFileRefs(ID_ONE, files2);
186     assertEquals(0, rqc.getReplicableHFiles(ID_ONE).size());
187     rp.removePeer(ID_ONE);
188   }
189 
190   @Test
191   public void testRemovePeerForHFileRefs() throws ReplicationException, KeeperException {
192     rq1.init(server1);
193     rqc.init();
194 
195     rp.init();
196     rp.addPeer(ID_ONE, new ReplicationPeerConfig().setClusterKey(KEY_ONE), null);
197     rp.addPeer(ID_TWO, new ReplicationPeerConfig().setClusterKey(KEY_TWO), null);
198 
199     List<String> files1 = new ArrayList<String>(3);
200     files1.add("file_1");
201     files1.add("file_2");
202     files1.add("file_3");
203     rq1.addHFileRefs(ID_ONE, files1);
204     rq1.addHFileRefs(ID_TWO, files1);
205     assertEquals(2, rqc.getAllPeersFromHFileRefsQueue().size());
206     assertEquals(3, rqc.getReplicableHFiles(ID_ONE).size());
207     assertEquals(3, rqc.getReplicableHFiles(ID_TWO).size());
208 
209     rp.removePeer(ID_ONE);
210     assertEquals(1, rqc.getAllPeersFromHFileRefsQueue().size());
211     assertNull(rqc.getReplicableHFiles(ID_ONE));
212     assertEquals(3, rqc.getReplicableHFiles(ID_TWO).size());
213 
214     rp.removePeer(ID_TWO);
215     assertEquals(0, rqc.getAllPeersFromHFileRefsQueue().size());
216     assertNull(rqc.getReplicableHFiles(ID_TWO));
217   }
218 
219   @Test
220   public void testReplicationPeers() throws Exception {
221     rp.init();
222 
223     // Test methods with non-existent peer ids
224     try {
225       rp.removePeer("bogus");
226       fail("Should have thrown an IllegalArgumentException when passed a bogus peerId");
227     } catch (IllegalArgumentException e) {
228     }
229     try {
230       rp.enablePeer("bogus");
231       fail("Should have thrown an IllegalArgumentException when passed a bogus peerId");
232     } catch (IllegalArgumentException e) {
233     }
234     try {
235       rp.disablePeer("bogus");
236       fail("Should have thrown an IllegalArgumentException when passed a bogus peerId");
237     } catch (IllegalArgumentException e) {
238     }
239     try {
240       rp.getStatusOfPeer("bogus");
241       fail("Should have thrown an IllegalArgumentException when passed a bogus peerId");
242     } catch (IllegalArgumentException e) {
243     }
244     assertFalse(rp.peerAdded("bogus"));
245     rp.peerRemoved("bogus");
246 
247     assertNull(rp.getPeerConf("bogus"));
248     assertNumberOfPeers(0);
249 
250     // Add some peers
251     rp.addPeer(ID_ONE, new ReplicationPeerConfig().setClusterKey(KEY_ONE), null);
252     assertNumberOfPeers(1);
253     rp.addPeer(ID_TWO, new ReplicationPeerConfig().setClusterKey(KEY_TWO), null);
254     assertNumberOfPeers(2);
255 
256     // Test methods with a peer that is added but not connected
257     try {
258       rp.getStatusOfPeer(ID_ONE);
259       fail("There are no connected peers, should have thrown an IllegalArgumentException");
260     } catch (IllegalArgumentException e) {
261     }
262     assertEquals(KEY_ONE, ZKConfig.getZooKeeperClusterKey(rp.getPeerConf(ID_ONE).getSecond()));
263     rp.removePeer(ID_ONE);
264     rp.peerRemoved(ID_ONE);
265     assertNumberOfPeers(1);
266 
267     // Add one peer
268     rp.addPeer(ID_ONE, new ReplicationPeerConfig().setClusterKey(KEY_ONE), null);
269     rp.peerAdded(ID_ONE);
270     assertNumberOfPeers(2);
271     assertTrue(rp.getStatusOfPeer(ID_ONE));
272     rp.disablePeer(ID_ONE);
273     assertConnectedPeerStatus(false, ID_ONE);
274     rp.enablePeer(ID_ONE);
275     assertConnectedPeerStatus(true, ID_ONE);
276 
277     // Disconnect peer
278     rp.peerRemoved(ID_ONE);
279     assertNumberOfPeers(2);
280     try {
281       rp.getStatusOfPeer(ID_ONE);
282       fail("There are no connected peers, should have thrown an IllegalArgumentException");
283     } catch (IllegalArgumentException e) {
284     }
285   }
286 
287   protected void assertConnectedPeerStatus(boolean status, String peerId) throws Exception {
288     // we can first check if the value was changed in the store, if it wasn't then fail right away
289     if (status != rp.getStatusOfPeerFromBackingStore(peerId)) {
290       fail("ConnectedPeerStatus was " + !status + " but expected " + status + " in ZK");
291     }
292     while (true) {
293       if (status == rp.getStatusOfPeer(peerId)) {
294         return;
295       }
296       if (zkTimeoutCount < ZK_MAX_COUNT) {
297         LOG.debug("ConnectedPeerStatus was " + !status + " but expected " + status
298             + ", sleeping and trying again.");
299         Thread.sleep(ZK_SLEEP_INTERVAL);
300       } else {
301         fail("Timed out waiting for ConnectedPeerStatus to be " + status);
302       }
303     }
304   }
305 
306   protected void assertNumberOfPeers(int total) {
307     assertEquals(total, rp.getAllPeerConfigs().size());
308     assertEquals(total, rp.getAllPeerIds().size());
309     assertEquals(total, rp.getAllPeerIds().size());
310   }
311 
312   /*
313    * three replicators: rq1 has 0 queues, rq2 has 1 queue with no logs, rq3 has 5 queues with 1, 2,
314    * 3, 4, 5 log files respectively
315    */
316   protected void populateQueues() throws ReplicationException {
317     rq1.addLog("trash", "trash");
318     rq1.removeQueue("trash");
319 
320     rq2.addLog("qId1", "trash");
321     rq2.removeLog("qId1", "trash");
322 
323     for (int i = 1; i < 6; i++) {
324       for (int j = 0; j < i; j++) {
325         rq3.addLog("qId" + i, "filename" + j);
326       }
327       //Add peers for the corresponding queues so they are not orphans
328       rp.addPeer("qId" + i, new ReplicationPeerConfig().setClusterKey("bogus" + i), null);
329     }
330   }
331 }
332