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.master.snapshot; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023import static org.junit.Assert.fail; 024 025import java.io.IOException; 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.HBaseClassTestRule; 031import org.apache.hadoop.hbase.HBaseTestingUtil; 032import org.apache.hadoop.hbase.Stoppable; 033import org.apache.hadoop.hbase.TableName; 034import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 035import org.apache.hadoop.hbase.client.RegionInfo; 036import org.apache.hadoop.hbase.client.RegionInfoBuilder; 037import org.apache.hadoop.hbase.executor.ExecutorService; 038import org.apache.hadoop.hbase.io.HFileLink; 039import org.apache.hadoop.hbase.master.MasterFileSystem; 040import org.apache.hadoop.hbase.master.MasterServices; 041import org.apache.hadoop.hbase.master.cleaner.DirScanPool; 042import org.apache.hadoop.hbase.master.cleaner.HFileCleaner; 043import org.apache.hadoop.hbase.master.cleaner.HFileLinkCleaner; 044import org.apache.hadoop.hbase.procedure.ProcedureCoordinator; 045import org.apache.hadoop.hbase.regionserver.HRegionFileSystem; 046import org.apache.hadoop.hbase.regionserver.StoreContext; 047import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTracker; 048import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory; 049import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils; 050import org.apache.hadoop.hbase.testclassification.MasterTests; 051import org.apache.hadoop.hbase.testclassification.SmallTests; 052import org.apache.hadoop.hbase.util.CommonFSUtils; 053import org.apache.hadoop.hbase.util.HFileArchiveUtil; 054import org.apache.zookeeper.KeeperException; 055import org.junit.ClassRule; 056import org.junit.Rule; 057import org.junit.Test; 058import org.junit.experimental.categories.Category; 059import org.junit.rules.TestName; 060import org.mockito.Mockito; 061 062/** 063 * Test basic snapshot manager functionality 064 */ 065@Category({ MasterTests.class, SmallTests.class }) 066public class TestSnapshotManager { 067 068 @ClassRule 069 public static final HBaseClassTestRule CLASS_RULE = 070 HBaseClassTestRule.forClass(TestSnapshotManager.class); 071 072 private static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); 073 074 @Rule 075 public TestName name = new TestName(); 076 077 MasterServices services = Mockito.mock(MasterServices.class); 078 ProcedureCoordinator coordinator = Mockito.mock(ProcedureCoordinator.class); 079 ExecutorService pool = Mockito.mock(ExecutorService.class); 080 MasterFileSystem mfs = Mockito.mock(MasterFileSystem.class); 081 FileSystem fs; 082 { 083 try { 084 fs = UTIL.getTestFileSystem(); 085 } catch (IOException e) { 086 throw new RuntimeException("Couldn't get test filesystem", e); 087 } 088 } 089 090 private SnapshotManager getNewManager() throws IOException, KeeperException { 091 return getNewManager(UTIL.getConfiguration()); 092 } 093 094 private SnapshotManager getNewManager(Configuration conf) throws IOException, KeeperException { 095 return getNewManager(conf, 1); 096 } 097 098 private SnapshotManager getNewManager(Configuration conf, int intervalSeconds) 099 throws IOException, KeeperException { 100 Mockito.reset(services); 101 Mockito.when(services.getConfiguration()).thenReturn(conf); 102 Mockito.when(services.getMasterFileSystem()).thenReturn(mfs); 103 Mockito.when(mfs.getFileSystem()).thenReturn(fs); 104 Mockito.when(mfs.getRootDir()).thenReturn(UTIL.getDataTestDir()); 105 return new SnapshotManager(services, coordinator, pool, intervalSeconds); 106 } 107 108 @Test 109 public void testCleanFinishedHandler() throws Exception { 110 TableName tableName = TableName.valueOf(name.getMethodName()); 111 Configuration conf = UTIL.getConfiguration(); 112 try { 113 conf.setLong(SnapshotManager.HBASE_SNAPSHOT_SENTINELS_CLEANUP_TIMEOUT_MILLIS, 5 * 1000L); 114 SnapshotManager manager = getNewManager(conf, 1); 115 TakeSnapshotHandler handler = Mockito.mock(TakeSnapshotHandler.class); 116 assertFalse("Manager is in process when there is no current handler", 117 manager.isTakingSnapshot(tableName)); 118 manager.setSnapshotHandlerForTesting(tableName, handler); 119 Mockito.when(handler.isFinished()).thenReturn(false); 120 assertTrue(manager.isTakingAnySnapshot()); 121 assertTrue("Manager isn't in process when handler is running", 122 manager.isTakingSnapshot(tableName)); 123 Mockito.when(handler.isFinished()).thenReturn(true); 124 assertFalse("Manager is process when handler isn't running", 125 manager.isTakingSnapshot(tableName)); 126 assertTrue(manager.isTakingAnySnapshot()); 127 Thread.sleep(6 * 1000); 128 assertFalse(manager.isTakingAnySnapshot()); 129 } finally { 130 conf.unset(SnapshotManager.HBASE_SNAPSHOT_SENTINELS_CLEANUP_TIMEOUT_MILLIS); 131 } 132 } 133 134 @Test 135 public void testInProcess() throws KeeperException, IOException { 136 final TableName tableName = TableName.valueOf(name.getMethodName()); 137 SnapshotManager manager = getNewManager(); 138 TakeSnapshotHandler handler = Mockito.mock(TakeSnapshotHandler.class); 139 assertFalse("Manager is in process when there is no current handler", 140 manager.isTakingSnapshot(tableName)); 141 manager.setSnapshotHandlerForTesting(tableName, handler); 142 Mockito.when(handler.isFinished()).thenReturn(false); 143 assertTrue("Manager isn't in process when handler is running", 144 manager.isTakingSnapshot(tableName)); 145 Mockito.when(handler.isFinished()).thenReturn(true); 146 assertFalse("Manager is process when handler isn't running", 147 manager.isTakingSnapshot(tableName)); 148 } 149 150 /** 151 * Verify the snapshot support based on the configuration. 152 */ 153 @Test 154 public void testSnapshotSupportConfiguration() throws Exception { 155 // No configuration (no cleaners, not enabled): snapshot feature disabled 156 Configuration conf = new Configuration(); 157 SnapshotManager manager = getNewManager(conf); 158 assertFalse("Snapshot should be disabled with no configuration", isSnapshotSupported(manager)); 159 160 // force snapshot feature to be enabled 161 conf = new Configuration(); 162 conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true); 163 manager = getNewManager(conf); 164 assertTrue("Snapshot should be enabled", isSnapshotSupported(manager)); 165 166 // force snapshot feature to be disabled 167 conf = new Configuration(); 168 conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, false); 169 manager = getNewManager(conf); 170 assertFalse("Snapshot should be disabled", isSnapshotSupported(manager)); 171 172 // force snapshot feature to be disabled, even if cleaners are present 173 conf = new Configuration(); 174 conf.setStrings(HFileCleaner.MASTER_HFILE_CLEANER_PLUGINS, SnapshotHFileCleaner.class.getName(), 175 HFileLinkCleaner.class.getName()); 176 conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, false); 177 manager = getNewManager(conf); 178 assertFalse("Snapshot should be disabled", isSnapshotSupported(manager)); 179 180 // cleaners are present, but missing snapshot enabled property 181 conf = new Configuration(); 182 conf.setStrings(HFileCleaner.MASTER_HFILE_CLEANER_PLUGINS, SnapshotHFileCleaner.class.getName(), 183 HFileLinkCleaner.class.getName()); 184 manager = getNewManager(conf); 185 assertTrue("Snapshot should be enabled, because cleaners are present", 186 isSnapshotSupported(manager)); 187 188 // Create a "test snapshot" 189 Path rootDir = UTIL.getDataTestDir(); 190 Path testSnapshotDir = 191 SnapshotDescriptionUtils.getCompletedSnapshotDir("testSnapshotSupportConfiguration", rootDir); 192 fs.mkdirs(testSnapshotDir); 193 try { 194 // force snapshot feature to be disabled, but snapshots are present 195 conf = new Configuration(); 196 conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, false); 197 manager = getNewManager(conf); 198 fail("Master should not start when snapshot is disabled, but snapshots are present"); 199 } catch (UnsupportedOperationException e) { 200 // expected 201 } finally { 202 fs.delete(testSnapshotDir, true); 203 } 204 } 205 206 @Test 207 public void testDisableSnapshotAndNotDeleteBackReference() throws Exception { 208 Configuration conf = new Configuration(); 209 conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, false); 210 SnapshotManager manager = getNewManager(conf); 211 String cleaners = conf.get(HFileCleaner.MASTER_HFILE_CLEANER_PLUGINS); 212 assertTrue(cleaners != null && cleaners.contains(HFileLinkCleaner.class.getName())); 213 Path rootDir = UTIL.getDataTestDir(); 214 CommonFSUtils.setRootDir(conf, rootDir); 215 216 TableName tableName = TableName.valueOf(name.getMethodName()); 217 TableName tableLinkName = TableName.valueOf(name.getMethodName() + "-link"); 218 String hfileName = "1234567890"; 219 String familyName = "cf"; 220 RegionInfo hri = RegionInfoBuilder.newBuilder(tableName).build(); 221 RegionInfo hriLink = RegionInfoBuilder.newBuilder(tableLinkName).build(); 222 Path archiveDir = HFileArchiveUtil.getArchivePath(conf); 223 Path archiveStoreDir = 224 HFileArchiveUtil.getStoreArchivePath(conf, tableName, hri.getEncodedName(), familyName); 225 226 // Create hfile /hbase/table-link/region/cf/getEncodedName.HFILE(conf); 227 Path familyPath = getFamilyDirPath(archiveDir, tableName, hri.getEncodedName(), familyName); 228 Path hfilePath = new Path(familyPath, hfileName); 229 fs.createNewFile(hfilePath); 230 // Create link to hfile 231 HRegionFileSystem regionFS = HRegionFileSystem.create(conf, fs, 232 CommonFSUtils.getTableDir(rootDir, tableLinkName), hriLink); 233 StoreFileTracker sft = StoreFileTrackerFactory.create(conf, true, 234 StoreContext.getBuilder() 235 .withFamilyStoreDirectoryPath(new Path(regionFS.getRegionDir(), familyName)) 236 .withColumnFamilyDescriptor(ColumnFamilyDescriptorBuilder.of(familyName)) 237 .withRegionFileSystem(regionFS).build()); 238 sft.createHFileLink(hri.getTable(), hri.getEncodedName(), hfileName, true); 239 Path linkBackRefDir = HFileLink.getBackReferencesDir(archiveStoreDir, hfileName); 240 assertTrue(fs.exists(linkBackRefDir)); 241 FileStatus[] backRefs = fs.listStatus(linkBackRefDir); 242 assertEquals(1, backRefs.length); 243 Path linkBackRef = backRefs[0].getPath(); 244 245 // Initialize cleaner 246 HFileCleaner cleaner = new HFileCleaner(10000, Mockito.mock(Stoppable.class), conf, fs, 247 archiveDir, DirScanPool.getHFileCleanerScanPool(conf)); 248 // Link backref and HFile cannot be removed 249 cleaner.choreForTesting(); 250 assertTrue(fs.exists(linkBackRef)); 251 assertTrue(fs.exists(hfilePath)); 252 253 fs.rename(CommonFSUtils.getTableDir(rootDir, tableLinkName), 254 CommonFSUtils.getTableDir(archiveDir, tableLinkName)); 255 // Link backref can be removed 256 cleaner.choreForTesting(); 257 assertFalse("Link should be deleted", fs.exists(linkBackRef)); 258 // HFile can be removed 259 cleaner.choreForTesting(); 260 assertFalse("HFile should be deleted", fs.exists(hfilePath)); 261 } 262 263 private Path getFamilyDirPath(final Path rootDir, final TableName table, final String region, 264 final String family) { 265 return new Path(new Path(CommonFSUtils.getTableDir(rootDir, table), region), family); 266 } 267 268 private boolean isSnapshotSupported(final SnapshotManager manager) { 269 try { 270 manager.checkSnapshotSupport(); 271 return true; 272 } catch (UnsupportedOperationException e) { 273 return false; 274 } 275 } 276}