001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.mapreduce; 019 020import static org.junit.jupiter.api.Assertions.assertArrayEquals; 021import static org.junit.jupiter.api.Assertions.assertEquals; 022import static org.junit.jupiter.api.Assertions.assertFalse; 023 024import java.io.IOException; 025import java.util.Arrays; 026import org.apache.hadoop.conf.Configuration; 027import org.apache.hadoop.fs.FileStatus; 028import org.apache.hadoop.fs.FileSystem; 029import org.apache.hadoop.fs.Path; 030import org.apache.hadoop.hbase.Cell; 031import org.apache.hadoop.hbase.CellScanner; 032import org.apache.hadoop.hbase.HBaseTestingUtil; 033import org.apache.hadoop.hbase.StartTestingClusterOption; 034import org.apache.hadoop.hbase.TableName; 035import org.apache.hadoop.hbase.client.Admin; 036import org.apache.hadoop.hbase.client.Result; 037import org.apache.hadoop.hbase.client.Table; 038import org.apache.hadoop.hbase.io.HFileLink; 039import org.apache.hadoop.hbase.io.ImmutableBytesWritable; 040import org.apache.hadoop.hbase.master.snapshot.SnapshotManager; 041import org.apache.hadoop.hbase.regionserver.StoreFileInfo; 042import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils; 043import org.apache.hadoop.hbase.util.Bytes; 044import org.apache.hadoop.hbase.util.CommonFSUtils; 045import org.apache.hadoop.hbase.util.FSUtils; 046import org.apache.hadoop.hbase.util.HFileArchiveUtil; 047import org.junit.jupiter.api.AfterEach; 048import org.junit.jupiter.api.BeforeEach; 049import org.junit.jupiter.api.Test; 050import org.slf4j.Logger; 051import org.slf4j.LoggerFactory; 052 053public abstract class TableSnapshotInputFormatTestBase { 054 private static final Logger LOG = LoggerFactory.getLogger(TableSnapshotInputFormatTestBase.class); 055 protected final HBaseTestingUtil UTIL = new HBaseTestingUtil(); 056 protected static final int NUM_REGION_SERVERS = 2; 057 protected static final byte[][] FAMILIES = { Bytes.toBytes("f1"), Bytes.toBytes("f2") }; 058 059 protected FileSystem fs; 060 protected Path rootDir; 061 062 @BeforeEach 063 public void setupCluster() throws Exception { 064 setupConf(UTIL.getConfiguration()); 065 StartTestingClusterOption option = 066 StartTestingClusterOption.builder().numRegionServers(NUM_REGION_SERVERS) 067 .numDataNodes(NUM_REGION_SERVERS).createRootDir(true).build(); 068 UTIL.startMiniCluster(option); 069 rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir(); 070 fs = rootDir.getFileSystem(UTIL.getConfiguration()); 071 } 072 073 @AfterEach 074 public void tearDownCluster() throws Exception { 075 UTIL.shutdownMiniCluster(); 076 } 077 078 private static void setupConf(Configuration conf) { 079 // Enable snapshot 080 conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true); 081 } 082 083 protected abstract void testWithMockedMapReduce(HBaseTestingUtil util, String snapshotName, 084 int numRegions, int numSplitsPerRegion, int expectedNumSplits, boolean setLocalityEnabledTo) 085 throws Exception; 086 087 protected abstract void testWithMapReduceImpl(HBaseTestingUtil util, TableName tableName, 088 String snapshotName, Path tableDir, int numRegions, int numSplitsPerRegion, 089 int expectedNumSplits, boolean shutdownCluster) throws Exception; 090 091 protected abstract byte[] getStartRow(); 092 093 protected abstract byte[] getEndRow(); 094 095 @Test 096 public void testWithMockedMapReduceSingleRegion() throws Exception { 097 testWithMockedMapReduce(UTIL, "testWithMockedMapReduceSingleRegion", 1, 1, 1, true); 098 } 099 100 @Test 101 public void testWithMockedMapReduceMultiRegion() throws Exception { 102 testWithMockedMapReduce(UTIL, "testWithMockedMapReduceMultiRegion", 10, 1, 8, false); 103 } 104 105 @Test 106 public void testWithMapReduceSingleRegion() throws Exception { 107 testWithMapReduce(UTIL, "testWithMapReduceSingleRegion", 1, 1, 1, false); 108 } 109 110 @Test 111 public void testWithMapReduceMultiRegion() throws Exception { 112 testWithMapReduce(UTIL, "testWithMapReduceMultiRegion", 10, 1, 8, false); 113 } 114 115 @Test 116 // run the MR job while HBase is offline 117 public void testWithMapReduceAndOfflineHBaseMultiRegion() throws Exception { 118 testWithMapReduce(UTIL, "testWithMapReduceAndOfflineHBaseMultiRegion", 10, 1, 8, true); 119 } 120 121 // Test that snapshot restore does not create back references in the HBase root dir. 122 @Test 123 public void testRestoreSnapshotDoesNotCreateBackRefLinks() throws Exception { 124 TableName tableName = TableName.valueOf("testRestoreSnapshotDoesNotCreateBackRefLinks"); 125 String snapshotName = "foo"; 126 127 try { 128 createTableAndSnapshot(UTIL, tableName, snapshotName, getStartRow(), getEndRow(), 1); 129 130 Path tmpTableDir = UTIL.getDataTestDirOnTestFS(snapshotName); 131 132 testRestoreSnapshotDoesNotCreateBackRefLinksInit(tableName, snapshotName, tmpTableDir); 133 134 Path rootDir = CommonFSUtils.getRootDir(UTIL.getConfiguration()); 135 for (Path regionDir : FSUtils.getRegionDirs(fs, 136 CommonFSUtils.getTableDir(rootDir, tableName))) { 137 for (Path storeDir : FSUtils.getFamilyDirs(fs, regionDir)) { 138 for (FileStatus status : fs.listStatus(storeDir)) { 139 System.out.println(status.getPath()); 140 if (StoreFileInfo.isValid(status)) { 141 Path archiveStoreDir = HFileArchiveUtil.getStoreArchivePath(UTIL.getConfiguration(), 142 tableName, regionDir.getName(), storeDir.getName()); 143 144 Path path = HFileLink.getBackReferencesDir(storeDir, status.getPath().getName()); 145 // assert back references directory is empty 146 assertFalse(fs.exists(path), "There is a back reference in " + path); 147 148 path = HFileLink.getBackReferencesDir(archiveStoreDir, status.getPath().getName()); 149 // assert back references directory is empty 150 assertFalse(fs.exists(path), "There is a back reference in " + path); 151 } 152 } 153 } 154 } 155 } finally { 156 UTIL.getAdmin().deleteSnapshot(snapshotName); 157 UTIL.deleteTable(tableName); 158 } 159 } 160 161 public abstract void testRestoreSnapshotDoesNotCreateBackRefLinksInit(TableName tableName, 162 String snapshotName, Path tmpTableDir) throws Exception; 163 164 protected void testWithMapReduce(HBaseTestingUtil util, String snapshotName, int numRegions, 165 int numSplitsPerRegion, int expectedNumSplits, boolean shutdownCluster) throws Exception { 166 Path tableDir = util.getDataTestDirOnTestFS(snapshotName); 167 TableName tableName = TableName.valueOf("testWithMapReduce"); 168 testWithMapReduceImpl(util, tableName, snapshotName, tableDir, numRegions, numSplitsPerRegion, 169 expectedNumSplits, shutdownCluster); 170 } 171 172 protected static void verifyRowFromMap(ImmutableBytesWritable key, Result result) 173 throws IOException { 174 byte[] row = key.get(); 175 CellScanner scanner = result.cellScanner(); 176 while (scanner.advance()) { 177 Cell cell = scanner.current(); 178 179 // assert that all Cells in the Result have the same key 180 assertEquals(0, Bytes.compareTo(row, 0, row.length, cell.getRowArray(), cell.getRowOffset(), 181 cell.getRowLength())); 182 } 183 184 for (byte[] family : FAMILIES) { 185 byte[] actual = result.getValue(family, family); 186 assertArrayEquals(row, actual, "Row in snapshot does not match, expected:" 187 + Bytes.toString(row) + " ,actual:" + Bytes.toString(actual)); 188 } 189 } 190 191 protected static void createTableAndSnapshot(HBaseTestingUtil util, TableName tableName, 192 String snapshotName, byte[] startRow, byte[] endRow, int numRegions) throws Exception { 193 try { 194 LOG.debug("Ensuring table doesn't exist."); 195 util.deleteTable(tableName); 196 } catch (Exception ex) { 197 // ignore 198 } 199 200 LOG.info("creating table '" + tableName + "'"); 201 if (numRegions > 1) { 202 util.createTable(tableName, FAMILIES, 1, startRow, endRow, numRegions); 203 } else { 204 util.createTable(tableName, FAMILIES); 205 } 206 Admin admin = util.getAdmin(); 207 208 LOG.info("put some stuff in the table"); 209 Table table = util.getConnection().getTable(tableName); 210 util.loadTable(table, FAMILIES); 211 212 Path rootDir = CommonFSUtils.getRootDir(util.getConfiguration()); 213 FileSystem fs = rootDir.getFileSystem(util.getConfiguration()); 214 215 LOG.info("snapshot"); 216 SnapshotTestingUtils.createSnapshotAndValidate(admin, tableName, Arrays.asList(FAMILIES), null, 217 snapshotName, rootDir, fs, true); 218 219 LOG.info("load different values"); 220 byte[] value = Bytes.toBytes("after_snapshot_value"); 221 util.loadTable(table, FAMILIES, value); 222 223 LOG.info("cause flush to create new files in the region"); 224 admin.flush(tableName); 225 table.close(); 226 } 227}