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.assertArrayEquals;
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertTrue;
24  import static org.junit.Assert.fail;
25  
26  import java.io.IOException;
27  import java.util.ArrayList;
28  import java.util.HashMap;
29  import java.util.List;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.apache.hadoop.conf.Configuration;
34  import org.apache.hadoop.fs.Path;
35  import org.apache.hadoop.hbase.Cell;
36  import org.apache.hadoop.hbase.CellUtil;
37  import org.apache.hadoop.hbase.HBaseTestingUtility;
38  import org.apache.hadoop.hbase.HColumnDescriptor;
39  import org.apache.hadoop.hbase.HConstants;
40  import org.apache.hadoop.hbase.HRegionInfo;
41  import org.apache.hadoop.hbase.HTableDescriptor;
42  import org.apache.hadoop.hbase.TableName;
43  import org.apache.hadoop.hbase.Waiter;
44  import org.apache.hadoop.hbase.client.Admin;
45  import org.apache.hadoop.hbase.client.Connection;
46  import org.apache.hadoop.hbase.client.ConnectionFactory;
47  import org.apache.hadoop.hbase.client.Delete;
48  import org.apache.hadoop.hbase.client.Get;
49  import org.apache.hadoop.hbase.client.HBaseAdmin;
50  import org.apache.hadoop.hbase.client.HTable;
51  import org.apache.hadoop.hbase.client.Put;
52  import org.apache.hadoop.hbase.client.Result;
53  import org.apache.hadoop.hbase.client.ResultScanner;
54  import org.apache.hadoop.hbase.client.Scan;
55  import org.apache.hadoop.hbase.client.Table;
56  import org.apache.hadoop.hbase.client.replication.ReplicationAdmin;
57  import org.apache.hadoop.hbase.mapreduce.replication.VerifyReplication;
58  import org.apache.hadoop.hbase.protobuf.generated.WALProtos;
59  import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
60  import org.apache.hadoop.hbase.replication.regionserver.Replication;
61  import org.apache.hadoop.hbase.replication.regionserver.ReplicationSource;
62  import org.apache.hadoop.hbase.replication.regionserver.ReplicationSourceInterface;
63  import org.apache.hadoop.hbase.testclassification.LargeTests;
64  import org.apache.hadoop.hbase.util.Bytes;
65  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
66  import org.apache.hadoop.hbase.util.JVMClusterUtil;
67  import org.apache.hadoop.hbase.wal.DefaultWALProvider;
68  import org.apache.hadoop.hbase.wal.WAL;
69  import org.apache.hadoop.hbase.wal.WALKey;
70  import org.apache.hadoop.mapreduce.Job;
71  import org.junit.Before;
72  import org.junit.Test;
73  import org.junit.experimental.categories.Category;
74  
75  
76  @Category(LargeTests.class)
77  public class TestReplicationSmallTests extends TestReplicationBase {
78  
79    private static final Log LOG = LogFactory.getLog(TestReplicationSmallTests.class);
80    private static final String PEER_ID = "2";
81  
82    /**
83     * @throws java.lang.Exception
84     */
85    @Before
86    public void setUp() throws Exception {
87      // Starting and stopping replication can make us miss new logs,
88      // rolling like this makes sure the most recent one gets added to the queue
89      for ( JVMClusterUtil.RegionServerThread r :
90          utility1.getHBaseCluster().getRegionServerThreads()) {
91        utility1.getHBaseAdmin().rollWALWriter(r.getRegionServer().getServerName());
92      }
93      int rowCount = utility1.countRows(tableName);
94      utility1.deleteTableData(tableName);
95      // truncating the table will send one Delete per row to the slave cluster
96      // in an async fashion, which is why we cannot just call deleteTableData on
97      // utility2 since late writes could make it to the slave in some way.
98      // Instead, we truncate the first table and wait for all the Deletes to
99      // make it to the slave.
100     Scan scan = new Scan();
101     int lastCount = 0;
102     for (int i = 0; i < NB_RETRIES; i++) {
103       if (i==NB_RETRIES-1) {
104         fail("Waited too much time for truncate");
105       }
106       ResultScanner scanner = htable2.getScanner(scan);
107       Result[] res = scanner.next(rowCount);
108       scanner.close();
109       if (res.length != 0) {
110         if (res.length < lastCount) {
111           i--; // Don't increment timeout if we make progress
112         }
113         lastCount = res.length;
114         LOG.info("Still got " + res.length + " rows");
115         Thread.sleep(SLEEP_TIME);
116       } else {
117         break;
118       }
119     }
120   }
121 
122   /**
123    * Verify that version and column delete marker types are replicated
124    * correctly.
125    * @throws Exception
126    */
127   @Test(timeout=300000)
128   public void testDeleteTypes() throws Exception {
129     LOG.info("testDeleteTypes");
130     final byte[] v1 = Bytes.toBytes("v1");
131     final byte[] v2 = Bytes.toBytes("v2");
132     final byte[] v3 = Bytes.toBytes("v3");
133     htable1 = new HTable(conf1, tableName);
134 
135     long t = EnvironmentEdgeManager.currentTime();
136     // create three versions for "row"
137     Put put = new Put(row);
138     put.add(famName, row, t, v1);
139     htable1.put(put);
140 
141     put = new Put(row);
142     put.add(famName, row, t+1, v2);
143     htable1.put(put);
144 
145     put = new Put(row);
146     put.add(famName, row, t+2, v3);
147     htable1.put(put);
148 
149     Get get = new Get(row);
150     get.setMaxVersions();
151     for (int i = 0; i < NB_RETRIES; i++) {
152       if (i==NB_RETRIES-1) {
153         fail("Waited too much time for put replication");
154       }
155       Result res = htable2.get(get);
156       if (res.size() < 3) {
157         LOG.info("Rows not available");
158         Thread.sleep(SLEEP_TIME);
159       } else {
160         assertArrayEquals(CellUtil.cloneValue(res.rawCells()[0]), v3);
161         assertArrayEquals(CellUtil.cloneValue(res.rawCells()[1]), v2);
162         assertArrayEquals(CellUtil.cloneValue(res.rawCells()[2]), v1);
163         break;
164       }
165     }
166     // place a version delete marker (delete last version)
167     Delete d = new Delete(row);
168     d.deleteColumn(famName, row, t);
169     htable1.delete(d);
170 
171     get = new Get(row);
172     get.setMaxVersions();
173     for (int i = 0; i < NB_RETRIES; i++) {
174       if (i==NB_RETRIES-1) {
175         fail("Waited too much time for put replication");
176       }
177       Result res = htable2.get(get);
178       if (res.size() > 2) {
179         LOG.info("Version not deleted");
180         Thread.sleep(SLEEP_TIME);
181       } else {
182         assertArrayEquals(CellUtil.cloneValue(res.rawCells()[0]), v3);
183         assertArrayEquals(CellUtil.cloneValue(res.rawCells()[1]), v2);
184         break;
185       }
186     }
187 
188     // place a column delete marker
189     d = new Delete(row);
190     d.deleteColumns(famName, row, t+2);
191     htable1.delete(d);
192 
193     // now *both* of the remaining version should be deleted
194     // at the replica
195     get = new Get(row);
196     for (int i = 0; i < NB_RETRIES; i++) {
197       if (i==NB_RETRIES-1) {
198         fail("Waited too much time for del replication");
199       }
200       Result res = htable2.get(get);
201       if (res.size() >= 1) {
202         LOG.info("Rows not deleted");
203         Thread.sleep(SLEEP_TIME);
204       } else {
205         break;
206       }
207     }
208   }
209 
210   /**
211    * Add a row, check it's replicated, delete it, check's gone
212    * @throws Exception
213    */
214   @Test(timeout=300000)
215   public void testSimplePutDelete() throws Exception {
216     LOG.info("testSimplePutDelete");
217     Put put = new Put(row);
218     put.add(famName, row, row);
219 
220     htable1 = new HTable(conf1, tableName);
221     htable1.put(put);
222 
223     Get get = new Get(row);
224     for (int i = 0; i < NB_RETRIES; i++) {
225       if (i==NB_RETRIES-1) {
226         fail("Waited too much time for put replication");
227       }
228       Result res = htable2.get(get);
229       if (res.size() == 0) {
230         LOG.info("Row not available");
231         Thread.sleep(SLEEP_TIME);
232       } else {
233         assertArrayEquals(res.value(), row);
234         break;
235       }
236     }
237 
238     Delete del = new Delete(row);
239     htable1.delete(del);
240 
241     get = new Get(row);
242     for (int i = 0; i < NB_RETRIES; i++) {
243       if (i==NB_RETRIES-1) {
244         fail("Waited too much time for del replication");
245       }
246       Result res = htable2.get(get);
247       if (res.size() >= 1) {
248         LOG.info("Row not deleted");
249         Thread.sleep(SLEEP_TIME);
250       } else {
251         break;
252       }
253     }
254   }
255 
256   /**
257    * Try a small batch upload using the write buffer, check it's replicated
258    * @throws Exception
259    */
260   @Test(timeout=300000)
261   public void testSmallBatch() throws Exception {
262     LOG.info("testSmallBatch");
263     // normal Batch tests
264     loadData("", row);
265  
266     Scan scan = new Scan();
267 
268     ResultScanner scanner1 = htable1.getScanner(scan);
269     Result[] res1 = scanner1.next(NB_ROWS_IN_BATCH);
270     scanner1.close();
271     assertEquals(NB_ROWS_IN_BATCH, res1.length);
272 
273     waitForReplication(NB_ROWS_IN_BATCH, NB_RETRIES);
274   }
275 
276   private void waitForReplication(int expectedRows, int retries) throws IOException, InterruptedException {
277     Scan scan;
278     for (int i = 0; i < retries; i++) {
279       scan = new Scan();
280       if (i== retries -1) {
281         fail("Waited too much time for normal batch replication");
282       }
283       ResultScanner scanner = htable2.getScanner(scan);
284       Result[] res = scanner.next(expectedRows);
285       scanner.close();
286       if (res.length != expectedRows) {
287         LOG.info("Only got " + res.length + " rows");
288         Thread.sleep(SLEEP_TIME);
289       } else {
290         break;
291       }
292     }
293   }
294 
295   private void loadData(String prefix, byte[] row) throws IOException {
296     List<Put> puts = new ArrayList<>();
297     for (int i = 0; i < NB_ROWS_IN_BATCH; i++) {
298       Put put = new Put(Bytes.toBytes(prefix + Integer.toString(i)));
299       put.addColumn(famName, row, row);
300       puts.add(put);
301     }
302     htable1.put(puts);
303   }
304 
305   /**
306    * Test disable/enable replication, trying to insert, make sure nothing's
307    * replicated, enable it, the insert should be replicated
308    *
309    * @throws Exception
310    */
311   @Test(timeout = 300000)
312   public void testDisableEnable() throws Exception {
313 
314     // Test disabling replication
315     admin.disablePeer(PEER_ID);
316 
317     byte[] rowkey = Bytes.toBytes("disable enable");
318     Put put = new Put(rowkey);
319     put.add(famName, row, row);
320     htable1.put(put);
321 
322     Get get = new Get(rowkey);
323     for (int i = 0; i < NB_RETRIES; i++) {
324       Result res = htable2.get(get);
325       if (res.size() >= 1) {
326         fail("Replication wasn't disabled");
327       } else {
328         LOG.info("Row not replicated, let's wait a bit more...");
329         Thread.sleep(SLEEP_TIME);
330       }
331     }
332 
333     // Test enable replication
334     admin.enablePeer(PEER_ID);
335 
336     for (int i = 0; i < NB_RETRIES; i++) {
337       Result res = htable2.get(get);
338       if (res.size() == 0) {
339         LOG.info("Row not available");
340         Thread.sleep(SLEEP_TIME);
341       } else {
342         assertArrayEquals(res.value(), row);
343         return;
344       }
345     }
346     fail("Waited too much time for put replication");
347   }
348 
349   /**
350    * Integration test for TestReplicationAdmin, removes and re-add a peer
351    * cluster
352    *
353    * @throws Exception
354    */
355   @Test(timeout=300000)
356   public void testAddAndRemoveClusters() throws Exception {
357     LOG.info("testAddAndRemoveClusters");
358     admin.removePeer(PEER_ID);
359     Thread.sleep(SLEEP_TIME);
360     byte[] rowKey = Bytes.toBytes("Won't be replicated");
361     Put put = new Put(rowKey);
362     put.add(famName, row, row);
363     htable1.put(put);
364 
365     Get get = new Get(rowKey);
366     for (int i = 0; i < NB_RETRIES; i++) {
367       if (i == NB_RETRIES-1) {
368         break;
369       }
370       Result res = htable2.get(get);
371       if (res.size() >= 1) {
372         fail("Not supposed to be replicated");
373       } else {
374         LOG.info("Row not replicated, let's wait a bit more...");
375         Thread.sleep(SLEEP_TIME);
376       }
377     }
378 
379     ReplicationPeerConfig rpc = new ReplicationPeerConfig();
380     rpc.setClusterKey(utility2.getClusterKey());
381     admin.addPeer("2", rpc);
382     Thread.sleep(SLEEP_TIME);
383     rowKey = Bytes.toBytes("do rep");
384     put = new Put(rowKey);
385     put.add(famName, row, row);
386     LOG.info("Adding new row");
387     htable1.put(put);
388 
389     get = new Get(rowKey);
390     for (int i = 0; i < NB_RETRIES; i++) {
391       if (i==NB_RETRIES-1) {
392         fail("Waited too much time for put replication");
393       }
394       Result res = htable2.get(get);
395       if (res.size() == 0) {
396         LOG.info("Row not available");
397         Thread.sleep(SLEEP_TIME*i);
398       } else {
399         assertArrayEquals(res.value(), row);
400         break;
401       }
402     }
403   }
404 
405 
406   /**
407    * Do a more intense version testSmallBatch, one  that will trigger
408    * wal rolling and other non-trivial code paths
409    * @throws Exception
410    */
411   @Test(timeout=300000)
412   public void testLoading() throws Exception {
413     LOG.info("Writing out rows to table1 in testLoading");
414     List<Put> puts = new ArrayList<Put>();
415     for (int i = 0; i < NB_ROWS_IN_BIG_BATCH; i++) {
416       Put put = new Put(Bytes.toBytes(i));
417       put.add(famName, row, row);
418       puts.add(put);
419     }
420     htable1.setWriteBufferSize(1024);
421     // The puts will be iterated through and flushed only when the buffer
422     // size is reached.
423     htable1.put(puts);
424 
425     Scan scan = new Scan();
426 
427     ResultScanner scanner = htable1.getScanner(scan);
428     Result[] res = scanner.next(NB_ROWS_IN_BIG_BATCH);
429     scanner.close();
430 
431     assertEquals(NB_ROWS_IN_BIG_BATCH, res.length);
432 
433     LOG.info("Looking in table2 for replicated rows in testLoading");
434     long start = System.currentTimeMillis();
435     // Retry more than NB_RETRIES.  As it was, retries were done in 5 seconds and we'd fail
436     // sometimes.
437     final long retries = NB_RETRIES * 10;
438     for (int i = 0; i < retries; i++) {
439       scan = new Scan();
440       scanner = htable2.getScanner(scan);
441       res = scanner.next(NB_ROWS_IN_BIG_BATCH);
442       scanner.close();
443       if (res.length != NB_ROWS_IN_BIG_BATCH) {
444         if (i == retries - 1) {
445           int lastRow = -1;
446           for (Result result : res) {
447             int currentRow = Bytes.toInt(result.getRow());
448             for (int row = lastRow+1; row < currentRow; row++) {
449               LOG.error("Row missing: " + row);
450             }
451             lastRow = currentRow;
452           }
453           LOG.error("Last row: " + lastRow);
454           fail("Waited too much time for normal batch replication, " +
455             res.length + " instead of " + NB_ROWS_IN_BIG_BATCH + "; waited=" +
456             (System.currentTimeMillis() - start) + "ms");
457         } else {
458           LOG.info("Only got " + res.length + " rows... retrying");
459           Thread.sleep(SLEEP_TIME);
460         }
461       } else {
462         break;
463       }
464     }
465   }
466 
467   /**
468    * Do a small loading into a table, make sure the data is really the same,
469    * then run the VerifyReplication job to check the results. Do a second
470    * comparison where all the cells are different.
471    * @throws Exception
472    */
473   @Test(timeout=300000)
474   public void testVerifyRepJob() throws Exception {
475     // Populate the tables, at the same time it guarantees that the tables are
476     // identical since it does the check
477     testSmallBatch();
478 
479     String[] args = new String[] {PEER_ID, tableName.getNameAsString()};
480     runVerifyReplication(args, NB_ROWS_IN_BATCH, 0);
481 
482     Scan scan = new Scan();
483     ResultScanner rs = htable2.getScanner(scan);
484     Put put = null;
485     for (Result result : rs) {
486       put = new Put(result.getRow());
487       Cell firstVal = result.rawCells()[0];
488       put.add(CellUtil.cloneFamily(firstVal),
489           CellUtil.cloneQualifier(firstVal), Bytes.toBytes("diff data"));
490       htable2.put(put);
491     }
492     Delete delete = new Delete(put.getRow());
493     htable2.delete(delete);
494     runVerifyReplication(args, 0, NB_ROWS_IN_BATCH);
495   }
496 
497   /**
498    * Load a row into a table, make sure the data is really the same,
499    * delete the row, make sure the delete marker is replicated,
500    * run verify replication with and without raw to check the results.
501    * @throws Exception
502    */
503   @Test(timeout=300000)
504   public void testVerifyRepJobWithRawOptions() throws Exception {
505     LOG.info("testVerifyRepJobWithRawOptions");
506 
507     TableName tablename = TableName.valueOf("test_raw");
508     byte[] familyname = Bytes.toBytes("fam_raw");
509     byte[] row = Bytes.toBytes("row_raw");
510 
511     Table lHtable1 = null;
512     Table lHtable2 = null;
513 
514     try {
515       HTableDescriptor table = new HTableDescriptor(tablename);
516       HColumnDescriptor fam = new HColumnDescriptor(familyname);
517       fam.setMaxVersions(100);
518       fam.setScope(HConstants.REPLICATION_SCOPE_GLOBAL);
519       table.addFamily(fam);
520 
521       try (Admin admin1 =  utility1.getConnection().getAdmin()) {
522         admin1.createTable(table, HBaseTestingUtility.KEYS_FOR_HBA_CREATE_TABLE);
523       }
524       try (Admin admin2 = utility2.getConnection().getAdmin()) {
525         admin2.createTable(table, HBaseTestingUtility.KEYS_FOR_HBA_CREATE_TABLE);
526       }
527       utility1.waitUntilAllRegionsAssigned(tablename);
528       utility2.waitUntilAllRegionsAssigned(tablename);
529 
530       lHtable1 = utility1.getConnection().getTable(tablename);
531       lHtable2 = utility2.getConnection().getTable(tablename);
532 
533       Put put = new Put(row);
534       put.addColumn(familyname, row, row);
535       lHtable1.put(put);
536 
537       Get get = new Get(row);
538       for (int i = 0; i < NB_RETRIES; i++) {
539         if (i==NB_RETRIES-1) {
540           fail("Waited too much time for put replication");
541         }
542         Result res = lHtable2.get(get);
543         if (res.size() == 0) {
544           LOG.info("Row not available");
545           Thread.sleep(SLEEP_TIME);
546         } else {
547           assertArrayEquals(res.value(), row);
548           break;
549         }
550       }
551 
552       Delete del = new Delete(row);
553       lHtable1.delete(del);
554 
555       get = new Get(row);
556       for (int i = 0; i < NB_RETRIES; i++) {
557         if (i==NB_RETRIES-1) {
558           fail("Waited too much time for del replication");
559         }
560         Result res = lHtable2.get(get);
561         if (res.size() >= 1) {
562           LOG.info("Row not deleted");
563           Thread.sleep(SLEEP_TIME);
564         } else {
565           break;
566         }
567       }
568 
569       // Checking verifyReplication for the default behavior.
570       String[] argsWithoutRaw = new String[] {PEER_ID, tablename.getNameAsString()};
571       runVerifyReplication(argsWithoutRaw, 0, 0);
572 
573       // Checking verifyReplication with raw
574       String[] argsWithRawAsTrue = new String[] {"--raw", PEER_ID, tablename.getNameAsString()};
575       runVerifyReplication(argsWithRawAsTrue, 1, 0);
576     } finally {
577       if (lHtable1 != null) {
578         lHtable1.close();
579       }
580       if (lHtable2 != null) {
581         lHtable2.close();
582       }
583     }
584   }
585 
586   private void runVerifyReplication(String[] args, int expectedGoodRows, int expectedBadRows)
587       throws IOException, InterruptedException, ClassNotFoundException {
588     Job job = VerifyReplication.createSubmittableJob(new Configuration(CONF_WITH_LOCALFS), args);
589     if (job == null) {
590       fail("Job wasn't created, see the log");
591     }
592     if (!job.waitForCompletion(true)) {
593       fail("Job failed, see the log");
594     }
595     assertEquals(expectedGoodRows, job.getCounters().
596         findCounter(VerifyReplication.Verifier.Counters.GOODROWS).getValue());
597     assertEquals(expectedBadRows, job.getCounters().
598         findCounter(VerifyReplication.Verifier.Counters.BADROWS).getValue());
599   }
600 
601   @Test(timeout=300000)
602   // VerifyReplication should honor versions option
603   public void testHBase14905() throws Exception {
604     // normal Batch tests
605     byte[] qualifierName = Bytes.toBytes("f1");
606     Put put = new Put(Bytes.toBytes("r1"));
607     put.addColumn(famName, qualifierName, Bytes.toBytes("v1002"));
608     htable1.put(put);
609     put.addColumn(famName, qualifierName, Bytes.toBytes("v1001"));
610     htable1.put(put);
611     put.addColumn(famName, qualifierName, Bytes.toBytes("v1112"));
612     htable1.put(put);
613 
614     Scan scan = new Scan();
615     scan.setMaxVersions(100);
616     ResultScanner scanner1 = htable1.getScanner(scan);
617     Result[] res1 = scanner1.next(1);
618     scanner1.close();
619 
620     assertEquals(1, res1.length);
621     assertEquals(3, res1[0].getColumnCells(famName, qualifierName).size());
622 
623     for (int i = 0; i < NB_RETRIES; i++) {
624       scan = new Scan();
625       scan.setMaxVersions(100);
626       scanner1 = htable2.getScanner(scan);
627       res1 = scanner1.next(1);
628       scanner1.close();
629       if (res1.length != 1) {
630         LOG.info("Only got " + res1.length + " rows");
631         Thread.sleep(SLEEP_TIME);
632       } else {
633         int cellNumber = res1[0].getColumnCells(famName, Bytes.toBytes("f1")).size();
634         if (cellNumber != 3) {
635           LOG.info("Only got " + cellNumber + " cells");
636           Thread.sleep(SLEEP_TIME);
637         } else {
638           break;
639         }
640       }
641       if (i == NB_RETRIES-1) {
642         fail("Waited too much time for normal batch replication");
643       }
644     }
645 
646     put.addColumn(famName, qualifierName, Bytes.toBytes("v1111"));
647     htable2.put(put);
648     put.addColumn(famName, qualifierName, Bytes.toBytes("v1112"));
649     htable2.put(put);
650 
651     scan = new Scan();
652     scan.setMaxVersions(100);
653     scanner1 = htable2.getScanner(scan);
654     res1 = scanner1.next(NB_ROWS_IN_BATCH);
655     scanner1.close();
656 
657     assertEquals(1, res1.length);
658     assertEquals(5, res1[0].getColumnCells(famName, qualifierName).size());
659 
660     String[] args = new String[] {"--versions=100", PEER_ID, tableName.getNameAsString()};
661     runVerifyReplication(args, 0, 1);
662   }
663 
664   @Test(timeout=300000)
665   // VerifyReplication should honor versions option
666   public void testVersionMismatchHBase14905() throws Exception {
667     // normal Batch tests
668     byte[] qualifierName = Bytes.toBytes("f1");
669     Put put = new Put(Bytes.toBytes("r1"));
670     long ts = System.currentTimeMillis();
671     put.addColumn(famName, qualifierName, ts + 1, Bytes.toBytes("v1"));
672     htable1.put(put);
673     put.addColumn(famName, qualifierName, ts + 2, Bytes.toBytes("v2"));
674     htable1.put(put);
675     put.addColumn(famName, qualifierName, ts + 3, Bytes.toBytes("v3"));
676     htable1.put(put);
677        
678     Scan scan = new Scan();
679     scan.setMaxVersions(100);
680     ResultScanner scanner1 = htable1.getScanner(scan);
681     Result[] res1 = scanner1.next(1);
682     scanner1.close();
683 
684     assertEquals(1, res1.length);
685     assertEquals(3, res1[0].getColumnCells(famName, qualifierName).size());
686     
687     for (int i = 0; i < NB_RETRIES; i++) {
688       scan = new Scan();
689       scan.setMaxVersions(100);
690       scanner1 = htable2.getScanner(scan);
691       res1 = scanner1.next(1);
692       scanner1.close();
693       if (res1.length != 1) {
694         LOG.info("Only got " + res1.length + " rows");
695         Thread.sleep(SLEEP_TIME);
696       } else {
697         int cellNumber = res1[0].getColumnCells(famName, Bytes.toBytes("f1")).size();
698         if (cellNumber != 3) {
699           LOG.info("Only got " + cellNumber + " cells");
700           Thread.sleep(SLEEP_TIME);
701         } else {
702           break;
703         }
704       }
705       if (i == NB_RETRIES-1) {
706         fail("Waited too much time for normal batch replication");
707       }
708     }
709    
710     try {
711       // Disabling replication and modifying the particular version of the cell to validate the feature.  
712       admin.disablePeer(PEER_ID);
713       Put put2 = new Put(Bytes.toBytes("r1"));
714       put2.addColumn(famName, qualifierName, ts +2, Bytes.toBytes("v99"));
715       htable2.put(put2);
716       
717       scan = new Scan();
718       scan.setMaxVersions(100);
719       scanner1 = htable2.getScanner(scan);
720       res1 = scanner1.next(NB_ROWS_IN_BATCH);
721       scanner1.close();
722       assertEquals(1, res1.length);
723       assertEquals(3, res1[0].getColumnCells(famName, qualifierName).size());
724     
725       String[] args = new String[] {"--versions=100", PEER_ID, tableName.getNameAsString()};
726       runVerifyReplication(args, 0, 1);
727       }
728     finally {
729       admin.enablePeer(PEER_ID);
730     }
731   }
732 
733   /**
734    * Test for HBASE-9038, Replication.scopeWALEdits would NPE if it wasn't filtering out
735    * the compaction WALEdit
736    * @throws Exception
737    */
738   @Test(timeout=300000)
739   public void testCompactionWALEdits() throws Exception {
740     WALProtos.CompactionDescriptor compactionDescriptor =
741         WALProtos.CompactionDescriptor.getDefaultInstance();
742     HRegionInfo hri = new HRegionInfo(htable1.getName(),
743       HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
744     WALEdit edit = WALEdit.createCompaction(hri, compactionDescriptor);
745     Replication.scopeWALEdits(htable1.getTableDescriptor(), new WALKey(), edit,
746       htable1.getConfiguration(), null);
747   }
748 
749   /**
750    * Test for HBASE-8663
751    * Create two new Tables with colfamilies enabled for replication then run
752    * ReplicationAdmin.listReplicated(). Finally verify the table:colfamilies. Note:
753    * TestReplicationAdmin is a better place for this testing but it would need mocks.
754    * @throws Exception
755    */
756   @Test(timeout = 300000)
757   public void testVerifyListReplicatedTable() throws Exception {
758     LOG.info("testVerifyListReplicatedTable");
759 
760     final String tName = "VerifyListReplicated_";
761     final String colFam = "cf1";
762     final int numOfTables = 3;
763 
764     HBaseAdmin hadmin = new HBaseAdmin(conf1);
765 
766     // Create Tables
767     for (int i = 0; i < numOfTables; i++) {
768       HTableDescriptor ht = new HTableDescriptor(TableName.valueOf(tName + i));
769       HColumnDescriptor cfd = new HColumnDescriptor(colFam);
770       cfd.setScope(HConstants.REPLICATION_SCOPE_GLOBAL);
771       ht.addFamily(cfd);
772       hadmin.createTable(ht);
773     }
774 
775     // verify the result
776     List<HashMap<String, String>> replicationColFams = admin.listReplicated();
777     int[] match = new int[numOfTables]; // array of 3 with init value of zero
778 
779     for (int i = 0; i < replicationColFams.size(); i++) {
780       HashMap<String, String> replicationEntry = replicationColFams.get(i);
781       String tn = replicationEntry.get(ReplicationAdmin.TNAME);
782       if ((tn.startsWith(tName)) && replicationEntry.get(ReplicationAdmin.CFNAME).equals(colFam)) {
783         int m = Integer.parseInt(tn.substring(tn.length() - 1)); // get the last digit
784         match[m]++; // should only increase once
785       }
786     }
787 
788     // check the matching result
789     for (int i = 0; i < match.length; i++) {
790       assertTrue("listReplicated() does not match table " + i, (match[i] == 1));
791     }
792 
793     // drop tables
794     for (int i = 0; i < numOfTables; i++) {
795       String ht = tName + i;
796       hadmin.disableTable(ht);
797       hadmin.deleteTable(ht);
798     }
799 
800     hadmin.close();
801   }
802 
803   @Test(timeout=300000)
804   public void testVerifyReplicationPrefixFiltering() throws Exception {
805     final byte[] prefixRow = Bytes.toBytes("prefixrow");
806     final byte[] prefixRow2 = Bytes.toBytes("secondrow");
807     loadData("prefixrow", prefixRow);
808     loadData("secondrow", prefixRow2);
809     loadData("aaa", row);
810     loadData("zzz", row);
811     waitForReplication(NB_ROWS_IN_BATCH * 4, NB_RETRIES * 4);
812     String[] args = new String[] {"--row-prefixes=prefixrow,secondrow", PEER_ID,
813         tableName.getNameAsString()};
814     runVerifyReplication(args, NB_ROWS_IN_BATCH *2, 0);
815   }
816 
817   @Test
818   public void testEmptyWALRecovery() throws Exception {
819     final int numRs = utility1.getHBaseCluster().getRegionServerThreads().size();
820 
821     // for each RS, create an empty wal with same walGroupId
822     final List<Path> emptyWalPaths = new ArrayList<>();
823     long ts = System.currentTimeMillis();
824     for (int i = 0; i < numRs; i++) {
825       HRegionInfo regionInfo =
826           utility1.getHBaseCluster().getRegions(htable1.getName()).get(0).getRegionInfo();
827       WAL wal = utility1.getHBaseCluster().getRegionServer(i).getWAL(regionInfo);
828       Path currentWalPath = DefaultWALProvider.getCurrentFileName(wal);
829       String walGroupId = DefaultWALProvider.getWALPrefixFromWALName(currentWalPath.getName());
830       Path emptyWalPath = new Path(utility1.getDataTestDir(), walGroupId + "." + ts);
831       utility1.getTestFileSystem().create(emptyWalPath).close();
832       emptyWalPaths.add(emptyWalPath);
833     }
834 
835     // inject our empty wal into the replication queue
836     for (int i = 0; i < numRs; i++) {
837       Replication replicationService =
838           (Replication) utility1.getHBaseCluster().getRegionServer(i).getReplicationSourceService();
839       replicationService.preLogRoll(null, emptyWalPaths.get(i));
840       replicationService.postLogRoll(null, emptyWalPaths.get(i));
841     }
842 
843     // wait for ReplicationSource to start reading from our empty wal
844     waitForLogAdvance(numRs, emptyWalPaths, false);
845 
846     // roll the original wal, which enqueues a new wal behind our empty wal
847     for (int i = 0; i < numRs; i++) {
848       HRegionInfo regionInfo =
849           utility1.getHBaseCluster().getRegions(htable1.getName()).get(0).getRegionInfo();
850       WAL wal = utility1.getHBaseCluster().getRegionServer(i).getWAL(regionInfo);
851       wal.rollWriter(true);
852     }
853 
854     // ReplicationSource should advance past the empty wal, or else the test will fail
855     waitForLogAdvance(numRs, emptyWalPaths, true);
856 
857     // we're now writing to the new wal
858     // if everything works, the source should've stopped reading from the empty wal, and start
859     // replicating from the new wal
860     testSimplePutDelete();
861   }
862 
863   /**
864    * Waits for the ReplicationSource to start reading from the given paths
865    * @param numRs number of regionservers
866    * @param emptyWalPaths path for each regionserver
867    * @param invert if true, waits until ReplicationSource is NOT reading from the given paths
868    */
869   private void waitForLogAdvance(final int numRs, final List<Path> emptyWalPaths,
870       final boolean invert) throws Exception {
871     Waiter.waitFor(conf1, 10000, new Waiter.Predicate<Exception>() {
872       @Override
873       public boolean evaluate() throws Exception {
874         for (int i = 0; i < numRs; i++) {
875           Replication replicationService = (Replication) utility1.getHBaseCluster()
876               .getRegionServer(i).getReplicationSourceService();
877           for (ReplicationSourceInterface rsi : replicationService.getReplicationManager()
878               .getSources()) {
879             ReplicationSource source = (ReplicationSource) rsi;
880             if (!invert && !emptyWalPaths.get(i).equals(source.getCurrentPath())) {
881               return false;
882             }
883             if (invert && emptyWalPaths.get(i).equals(source.getCurrentPath())) {
884               return false;
885             }
886           }
887         }
888         return true;
889       }
890     });
891   }
892 }