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.client; 019 020import java.io.IOException; 021import java.util.Arrays; 022import java.util.List; 023import org.apache.hadoop.conf.Configuration; 024import org.apache.hadoop.fs.FileSystem; 025import org.apache.hadoop.fs.Path; 026import org.apache.hadoop.hbase.Cell; 027import org.apache.hadoop.hbase.CellScanner; 028import org.apache.hadoop.hbase.HBaseClassTestRule; 029import org.apache.hadoop.hbase.HBaseTestingUtility; 030import org.apache.hadoop.hbase.HRegionInfo; 031import org.apache.hadoop.hbase.TableName; 032import org.apache.hadoop.hbase.master.snapshot.SnapshotManager; 033import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper; 034import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils; 035import org.apache.hadoop.hbase.testclassification.ClientTests; 036import org.apache.hadoop.hbase.testclassification.LargeTests; 037import org.apache.hadoop.hbase.util.Bytes; 038import org.apache.hadoop.hbase.util.FSUtils; 039import org.junit.After; 040import org.junit.Assert; 041import org.junit.ClassRule; 042import org.junit.Test; 043import org.junit.experimental.categories.Category; 044import org.slf4j.Logger; 045import org.slf4j.LoggerFactory; 046 047@Category({LargeTests.class, ClientTests.class}) 048public class TestTableSnapshotScanner { 049 050 @ClassRule 051 public static final HBaseClassTestRule CLASS_RULE = 052 HBaseClassTestRule.forClass(TestTableSnapshotScanner.class); 053 054 private static final Logger LOG = LoggerFactory.getLogger(TestTableSnapshotScanner.class); 055 private final HBaseTestingUtility UTIL = new HBaseTestingUtility(); 056 private static final int NUM_REGION_SERVERS = 2; 057 private static final byte[][] FAMILIES = {Bytes.toBytes("f1"), Bytes.toBytes("f2")}; 058 public static byte[] bbb = Bytes.toBytes("bbb"); 059 public static byte[] yyy = Bytes.toBytes("yyy"); 060 061 private FileSystem fs; 062 private Path rootDir; 063 064 public static void blockUntilSplitFinished(HBaseTestingUtility util, TableName tableName, 065 int expectedRegionSize) throws Exception { 066 for (int i = 0; i < 100; i++) { 067 List<HRegionInfo> hRegionInfoList = util.getAdmin().getTableRegions(tableName); 068 if (hRegionInfoList.size() >= expectedRegionSize) { 069 break; 070 } 071 Thread.sleep(1000); 072 } 073 } 074 075 public void setupCluster() throws Exception { 076 setupConf(UTIL.getConfiguration()); 077 UTIL.startMiniCluster(NUM_REGION_SERVERS, true); 078 rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir(); 079 fs = rootDir.getFileSystem(UTIL.getConfiguration()); 080 } 081 082 public void tearDownCluster() throws Exception { 083 UTIL.shutdownMiniCluster(); 084 } 085 086 private static void setupConf(Configuration conf) { 087 // Enable snapshot 088 conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true); 089 } 090 091 @After 092 public void tearDown() throws Exception { 093 } 094 095 public static void createTableAndSnapshot(HBaseTestingUtility util, TableName tableName, 096 String snapshotName, int numRegions) 097 throws Exception { 098 try { 099 util.deleteTable(tableName); 100 } catch(Exception ex) { 101 // ignore 102 } 103 104 if (numRegions > 1) { 105 util.createTable(tableName, FAMILIES, 1, bbb, yyy, numRegions); 106 } else { 107 util.createTable(tableName, FAMILIES); 108 } 109 Admin admin = util.getAdmin(); 110 111 // put some stuff in the table 112 Table table = util.getConnection().getTable(tableName); 113 util.loadTable(table, FAMILIES); 114 115 Path rootDir = FSUtils.getRootDir(util.getConfiguration()); 116 FileSystem fs = rootDir.getFileSystem(util.getConfiguration()); 117 118 SnapshotTestingUtils.createSnapshotAndValidate(admin, tableName, 119 Arrays.asList(FAMILIES), null, snapshotName, rootDir, fs, true); 120 121 // load different values 122 byte[] value = Bytes.toBytes("after_snapshot_value"); 123 util.loadTable(table, FAMILIES, value); 124 125 // cause flush to create new files in the region 126 admin.flush(tableName); 127 table.close(); 128 } 129 130 @Test 131 public void testNoDuplicateResultsWhenSplitting() throws Exception { 132 setupCluster(); 133 TableName tableName = TableName.valueOf("testNoDuplicateResultsWhenSplitting"); 134 String snapshotName = "testSnapshotBug"; 135 try { 136 if (UTIL.getAdmin().tableExists(tableName)) { 137 UTIL.deleteTable(tableName); 138 } 139 140 UTIL.createTable(tableName, FAMILIES); 141 Admin admin = UTIL.getAdmin(); 142 143 // put some stuff in the table 144 Table table = UTIL.getConnection().getTable(tableName); 145 UTIL.loadTable(table, FAMILIES); 146 147 // split to 2 regions 148 admin.split(tableName, Bytes.toBytes("eee")); 149 blockUntilSplitFinished(UTIL, tableName, 2); 150 151 Path rootDir = FSUtils.getRootDir(UTIL.getConfiguration()); 152 FileSystem fs = rootDir.getFileSystem(UTIL.getConfiguration()); 153 154 SnapshotTestingUtils.createSnapshotAndValidate(admin, tableName, 155 Arrays.asList(FAMILIES), null, snapshotName, rootDir, fs, true); 156 157 // load different values 158 byte[] value = Bytes.toBytes("after_snapshot_value"); 159 UTIL.loadTable(table, FAMILIES, value); 160 161 // cause flush to create new files in the region 162 admin.flush(tableName); 163 table.close(); 164 165 Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName); 166 Scan scan = new Scan().withStartRow(bbb).withStopRow(yyy); // limit the scan 167 168 TableSnapshotScanner scanner = 169 new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, snapshotName, scan); 170 171 verifyScanner(scanner, bbb, yyy); 172 scanner.close(); 173 } finally { 174 UTIL.getAdmin().deleteSnapshot(snapshotName); 175 UTIL.deleteTable(tableName); 176 tearDownCluster(); 177 } 178 } 179 180 @Test 181 public void testWithSingleRegion() throws Exception { 182 testScanner(UTIL, "testWithSingleRegion", 1, false); 183 } 184 185 @Test 186 public void testWithMultiRegion() throws Exception { 187 testScanner(UTIL, "testWithMultiRegion", 10, false); 188 } 189 190 @Test 191 public void testWithOfflineHBaseMultiRegion() throws Exception { 192 testScanner(UTIL, "testWithMultiRegion", 20, true); 193 } 194 195 @Test 196 public void testScannerWithRestoreScanner() throws Exception { 197 setupCluster(); 198 TableName tableName = TableName.valueOf("testScanner"); 199 String snapshotName = "testScannerWithRestoreScanner"; 200 try { 201 createTableAndSnapshot(UTIL, tableName, snapshotName, 50); 202 Path restoreDir = UTIL.getDataTestDirOnTestFS(snapshotName); 203 Scan scan = new Scan(bbb, yyy); // limit the scan 204 205 Configuration conf = UTIL.getConfiguration(); 206 Path rootDir = FSUtils.getRootDir(conf); 207 208 TableSnapshotScanner scanner0 = 209 new TableSnapshotScanner(conf, restoreDir, snapshotName, scan); 210 verifyScanner(scanner0, bbb, yyy); 211 scanner0.close(); 212 213 // restore snapshot. 214 RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName); 215 216 // scan the snapshot without restoring snapshot 217 TableSnapshotScanner scanner = 218 new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true); 219 verifyScanner(scanner, bbb, yyy); 220 scanner.close(); 221 222 // check whether the snapshot has been deleted by the close of scanner. 223 scanner = new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true); 224 verifyScanner(scanner, bbb, yyy); 225 scanner.close(); 226 227 // restore snapshot again. 228 RestoreSnapshotHelper.copySnapshotForScanner(conf, fs, rootDir, restoreDir, snapshotName); 229 230 // check whether the snapshot has been deleted by the close of scanner. 231 scanner = new TableSnapshotScanner(conf, rootDir, restoreDir, snapshotName, scan, true); 232 verifyScanner(scanner, bbb, yyy); 233 scanner.close(); 234 } finally { 235 UTIL.getAdmin().deleteSnapshot(snapshotName); 236 UTIL.deleteTable(tableName); 237 tearDownCluster(); 238 } 239 } 240 241 private void testScanner(HBaseTestingUtility util, String snapshotName, int numRegions, 242 boolean shutdownCluster) throws Exception { 243 setupCluster(); 244 TableName tableName = TableName.valueOf("testScanner"); 245 try { 246 createTableAndSnapshot(util, tableName, snapshotName, numRegions); 247 248 if (shutdownCluster) { 249 util.shutdownMiniHBaseCluster(); 250 } 251 252 Path restoreDir = util.getDataTestDirOnTestFS(snapshotName); 253 Scan scan = new Scan(bbb, yyy); // limit the scan 254 255 TableSnapshotScanner scanner = new TableSnapshotScanner(UTIL.getConfiguration(), restoreDir, 256 snapshotName, scan); 257 258 verifyScanner(scanner, bbb, yyy); 259 scanner.close(); 260 } finally { 261 if (!shutdownCluster) { 262 util.getAdmin().deleteSnapshot(snapshotName); 263 util.deleteTable(tableName); 264 tearDownCluster(); 265 } 266 } 267 } 268 269 private void verifyScanner(ResultScanner scanner, byte[] startRow, byte[] stopRow) 270 throws IOException, InterruptedException { 271 272 HBaseTestingUtility.SeenRowTracker rowTracker = 273 new HBaseTestingUtility.SeenRowTracker(startRow, stopRow); 274 275 while (true) { 276 Result result = scanner.next(); 277 if (result == null) { 278 break; 279 } 280 verifyRow(result); 281 rowTracker.addRow(result.getRow()); 282 } 283 284 // validate all rows are seen 285 rowTracker.validate(); 286 } 287 288 private static void verifyRow(Result result) throws IOException { 289 byte[] row = result.getRow(); 290 CellScanner scanner = result.cellScanner(); 291 while (scanner.advance()) { 292 Cell cell = scanner.current(); 293 294 //assert that all Cells in the Result have the same key 295 Assert.assertEquals(0, Bytes.compareTo(row, 0, row.length, 296 cell.getRowArray(), cell.getRowOffset(), cell.getRowLength())); 297 } 298 299 for (int j = 0; j < FAMILIES.length; j++) { 300 byte[] actual = result.getValue(FAMILIES[j], FAMILIES[j]); 301 Assert.assertArrayEquals("Row in snapshot does not match, expected:" + Bytes.toString(row) 302 + " ,actual:" + Bytes.toString(actual), row, actual); 303 } 304 } 305 306}