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