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  package org.apache.hadoop.hbase.snapshot;
19  
20  import static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.assertFalse;
22  import static org.junit.Assert.assertTrue;
23  
24  import java.io.IOException;
25  import java.util.Collections;
26  import java.util.ArrayList;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  import java.util.TreeSet;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.hadoop.conf.Configuration;
36  import org.apache.hadoop.fs.FSDataOutputStream;
37  import org.apache.hadoop.fs.FileSystem;
38  import org.apache.hadoop.fs.Path;
39  import org.apache.hadoop.hbase.HBaseTestingUtility;
40  import org.apache.hadoop.hbase.HColumnDescriptor;
41  import org.apache.hadoop.hbase.HConstants;
42  import org.apache.hadoop.hbase.HRegionInfo;
43  import org.apache.hadoop.hbase.HTableDescriptor;
44  import org.apache.hadoop.hbase.TableName;
45  import org.apache.hadoop.hbase.TableNotEnabledException;
46  import org.apache.hadoop.hbase.client.Admin;
47  import org.apache.hadoop.hbase.client.BufferedMutator;
48  import org.apache.hadoop.hbase.client.Durability;
49  import org.apache.hadoop.hbase.client.HTable;
50  import org.apache.hadoop.hbase.client.Put;
51  import org.apache.hadoop.hbase.client.Table;
52  import org.apache.hadoop.hbase.errorhandling.ForeignExceptionDispatcher;
53  import org.apache.hadoop.hbase.client.RegionReplicaUtil;
54  import org.apache.hadoop.hbase.io.HFileLink;
55  import org.apache.hadoop.hbase.master.HMaster;
56  import org.apache.hadoop.hbase.master.MasterFileSystem;
57  import org.apache.hadoop.hbase.mob.MobUtils;
58  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
59  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
60  import org.apache.hadoop.hbase.protobuf.generated.SnapshotProtos.SnapshotRegionManifest;
61  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneRequest;
62  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneResponse;
63  import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
64  import org.apache.hadoop.hbase.regionserver.HRegionServer;
65  import org.apache.hadoop.hbase.regionserver.Region;
66  import org.apache.hadoop.hbase.util.Bytes;
67  import org.apache.hadoop.hbase.util.FSTableDescriptors;
68  import org.apache.hadoop.hbase.util.FSVisitor;
69  import org.apache.hadoop.hbase.util.FSUtils;
70  import org.apache.hadoop.hbase.util.MD5Hash;
71  import org.junit.Assert;
72  
73  import com.google.protobuf.ServiceException;
74  
75  /**
76   * Utilities class for snapshots
77   */
78  public class SnapshotTestingUtils {
79  
80    private static final Log LOG = LogFactory.getLog(SnapshotTestingUtils.class);
81    private static byte[] KEYS = Bytes.toBytes("0123456789");
82  
83    /**
84     * Assert that we don't have any snapshots lists
85     *
86     * @throws IOException
87     *           if the admin operation fails
88     */
89    public static void assertNoSnapshots(Admin admin) throws IOException {
90      assertEquals("Have some previous snapshots", 0, admin.listSnapshots()
91          .size());
92    }
93  
94    /**
95     * Make sure that there is only one snapshot returned from the master and its
96     * name and table match the passed in parameters.
97     */
98    public static List<SnapshotDescription> assertExistsMatchingSnapshot(
99        Admin admin, String snapshotName, TableName tableName)
100       throws IOException {
101     // list the snapshot
102     List<SnapshotDescription> snapshots = admin.listSnapshots();
103 
104     List<SnapshotDescription> returnedSnapshots = new ArrayList<SnapshotDescription>();
105     for (SnapshotDescription sd : snapshots) {
106       if (snapshotName.equals(sd.getName()) &&
107           tableName.equals(TableName.valueOf(sd.getTable()))) {
108         returnedSnapshots.add(sd);
109       }
110     }
111 
112     Assert.assertTrue("No matching snapshots found.", returnedSnapshots.size()>0);
113     return returnedSnapshots;
114   }
115 
116   /**
117    * Make sure that there is only one snapshot returned from the master
118    */
119   public static void assertOneSnapshotThatMatches(Admin admin,
120       SnapshotDescription snapshot) throws IOException {
121     assertOneSnapshotThatMatches(admin, snapshot.getName(),
122         TableName.valueOf(snapshot.getTable()));
123   }
124 
125   /**
126    * Make sure that there is only one snapshot returned from the master and its
127    * name and table match the passed in parameters.
128    */
129   public static List<SnapshotDescription> assertOneSnapshotThatMatches(
130       Admin admin, String snapshotName, TableName tableName)
131       throws IOException {
132     // list the snapshot
133     List<SnapshotDescription> snapshots = admin.listSnapshots();
134 
135     assertEquals("Should only have 1 snapshot", 1, snapshots.size());
136     assertEquals(snapshotName, snapshots.get(0).getName());
137     assertEquals(tableName, TableName.valueOf(snapshots.get(0).getTable()));
138 
139     return snapshots;
140   }
141 
142   /**
143    * Make sure that there is only one snapshot returned from the master and its
144    * name and table match the passed in parameters.
145    */
146   public static List<SnapshotDescription> assertOneSnapshotThatMatches(
147       Admin admin, byte[] snapshot, TableName tableName) throws IOException {
148     return assertOneSnapshotThatMatches(admin, Bytes.toString(snapshot),
149         tableName);
150   }
151 
152   public static void confirmSnapshotValid(HBaseTestingUtility testUtil,
153       SnapshotDescription snapshotDescriptor, TableName tableName, byte[] family)
154       throws IOException {
155     MasterFileSystem mfs = testUtil.getHBaseCluster().getMaster().getMasterFileSystem();
156     confirmSnapshotValid(snapshotDescriptor, tableName, family,
157         mfs.getRootDir(), testUtil.getHBaseAdmin(), mfs.getFileSystem());
158   }
159 
160   /**
161    * Confirm that the snapshot contains references to all the files that should
162    * be in the snapshot.
163    */
164   public static void confirmSnapshotValid(
165       SnapshotDescription snapshotDescriptor, TableName tableName,
166       byte[] testFamily, Path rootDir, Admin admin, FileSystem fs)
167       throws IOException {
168     ArrayList nonEmptyTestFamilies = new ArrayList(1);
169     nonEmptyTestFamilies.add(testFamily);
170     confirmSnapshotValid(snapshotDescriptor, tableName,
171       nonEmptyTestFamilies, null, rootDir, admin, fs);
172   }
173 
174   /**
175    * Confirm that the snapshot has no references files but only metadata.
176    */
177   public static void confirmEmptySnapshotValid(
178       SnapshotDescription snapshotDescriptor, TableName tableName,
179       byte[] testFamily, Path rootDir, Admin admin, FileSystem fs)
180       throws IOException {
181     ArrayList emptyTestFamilies = new ArrayList(1);
182     emptyTestFamilies.add(testFamily);
183     confirmSnapshotValid(snapshotDescriptor, tableName,
184       null, emptyTestFamilies, rootDir, admin, fs);
185   }
186 
187   /**
188    * Confirm that the snapshot contains references to all the files that should
189    * be in the snapshot. This method also perform some redundant check like
190    * the existence of the snapshotinfo or the regioninfo which are done always
191    * by the MasterSnapshotVerifier, at the end of the snapshot operation.
192    */
193   public static void confirmSnapshotValid(
194       SnapshotDescription snapshotDescriptor, TableName tableName,
195       List<byte[]> nonEmptyTestFamilies, List<byte[]> emptyTestFamilies,
196       Path rootDir, Admin admin, FileSystem fs) throws IOException {
197     final Configuration conf = admin.getConfiguration();
198 
199     // check snapshot dir
200     Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(
201         snapshotDescriptor, rootDir);
202     assertTrue(fs.exists(snapshotDir));
203 
204     SnapshotDescription desc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);
205 
206     // Extract regions and families with store files
207     final Set<byte[]> snapshotFamilies = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
208 
209     SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, desc);
210     Map<String, SnapshotRegionManifest> regionManifests = manifest.getRegionManifestsMap();
211     for (SnapshotRegionManifest regionManifest: regionManifests.values()) {
212       SnapshotReferenceUtil.visitRegionStoreFiles(regionManifest,
213           new SnapshotReferenceUtil.StoreFileVisitor() {
214         @Override
215         public void storeFile(final HRegionInfo regionInfo, final String family,
216               final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
217           snapshotFamilies.add(Bytes.toBytes(family));
218         }
219       });
220     }
221 
222     // Verify that there are store files in the specified families
223     if (nonEmptyTestFamilies != null) {
224       for (final byte[] familyName: nonEmptyTestFamilies) {
225         assertTrue(snapshotFamilies.contains(familyName));
226       }
227     }
228 
229     // Verify that there are no store files in the specified families
230     if (emptyTestFamilies != null) {
231       for (final byte[] familyName: emptyTestFamilies) {
232         assertFalse(snapshotFamilies.contains(familyName));
233       }
234     }
235 
236     // check the region snapshot for all the regions
237     List<HRegionInfo> regions = admin.getTableRegions(tableName);
238     // remove the non-default regions
239     RegionReplicaUtil.removeNonDefaultRegions(regions);
240     boolean hasMob = regionManifests.containsKey(MobUtils.getMobRegionInfo(tableName)
241         .getEncodedName());
242     if (hasMob) {
243       assertEquals(regions.size(), regionManifests.size() - 1);
244     } else {
245       assertEquals(regions.size(), regionManifests.size());
246     }
247 
248     // Verify Regions (redundant check, see MasterSnapshotVerifier)
249     for (HRegionInfo info : regions) {
250       String regionName = info.getEncodedName();
251       assertTrue(regionManifests.containsKey(regionName));
252     }
253   }
254 
255   /**
256    * Helper method for testing async snapshot operations. Just waits for the
257    * given snapshot to complete on the server by repeatedly checking the master.
258    *
259    * @param master: the master running the snapshot
260    * @param snapshot: the snapshot to check
261    * @param sleep: amount to sleep between checks to see if the snapshot is done
262    * @throws ServiceException if the snapshot fails
263    */
264   public static void waitForSnapshotToComplete(HMaster master,
265       SnapshotDescription snapshot, long sleep) throws ServiceException {
266     final IsSnapshotDoneRequest request = IsSnapshotDoneRequest.newBuilder()
267         .setSnapshot(snapshot).build();
268     IsSnapshotDoneResponse done = IsSnapshotDoneResponse.newBuilder()
269         .buildPartial();
270     while (!done.getDone()) {
271       done = master.getMasterRpcServices().isSnapshotDone(null, request);
272       try {
273         Thread.sleep(sleep);
274       } catch (InterruptedException e) {
275         throw new ServiceException(e);
276       }
277     }
278   }
279 
280   /*
281    * Take snapshot with maximum of numTries attempts, ignoring CorruptedSnapshotException
282    * except for the last CorruptedSnapshotException
283    */
284   public static void snapshot(Admin admin,
285       final String snapshotName, final String tableName,
286       SnapshotDescription.Type type, int numTries) throws IOException {
287     int tries = 0;
288     CorruptedSnapshotException lastEx = null;
289     while (tries++ < numTries) {
290       try {
291         admin.snapshot(snapshotName, TableName.valueOf(tableName), type);
292         return;
293       } catch (CorruptedSnapshotException cse) {
294         LOG.warn("Got CorruptedSnapshotException", cse);
295         lastEx = cse;
296       }
297     }
298     throw lastEx;
299   }
300 
301   public static void cleanupSnapshot(Admin admin, byte[] tableName)
302       throws IOException {
303     SnapshotTestingUtils.cleanupSnapshot(admin, Bytes.toString(tableName));
304   }
305 
306   public static void cleanupSnapshot(Admin admin, String snapshotName)
307       throws IOException {
308     // delete the taken snapshot
309     admin.deleteSnapshot(snapshotName);
310     assertNoSnapshots(admin);
311   }
312 
313   /**
314    * Expect the snapshot to throw an error when checking if the snapshot is
315    * complete
316    *
317    * @param master master to check
318    * @param snapshot the {@link SnapshotDescription} request to pass to the master
319    * @param clazz expected exception from the master
320    */
321   public static void expectSnapshotDoneException(HMaster master,
322       IsSnapshotDoneRequest snapshot,
323       Class<? extends HBaseSnapshotException> clazz) {
324     try {
325       master.getMasterRpcServices().isSnapshotDone(null, snapshot);
326       Assert.fail("didn't fail to lookup a snapshot");
327     } catch (ServiceException se) {
328       try {
329         throw ProtobufUtil.getRemoteException(se);
330       } catch (HBaseSnapshotException e) {
331         assertEquals("Threw wrong snapshot exception!", clazz, e.getClass());
332       } catch (Throwable t) {
333         Assert.fail("Threw an unexpected exception:" + t);
334       }
335     }
336   }
337 
338   /**
339    * List all the HFiles in the given table
340    *
341    * @param fs: FileSystem where the table lives
342    * @param tableDir directory of the table
343    * @return array of the current HFiles in the table (could be a zero-length array)
344    * @throws IOException on unexecpted error reading the FS
345    */
346   public static ArrayList<String> listHFileNames(final FileSystem fs, final Path tableDir)
347       throws IOException {
348     final ArrayList<String> hfiles = new ArrayList<String>();
349     FSVisitor.visitTableStoreFiles(fs, tableDir, new FSVisitor.StoreFileVisitor() {
350       @Override
351       public void storeFile(final String region, final String family, final String hfileName)
352           throws IOException {
353         hfiles.add(hfileName);
354       }
355     });
356     Collections.sort(hfiles);
357     return hfiles;
358   }
359 
360   /**
361    * Take a snapshot of the specified table and verify that the given family is
362    * not empty. Note that this will leave the table disabled
363    * in the case of an offline snapshot.
364    */
365   public static void createSnapshotAndValidate(Admin admin,
366       TableName tableName, String familyName, String snapshotNameString,
367       Path rootDir, FileSystem fs, boolean onlineSnapshot)
368       throws Exception {
369     ArrayList<byte[]> nonEmptyFamilyNames = new ArrayList<byte[]>(1);
370     nonEmptyFamilyNames.add(Bytes.toBytes(familyName));
371     createSnapshotAndValidate(admin, tableName, nonEmptyFamilyNames, /* emptyFamilyNames= */ null,
372                               snapshotNameString, rootDir, fs, onlineSnapshot);
373   }
374 
375   /**
376    * Take a snapshot of the specified table and verify the given families.
377    * Note that this will leave the table disabled in the case of an offline snapshot.
378    */
379   public static void createSnapshotAndValidate(Admin admin,
380       TableName tableName, List<byte[]> nonEmptyFamilyNames, List<byte[]> emptyFamilyNames,
381       String snapshotNameString, Path rootDir, FileSystem fs, boolean onlineSnapshot)
382         throws Exception {
383     if (!onlineSnapshot) {
384       try {
385         admin.disableTable(tableName);
386       } catch (TableNotEnabledException tne) {
387         LOG.info("In attempting to disable " + tableName + " it turns out that the this table is " +
388             "already disabled.");
389       }
390     }
391     admin.snapshot(snapshotNameString, tableName);
392 
393     List<SnapshotDescription> snapshots = SnapshotTestingUtils.assertExistsMatchingSnapshot(admin,
394       snapshotNameString, tableName);
395     if (snapshots == null || snapshots.size() != 1) {
396       Assert.fail("Incorrect number of snapshots for table " + tableName);
397     }
398 
399     SnapshotTestingUtils.confirmSnapshotValid(snapshots.get(0), tableName, nonEmptyFamilyNames,
400       emptyFamilyNames, rootDir, admin, fs);
401   }
402 
403   /**
404    * Corrupt the specified snapshot by deleting some files.
405    *
406    * @param util {@link HBaseTestingUtility}
407    * @param snapshotName name of the snapshot to corrupt
408    * @return array of the corrupted HFiles
409    * @throws IOException on unexecpted error reading the FS
410    */
411   public static ArrayList corruptSnapshot(final HBaseTestingUtility util, final String snapshotName)
412       throws IOException {
413     final MasterFileSystem mfs = util.getHBaseCluster().getMaster().getMasterFileSystem();
414     final FileSystem fs = mfs.getFileSystem();
415 
416     Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName,
417                                                                         mfs.getRootDir());
418     SnapshotDescription snapshotDesc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);
419     final TableName table = TableName.valueOf(snapshotDesc.getTable());
420 
421     final ArrayList corruptedFiles = new ArrayList();
422     final Configuration conf = util.getConfiguration();
423     SnapshotReferenceUtil.visitTableStoreFiles(conf, fs, snapshotDir, snapshotDesc,
424         new SnapshotReferenceUtil.StoreFileVisitor() {
425       @Override
426       public void storeFile(final HRegionInfo regionInfo, final String family,
427             final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
428         String region = regionInfo.getEncodedName();
429         String hfile = storeFile.getName();
430         HFileLink link = HFileLink.build(conf, table, region, family, hfile);
431         if (corruptedFiles.size() % 2 == 0) {
432           fs.delete(link.getAvailablePath(fs), true);
433           corruptedFiles.add(hfile);
434         }
435       }
436     });
437 
438     assertTrue(corruptedFiles.size() > 0);
439     return corruptedFiles;
440   }
441 
442   // ==========================================================================
443   //  Snapshot Mock
444   // ==========================================================================
445   public static class SnapshotMock {
446     private final static String TEST_FAMILY = "cf";
447     public final static int TEST_NUM_REGIONS = 4;
448 
449     private final Configuration conf;
450     private final FileSystem fs;
451     private final Path rootDir;
452 
453     static class RegionData {
454       public HRegionInfo hri;
455       public Path tableDir;
456       public Path[] files;
457 
458       public RegionData(final Path tableDir, final HRegionInfo hri, final int nfiles) {
459         this.tableDir = tableDir;
460         this.hri = hri;
461         this.files = new Path[nfiles];
462       }
463     }
464 
465     public static class SnapshotBuilder {
466       private final RegionData[] tableRegions;
467       private final SnapshotDescription desc;
468       private final HTableDescriptor htd;
469       private final Configuration conf;
470       private final FileSystem fs;
471       private final Path rootDir;
472       private Path snapshotDir;
473       private int snapshotted = 0;
474 
475       public SnapshotBuilder(final Configuration conf, final FileSystem fs,
476           final Path rootDir, final HTableDescriptor htd,
477           final SnapshotDescription desc, final RegionData[] tableRegions)
478           throws IOException {
479         this.fs = fs;
480         this.conf = conf;
481         this.rootDir = rootDir;
482         this.htd = htd;
483         this.desc = desc;
484         this.tableRegions = tableRegions;
485         this.snapshotDir = SnapshotDescriptionUtils.getWorkingSnapshotDir(desc, rootDir);
486         new FSTableDescriptors(conf)
487           .createTableDescriptorForTableDirectory(snapshotDir, htd, false);
488       }
489 
490       public HTableDescriptor getTableDescriptor() {
491         return this.htd;
492       }
493 
494       public SnapshotDescription getSnapshotDescription() {
495         return this.desc;
496       }
497 
498       public Path getSnapshotsDir() {
499         return this.snapshotDir;
500       }
501 
502       public Path[] addRegion() throws IOException {
503         return addRegion(desc);
504       }
505 
506       public Path[] addRegionV1() throws IOException {
507         return addRegion(desc.toBuilder()
508                           .setVersion(SnapshotManifestV1.DESCRIPTOR_VERSION)
509                           .build());
510       }
511 
512       public Path[] addRegionV2() throws IOException {
513         return addRegion(desc.toBuilder()
514                           .setVersion(SnapshotManifestV2.DESCRIPTOR_VERSION)
515                           .build());
516       }
517 
518       private Path[] addRegion(final SnapshotDescription desc) throws IOException {
519         if (this.snapshotted == tableRegions.length) {
520           throw new UnsupportedOperationException("No more regions in the table");
521         }
522 
523         RegionData regionData = tableRegions[this.snapshotted++];
524         ForeignExceptionDispatcher monitor = new ForeignExceptionDispatcher(desc.getName());
525         SnapshotManifest manifest = SnapshotManifest.create(conf, fs, snapshotDir, desc, monitor);
526         manifest.addRegion(regionData.tableDir, regionData.hri);
527         return regionData.files;
528       }
529 
530       public Path commit() throws IOException {
531         ForeignExceptionDispatcher monitor = new ForeignExceptionDispatcher(desc.getName());
532         SnapshotManifest manifest = SnapshotManifest.create(conf, fs, snapshotDir, desc, monitor);
533         manifest.addTableDescriptor(htd);
534         manifest.consolidate();
535         SnapshotDescriptionUtils.completeSnapshot(desc, rootDir, snapshotDir, fs);
536         snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(desc, rootDir);
537         return snapshotDir;
538       }
539     }
540 
541     public SnapshotMock(final Configuration conf, final FileSystem fs, final Path rootDir) {
542       this.fs = fs;
543       this.conf = conf;
544       this.rootDir = rootDir;
545     }
546 
547     public SnapshotBuilder createSnapshotV1(final String snapshotName, final String tableName)
548         throws IOException {
549       return createSnapshot(snapshotName, tableName, SnapshotManifestV1.DESCRIPTOR_VERSION);
550     }
551 
552     public SnapshotBuilder createSnapshotV2(final String snapshotName, final String tableName)
553         throws IOException {
554       return createSnapshot(snapshotName, tableName, SnapshotManifestV2.DESCRIPTOR_VERSION);
555     }
556 
557     private SnapshotBuilder createSnapshot(final String snapshotName, final String tableName,
558         final int version) throws IOException {
559       HTableDescriptor htd = createHtd(tableName);
560 
561       RegionData[] regions = createTable(htd, TEST_NUM_REGIONS);
562 
563       SnapshotDescription desc = SnapshotDescription.newBuilder()
564         .setTable(htd.getNameAsString())
565         .setName(snapshotName)
566         .setVersion(version)
567         .build();
568 
569       Path workingDir = SnapshotDescriptionUtils.getWorkingSnapshotDir(desc, rootDir);
570       SnapshotDescriptionUtils.writeSnapshotInfo(desc, workingDir, fs);
571       return new SnapshotBuilder(conf, fs, rootDir, htd, desc, regions);
572     }
573 
574     public HTableDescriptor createHtd(final String tableName) {
575       HTableDescriptor htd = new HTableDescriptor(tableName);
576       htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
577       return htd;
578     }
579 
580     private RegionData[] createTable(final HTableDescriptor htd, final int nregions)
581         throws IOException {
582       Path tableDir = FSUtils.getTableDir(rootDir, htd.getTableName());
583       new FSTableDescriptors(conf).createTableDescriptorForTableDirectory(tableDir, htd, false);
584 
585       assertTrue(nregions % 2 == 0);
586       RegionData[] regions = new RegionData[nregions];
587       for (int i = 0; i < regions.length; i += 2) {
588         byte[] startKey = Bytes.toBytes(0 + i * 2);
589         byte[] endKey = Bytes.toBytes(1 + i * 2);
590 
591         // First region, simple with one plain hfile.
592         HRegionInfo hri = new HRegionInfo(htd.getTableName(), startKey, endKey);
593         HRegionFileSystem rfs = HRegionFileSystem.createRegionOnFileSystem(conf, fs, tableDir, hri);
594         regions[i] = new RegionData(tableDir, hri, 3);
595         for (int j = 0; j < regions[i].files.length; ++j) {
596           Path storeFile = createStoreFile(rfs.createTempName());
597           regions[i].files[j] = rfs.commitStoreFile(TEST_FAMILY, storeFile);
598         }
599 
600         // Second region, used to test the split case.
601         // This region contains a reference to the hfile in the first region.
602         startKey = Bytes.toBytes(2 + i * 2);
603         endKey = Bytes.toBytes(3 + i * 2);
604         hri = new HRegionInfo(htd.getTableName());
605         rfs = HRegionFileSystem.createRegionOnFileSystem(conf, fs, tableDir, hri);
606         regions[i+1] = new RegionData(tableDir, hri, regions[i].files.length);
607         for (int j = 0; j < regions[i].files.length; ++j) {
608           String refName = regions[i].files[j].getName() + '.' + regions[i].hri.getEncodedName();
609           Path refFile = createStoreFile(new Path(rootDir, refName));
610           regions[i+1].files[j] = rfs.commitStoreFile(TEST_FAMILY, refFile);
611         }
612       }
613       return regions;
614     }
615 
616     private Path createStoreFile(final Path storeFile)
617         throws IOException {
618       FSDataOutputStream out = fs.create(storeFile);
619       try {
620         out.write(Bytes.toBytes(storeFile.toString()));
621       } finally {
622         out.close();
623       }
624       return storeFile;
625     }
626   }
627 
628   // ==========================================================================
629   //  Table Helpers
630   // ==========================================================================
631   public static void waitForTableToBeOnline(final HBaseTestingUtility util,
632                                             final TableName tableName)
633       throws IOException, InterruptedException {
634     HRegionServer rs = util.getRSForFirstRegionInTable(tableName);
635     List<Region> onlineRegions = rs.getOnlineRegions(tableName);
636     for (Region region : onlineRegions) {
637       region.waitForFlushesAndCompactions();
638     }
639     // Wait up to 60 seconds for a table to be available.
640     util.waitFor(60000, util.predicateTableAvailable(tableName));
641   }
642 
643   public static void createTable(final HBaseTestingUtility util, final TableName tableName,
644       int regionReplication, final byte[]... families) throws IOException, InterruptedException {
645     HTableDescriptor htd = new HTableDescriptor(tableName);
646     htd.setRegionReplication(regionReplication);
647     for (byte[] family : families) {
648       HColumnDescriptor hcd = new HColumnDescriptor(family);
649       htd.addFamily(hcd);
650     }
651     byte[][] splitKeys = getSplitKeys();
652     util.createTable(htd, splitKeys);
653     assertEquals((splitKeys.length + 1) * regionReplication,
654         util.getHBaseAdmin().getTableRegions(tableName).size());
655   }
656 
657   public static byte[][] getSplitKeys() {
658     byte[][] splitKeys = new byte[KEYS.length-2][];
659     for (int i = 0; i < splitKeys.length; ++i) {
660       splitKeys[i] = new byte[] { KEYS[i+1] };
661     }
662     return splitKeys;
663   }
664 
665   public static void createTable(final HBaseTestingUtility util, final TableName tableName,
666       final byte[]... families) throws IOException, InterruptedException {
667     createTable(util, tableName, 1, families);
668   }
669 
670   public static void loadData(final HBaseTestingUtility util, final TableName tableName, int rows,
671       byte[]... families) throws IOException, InterruptedException {
672     try (BufferedMutator mutator = util.getConnection().getBufferedMutator(tableName)) {
673       loadData(util, mutator, rows, families);
674     }
675   }
676 
677   public static void loadData(final HBaseTestingUtility util, final BufferedMutator mutator, int rows,
678       byte[]... families) throws IOException, InterruptedException {
679     // Ensure one row per region
680     assertTrue(rows >= KEYS.length);
681     for (byte k0: KEYS) {
682       byte[] k = new byte[] { k0 };
683       byte[] value = Bytes.add(Bytes.toBytes(System.currentTimeMillis()), k);
684       byte[] key = Bytes.add(k, Bytes.toBytes(MD5Hash.getMD5AsHex(value)));
685       final byte[][] families1 = families;
686       final byte[] key1 = key;
687       final byte[] value1 = value;
688       mutator.mutate(createPut(families1, key1, value1));
689       rows--;
690     }
691 
692     // Add other extra rows. more rows, more files
693     while (rows-- > 0) {
694       byte[] value = Bytes.add(Bytes.toBytes(System.currentTimeMillis()), Bytes.toBytes(rows));
695       byte[] key = Bytes.toBytes(MD5Hash.getMD5AsHex(value));
696       final byte[][] families1 = families;
697       final byte[] key1 = key;
698       final byte[] value1 = value;
699       mutator.mutate(createPut(families1, key1, value1));
700     }
701     mutator.flush();
702 
703     waitForTableToBeOnline(util, mutator.getName());
704   }
705 
706   private static Put createPut(final byte[][] families, final byte[] key, final byte[] value) {
707     byte[] q = Bytes.toBytes("q");
708     Put put = new Put(key);
709     put.setDurability(Durability.SKIP_WAL);
710     for (byte[] family: families) {
711       put.add(family, q, value);
712     }
713     return put;
714   }
715 
716   public static void deleteAllSnapshots(final Admin admin)
717       throws IOException {
718     // Delete all the snapshots
719     for (SnapshotDescription snapshot: admin.listSnapshots()) {
720       admin.deleteSnapshot(snapshot.getName());
721     }
722     SnapshotTestingUtils.assertNoSnapshots(admin);
723   }
724 
725   public static void deleteArchiveDirectory(final HBaseTestingUtility util)
726       throws IOException {
727     // Ensure the archiver to be empty
728     MasterFileSystem mfs = util.getMiniHBaseCluster().getMaster().getMasterFileSystem();
729     Path archiveDir = new Path(mfs.getRootDir(), HConstants.HFILE_ARCHIVE_DIRECTORY);
730     mfs.getFileSystem().delete(archiveDir, true);
731   }
732 
733   public static void verifyRowCount(final HBaseTestingUtility util, final TableName tableName,
734       long expectedRows) throws IOException {
735     Table table = new HTable(util.getConfiguration(), tableName);
736     try {
737       assertEquals(expectedRows, util.countRows(table));
738     } finally {
739       table.close();
740     }
741   }
742 
743   public static void verifyReplicasCameOnline(TableName tableName, Admin admin,
744       int regionReplication) throws IOException {
745     List<HRegionInfo> regions = admin.getTableRegions(tableName);
746     HashSet<HRegionInfo> set = new HashSet<HRegionInfo>();
747     for (HRegionInfo hri : regions) {
748       set.add(RegionReplicaUtil.getRegionInfoForDefaultReplica(hri));
749       for (int i = 0; i < regionReplication; i++) {
750         HRegionInfo replica = RegionReplicaUtil.getRegionInfoForReplica(hri, i);
751         if (!regions.contains(replica)) {
752           Assert.fail(replica + " is not contained in the list of online regions");
753         }
754       }
755     }
756     assert(set.size() == getSplitKeys().length + 1);
757   }
758 }