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.assertEquals;
22  import static org.junit.Assert.assertFalse;
23  import static org.junit.Assert.assertNull;
24  import static org.junit.Assert.assertTrue;
25  import static org.junit.Assert.fail;
26  
27  import java.util.ArrayList;
28  import java.util.List;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.apache.hadoop.fs.Path;
33  import org.apache.hadoop.hbase.ServerName;
34  import org.apache.hadoop.hbase.util.Pair;
35  import org.apache.hadoop.hbase.zookeeper.ZKConfig;
36  import org.apache.zookeeper.KeeperException;
37  import org.junit.Before;
38  import org.junit.Test;
39  
40  /**
41   * White box testing for replication state interfaces. Implementations should extend this class, and
42   * initialize the interfaces properly.
43   */
44  public abstract class TestReplicationStateBasic {
45  
46    protected ReplicationQueues rq1;
47    protected ReplicationQueues rq2;
48    protected ReplicationQueues rq3;
49    protected ReplicationQueuesClient rqc;
50    protected String server1 = ServerName.valueOf("hostname1.example.org", 1234, -1L).toString();
51    protected String server2 = ServerName.valueOf("hostname2.example.org", 1234, -1L).toString();
52    protected String server3 = ServerName.valueOf("hostname3.example.org", 1234, -1L).toString();
53    protected ReplicationPeers rp;
54    protected static final String ID_ONE = "1";
55    protected static final String ID_TWO = "2";
56    protected static String KEY_ONE;
57    protected static String KEY_TWO;
58  
59    // For testing when we try to replicate to ourself
60    protected String OUR_KEY;
61  
62    protected static int zkTimeoutCount;
63    protected static final int ZK_MAX_COUNT = 300;
64    protected static final int ZK_SLEEP_INTERVAL = 100; // millis
65  
66    private static final Log LOG = LogFactory.getLog(TestReplicationStateBasic.class);
67  
68    @Before
69    public void setUp() {
70      zkTimeoutCount = 0;
71    }
72  
73    @Test
74    public void testReplicationQueuesClient() throws ReplicationException, KeeperException {
75      rqc.init();
76      // Test methods with empty state
77      assertEquals(0, rqc.getListOfReplicators().size());
78      assertNull(rqc.getLogsInQueue(server1, "qId1"));
79      assertNull(rqc.getAllQueues(server1));
80  
81      /*
82       * Set up data Two replicators: -- server1: three queues with 0, 1 and 2 log files each --
83       * server2: zero queues
84       */
85      rq1.init(server1);
86      rq2.init(server2);
87      rq1.addLog("qId1", "trash");
88      rq1.removeLog("qId1", "trash");
89      rq1.addLog("qId2", "filename1");
90      rq1.addLog("qId3", "filename2");
91      rq1.addLog("qId3", "filename3");
92      rq2.addLog("trash", "trash");
93      rq2.removeQueue("trash");
94  
95      List<String> reps = rqc.getListOfReplicators();
96      assertEquals(2, reps.size());
97      assertTrue(server1, reps.contains(server1));
98      assertTrue(server2, reps.contains(server2));
99  
100     assertNull(rqc.getLogsInQueue("bogus", "bogus"));
101     assertNull(rqc.getLogsInQueue(server1, "bogus"));
102     assertEquals(0, rqc.getLogsInQueue(server1, "qId1").size());
103     assertEquals(1, rqc.getLogsInQueue(server1, "qId2").size());
104     assertEquals("filename1", rqc.getLogsInQueue(server1, "qId2").get(0));
105 
106     assertNull(rqc.getAllQueues("bogus"));
107     assertEquals(0, rqc.getAllQueues(server2).size());
108     List<String> list = rqc.getAllQueues(server1);
109     assertEquals(3, list.size());
110     assertTrue(list.contains("qId2"));
111     assertTrue(list.contains("qId3"));
112   }
113 
114   @Test
115   public void testReplicationQueues() throws ReplicationException {
116     rq1.init(server1);
117     rq2.init(server2);
118     rq3.init(server3);
119     //Initialize ReplicationPeer so we can add peers (we don't transfer lone queues)
120     rp.init();
121 
122     // 3 replicators should exist
123     assertEquals(3, rq1.getListOfReplicators().size());
124     rq1.removeQueue("bogus");
125     rq1.removeAllQueues();
126     assertNull(rq1.getAllQueues());
127     assertEquals(0, rq1.getLogPosition("bogus", "bogus"));
128     assertNull(rq1.getLogsInQueue("bogus"));
129     assertNull(rq1.getUnClaimedQueueIds(ServerName.valueOf("bogus", 1234, -1L).toString()));
130 
131     rq1.setLogPosition("bogus", "bogus", 5L);
132 
133     populateQueues();
134 
135     assertEquals(3, rq1.getListOfReplicators().size());
136     assertEquals(0, rq2.getLogsInQueue("qId1").size());
137     assertEquals(5, rq3.getLogsInQueue("qId5").size());
138     assertEquals(0, rq3.getLogPosition("qId1", "filename0"));
139     rq3.setLogPosition("qId5", "filename4", 354L);
140     assertEquals(354L, rq3.getLogPosition("qId5", "filename4"));
141 
142     assertEquals(5, rq3.getLogsInQueue("qId5").size());
143     assertEquals(0, rq2.getLogsInQueue("qId1").size());
144     assertEquals(0, rq1.getAllQueues().size());
145     assertEquals(1, rq2.getAllQueues().size());
146     assertEquals(5, rq3.getAllQueues().size());
147 
148     assertEquals(0, rq3.getUnClaimedQueueIds(server1).size());
149     rq3.removeReplicatorIfQueueIsEmpty(server1);
150     assertEquals(2, rq3.getListOfReplicators().size());
151 
152     List<String> queues = rq2.getUnClaimedQueueIds(server3);
153     assertEquals(5, queues.size());
154     for(String queue: queues) {
155       rq2.claimQueue(server3, queue);
156     }
157     rq2.removeReplicatorIfQueueIsEmpty(server3);
158     assertEquals(1, rq2.getListOfReplicators().size());
159 
160     // Try to claim our own queues
161     assertNull(rq2.getUnClaimedQueueIds(server2));
162     rq2.removeReplicatorIfQueueIsEmpty(server2);
163 
164     assertEquals(6, rq2.getAllQueues().size());
165 
166     rq2.removeAllQueues();
167 
168     assertEquals(0, rq2.getListOfReplicators().size());
169   }
170 
171   @Test
172   public void testLogRemovalWithNoZnode() throws ReplicationException {
173     rq1.init(server1);
174     Exception expectedException = null;
175     try {
176       rq1.removeLog("bogus", "bogus");
177     } catch (ReplicationException e) {
178       expectedException = e;
179     }
180 
181     assertTrue(expectedException instanceof ReplicationSourceWithoutPeerException);
182   }
183 
184   @Test
185   public void testInvalidClusterKeys() throws ReplicationException, KeeperException {
186     rp.init();
187 
188     try {
189       rp.addPeer(ID_ONE,
190         new ReplicationPeerConfig().setClusterKey("hostname1.example.org:1234:hbase"));
191       fail("Should throw an IllegalArgumentException because "
192             + "zookeeper.znode.parent is missing leading '/'.");
193     } catch (IllegalArgumentException e) {
194       // Expected.
195     }
196 
197     try {
198       rp.addPeer(ID_ONE,
199         new ReplicationPeerConfig().setClusterKey("hostname1.example.org:1234:/"));
200       fail("Should throw an IllegalArgumentException because zookeeper.znode.parent is missing.");
201     } catch (IllegalArgumentException e) {
202       // Expected.
203     }
204 
205     try {
206       rp.addPeer(ID_ONE,
207         new ReplicationPeerConfig().setClusterKey("hostname1.example.org::/hbase"));
208       fail("Should throw an IllegalArgumentException because "
209           + "hbase.zookeeper.property.clientPort is missing.");
210     } catch (IllegalArgumentException e) {
211       // Expected.
212     }
213   }
214 
215   @Test
216   public void testHfileRefsReplicationQueues() throws ReplicationException, KeeperException {
217     rp.init();
218     rq1.init(server1);
219     rqc.init();
220 
221     List<Pair<Path, Path>> files1 = new ArrayList<>(3);
222     files1.add(new Pair<Path, Path>(null, new Path("file_1")));
223     files1.add(new Pair<Path, Path>(null, new Path("file_2")));
224     files1.add(new Pair<Path, Path>(null, new Path("file_3")));
225     assertNull(rqc.getReplicableHFiles(ID_ONE));
226     assertEquals(0, rqc.getAllPeersFromHFileRefsQueue().size());
227     rp.addPeer(ID_ONE, new ReplicationPeerConfig().setClusterKey(KEY_ONE));
228     rq1.addPeerToHFileRefs(ID_ONE);
229     rq1.addHFileRefs(ID_ONE, files1);
230     assertEquals(1, rqc.getAllPeersFromHFileRefsQueue().size());
231     assertEquals(3, rqc.getReplicableHFiles(ID_ONE).size());
232     List<String> hfiles2 = new ArrayList<>();
233     for (Pair<Path, Path> p : files1) {
234       hfiles2.add(p.getSecond().getName());
235     }
236     String removedString = hfiles2.remove(0);
237     rq1.removeHFileRefs(ID_ONE, hfiles2);
238     assertEquals(1, rqc.getReplicableHFiles(ID_ONE).size());
239     hfiles2 = new ArrayList<>(1);
240     hfiles2.add(removedString);
241     rq1.removeHFileRefs(ID_ONE, hfiles2);
242     assertEquals(0, rqc.getReplicableHFiles(ID_ONE).size());
243     rp.removePeer(ID_ONE);
244   }
245 
246   @Test
247   public void testRemovePeerForHFileRefs() throws ReplicationException, KeeperException {
248     rq1.init(server1);
249     rqc.init();
250 
251     rp.init();
252     rp.addPeer(ID_ONE, new ReplicationPeerConfig().setClusterKey(KEY_ONE));
253     rp.addPeer(ID_TWO, new ReplicationPeerConfig().setClusterKey(KEY_TWO));
254 
255     List<Pair<Path, Path>> files1 = new ArrayList<>(3);
256     files1.add(new Pair<Path, Path>(null, new Path("file_1")));
257     files1.add(new Pair<Path, Path>(null, new Path("file_2")));
258     files1.add(new Pair<Path, Path>(null, new Path("file_3")));
259     rq1.addPeerToHFileRefs(ID_ONE);
260     rq1.addHFileRefs(ID_ONE, files1);
261     rq1.addPeerToHFileRefs(ID_TWO);
262     rq1.addHFileRefs(ID_TWO, files1);
263     assertEquals(2, rqc.getAllPeersFromHFileRefsQueue().size());
264     assertEquals(3, rqc.getReplicableHFiles(ID_ONE).size());
265     assertEquals(3, rqc.getReplicableHFiles(ID_TWO).size());
266 
267     rp.removePeer(ID_ONE);
268     rq1.removePeerFromHFileRefs(ID_ONE);
269     assertEquals(1, rqc.getAllPeersFromHFileRefsQueue().size());
270     assertNull(rqc.getReplicableHFiles(ID_ONE));
271     assertEquals(3, rqc.getReplicableHFiles(ID_TWO).size());
272 
273     rp.removePeer(ID_TWO);
274     rq1.removePeerFromHFileRefs(ID_TWO);
275     assertEquals(0, rqc.getAllPeersFromHFileRefsQueue().size());
276     assertNull(rqc.getReplicableHFiles(ID_TWO));
277   }
278 
279   @Test
280   public void testReplicationPeers() throws Exception {
281     rp.init();
282 
283     // Test methods with non-existent peer ids
284     try {
285       rp.removePeer("bogus");
286       fail("Should have thrown an IllegalArgumentException when passed a bogus peerId");
287     } catch (IllegalArgumentException e) {
288     }
289     try {
290       rp.enablePeer("bogus");
291       fail("Should have thrown an IllegalArgumentException when passed a bogus peerId");
292     } catch (IllegalArgumentException e) {
293     }
294     try {
295       rp.disablePeer("bogus");
296       fail("Should have thrown an IllegalArgumentException when passed a bogus peerId");
297     } catch (IllegalArgumentException e) {
298     }
299     try {
300       rp.getStatusOfPeer("bogus");
301       fail("Should have thrown an IllegalArgumentException when passed a bogus peerId");
302     } catch (IllegalArgumentException e) {
303     }
304     assertFalse(rp.peerAdded("bogus"));
305     rp.peerRemoved("bogus");
306 
307     assertNull(rp.getPeerConf("bogus"));
308     assertNumberOfPeers(0);
309 
310     // Add some peers
311     rp.addPeer(ID_ONE, new ReplicationPeerConfig().setClusterKey(KEY_ONE));
312     assertNumberOfPeers(1);
313     rp.addPeer(ID_TWO, new ReplicationPeerConfig().setClusterKey(KEY_TWO));
314     assertNumberOfPeers(2);
315 
316     // Test methods with a peer that is added but not connected
317     try {
318       rp.getStatusOfPeer(ID_ONE);
319       fail("There are no connected peers, should have thrown an IllegalArgumentException");
320     } catch (IllegalArgumentException e) {
321     }
322     assertEquals(KEY_ONE, ZKConfig.getZooKeeperClusterKey(rp.getPeerConf(ID_ONE).getSecond()));
323     rp.removePeer(ID_ONE);
324     rp.peerRemoved(ID_ONE);
325     assertNumberOfPeers(1);
326 
327     // Add one peer
328     rp.addPeer(ID_ONE, new ReplicationPeerConfig().setClusterKey(KEY_ONE));
329     rp.peerAdded(ID_ONE);
330     assertNumberOfPeers(2);
331     assertTrue(rp.getStatusOfPeer(ID_ONE));
332     rp.disablePeer(ID_ONE);
333     assertConnectedPeerStatus(false, ID_ONE);
334     rp.enablePeer(ID_ONE);
335     assertConnectedPeerStatus(true, ID_ONE);
336 
337     // Disconnect peer
338     rp.peerRemoved(ID_ONE);
339     assertNumberOfPeers(2);
340     try {
341       rp.getStatusOfPeer(ID_ONE);
342       fail("There are no connected peers, should have thrown an IllegalArgumentException");
343     } catch (IllegalArgumentException e) {
344     }
345   }
346 
347   protected void assertConnectedPeerStatus(boolean status, String peerId) throws Exception {
348     // we can first check if the value was changed in the store, if it wasn't then fail right away
349     if (status != rp.getStatusOfPeerFromBackingStore(peerId)) {
350       fail("ConnectedPeerStatus was " + !status + " but expected " + status + " in ZK");
351     }
352     while (true) {
353       if (status == rp.getStatusOfPeer(peerId)) {
354         return;
355       }
356       if (zkTimeoutCount < ZK_MAX_COUNT) {
357         LOG.debug("ConnectedPeerStatus was " + !status + " but expected " + status
358             + ", sleeping and trying again.");
359         Thread.sleep(ZK_SLEEP_INTERVAL);
360       } else {
361         fail("Timed out waiting for ConnectedPeerStatus to be " + status);
362       }
363     }
364   }
365 
366   protected void assertNumberOfPeers(int total) {
367     assertEquals(total, rp.getAllPeerConfigs().size());
368     assertEquals(total, rp.getAllPeerIds().size());
369     assertEquals(total, rp.getAllPeerIds().size());
370   }
371 
372   /*
373    * three replicators: rq1 has 0 queues, rq2 has 1 queue with no logs, rq3 has 5 queues with 1, 2,
374    * 3, 4, 5 log files respectively
375    */
376   protected void populateQueues() throws ReplicationException {
377     rq1.addLog("trash", "trash");
378     rq1.removeQueue("trash");
379 
380     rq2.addLog("qId1", "trash");
381     rq2.removeLog("qId1", "trash");
382 
383     for (int i = 1; i < 6; i++) {
384       for (int j = 0; j < i; j++) {
385         rq3.addLog("qId" + i, "filename" + j);
386       }
387       //Add peers for the corresponding queues so they are not orphans
388       rp.addPeer("qId" + i, new ReplicationPeerConfig().setClusterKey("localhost:2818:/bogus" + i));
389     }
390   }
391 }
392