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.master.snapshot; 19 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertFalse; 22 import static org.junit.Assert.assertTrue; 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.when; 25 26 import java.io.IOException; 27 import java.util.*; 28 import java.util.concurrent.atomic.AtomicInteger; 29 30 import com.google.common.collect.Iterables; 31 import com.google.common.collect.Lists; 32 import org.apache.commons.logging.Log; 33 import org.apache.commons.logging.LogFactory; 34 import org.apache.hadoop.fs.FileStatus; 35 import org.apache.hadoop.fs.FileSystem; 36 import org.apache.hadoop.fs.Path; 37 import org.apache.hadoop.hbase.HBaseTestingUtility; 38 import org.apache.hadoop.hbase.HRegionInfo; 39 import org.apache.hadoop.hbase.protobuf.generated.SnapshotProtos; 40 import org.apache.hadoop.hbase.testclassification.MediumTests; 41 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription; 42 import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils; 43 import org.apache.hadoop.hbase.snapshot.SnapshotReferenceUtil; 44 import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils.SnapshotMock; 45 import org.apache.hadoop.hbase.util.FSUtils; 46 import org.junit.After; 47 import org.junit.AfterClass; 48 import org.junit.BeforeClass; 49 import org.junit.Test; 50 import org.junit.experimental.categories.Category; 51 52 /** 53 * Test that we correctly reload the cache, filter directories, etc. 54 */ 55 @Category(MediumTests.class) 56 public class TestSnapshotFileCache { 57 58 private static final Log LOG = LogFactory.getLog(TestSnapshotFileCache.class); 59 private static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); 60 private static long sequenceId = 0; 61 private static FileSystem fs; 62 private static Path rootDir; 63 64 @BeforeClass startCluster()65 public static void startCluster() throws Exception { 66 UTIL.startMiniDFSCluster(1); 67 fs = UTIL.getDFSCluster().getFileSystem(); 68 rootDir = UTIL.getDefaultRootDirPath(); 69 } 70 71 @AfterClass stopCluster()72 public static void stopCluster() throws Exception { 73 UTIL.shutdownMiniDFSCluster(); 74 } 75 76 @After cleanupFiles()77 public void cleanupFiles() throws Exception { 78 // cleanup the snapshot directory 79 Path snapshotDir = SnapshotDescriptionUtils.getSnapshotsDir(rootDir); 80 fs.delete(snapshotDir, true); 81 } 82 83 @Test(timeout = 10000000) testLoadAndDelete()84 public void testLoadAndDelete() throws IOException { 85 // don't refresh the cache unless we tell it to 86 long period = Long.MAX_VALUE; 87 SnapshotFileCache cache = new SnapshotFileCache(fs, rootDir, period, 10000000, 88 "test-snapshot-file-cache-refresh", new SnapshotFiles()); 89 90 createAndTestSnapshotV1(cache, "snapshot1a", false, true); 91 createAndTestSnapshotV1(cache, "snapshot1b", true, true); 92 93 createAndTestSnapshotV2(cache, "snapshot2a", false, true); 94 createAndTestSnapshotV2(cache, "snapshot2b", true, true); 95 } 96 97 @Test testJustFindLogsDirectory()98 public void testJustFindLogsDirectory() throws Exception { 99 // don't refresh the cache unless we tell it to 100 long period = Long.MAX_VALUE; 101 Path snapshotDir = SnapshotDescriptionUtils.getSnapshotsDir(rootDir); 102 SnapshotFileCache cache = new SnapshotFileCache(fs, rootDir, period, 10000000, 103 "test-snapshot-file-cache-refresh", new SnapshotFileCache.SnapshotFileInspector() { 104 public Collection<String> filesUnderSnapshot(final Path snapshotDir) 105 throws IOException { 106 return SnapshotReferenceUtil.getWALNames(fs, snapshotDir); 107 } 108 }); 109 110 // create a file in a 'completed' snapshot 111 SnapshotDescription desc = SnapshotDescription.newBuilder().setName("snapshot").build(); 112 Path snapshot = SnapshotDescriptionUtils.getCompletedSnapshotDir(desc, rootDir); 113 SnapshotDescriptionUtils.writeSnapshotInfo(desc, snapshot, fs); 114 Path file1 = new Path(new Path(new Path(snapshot, "7e91021"), "fam"), "file1"); 115 fs.createNewFile(file1); 116 117 // and another file in the logs directory 118 Path logs = SnapshotReferenceUtil.getLogsDir(snapshot, "server"); 119 Path log = new Path(logs, "me.hbase.com%2C58939%2C1350424310315.1350424315552"); 120 fs.createNewFile(log); 121 122 FSUtils.logFileSystemState(fs, rootDir, LOG); 123 124 // then make sure the cache only finds the log files 125 Iterable<FileStatus> notSnapshot = getNonSnapshotFiles(cache, file1); 126 assertFalse("Cache found '" + file1 + "', but it shouldn't have.", 127 Iterables.contains(notSnapshot, file1.getName())); 128 notSnapshot = getNonSnapshotFiles(cache, log); 129 assertTrue("Cache didn't find:" + log, !Iterables.contains(notSnapshot, log)); 130 } 131 132 @Test testReloadModifiedDirectory()133 public void testReloadModifiedDirectory() throws IOException { 134 // don't refresh the cache unless we tell it to 135 long period = Long.MAX_VALUE; 136 SnapshotFileCache cache = new SnapshotFileCache(fs, rootDir, period, 10000000, 137 "test-snapshot-file-cache-refresh", new SnapshotFiles()); 138 139 createAndTestSnapshotV1(cache, "snapshot1", false, true); 140 // now delete the snapshot and add a file with a different name 141 createAndTestSnapshotV1(cache, "snapshot1", false, false); 142 143 createAndTestSnapshotV2(cache, "snapshot2", false, true); 144 // now delete the snapshot and add a file with a different name 145 createAndTestSnapshotV2(cache, "snapshot2", false, false); 146 } 147 148 @Test testSnapshotTempDirReload()149 public void testSnapshotTempDirReload() throws IOException { 150 long period = Long.MAX_VALUE; 151 // This doesn't refresh cache until we invoke it explicitly 152 SnapshotFileCache cache = new SnapshotFileCache(fs, rootDir, period, 10000000, 153 "test-snapshot-file-cache-refresh", new SnapshotFiles()); 154 155 // Add a new non-tmp snapshot 156 createAndTestSnapshotV1(cache, "snapshot0v1", false, false); 157 createAndTestSnapshotV1(cache, "snapshot0v2", false, false); 158 159 // Add a new tmp snapshot 160 createAndTestSnapshotV2(cache, "snapshot1", true, false); 161 162 // Add another tmp snapshot 163 createAndTestSnapshotV2(cache, "snapshot2", true, false); 164 } 165 166 @Test testWeNeverCacheTmpDirAndLoadIt()167 public void testWeNeverCacheTmpDirAndLoadIt() throws Exception { 168 169 final AtomicInteger count = new AtomicInteger(0); 170 // don't refresh the cache unless we tell it to 171 long period = Long.MAX_VALUE; 172 SnapshotFileCache cache = new SnapshotFileCache(fs, rootDir, period, 10000000, 173 "test-snapshot-file-cache-refresh", new SnapshotFiles()) { 174 @Override 175 List<String> getSnapshotsInProgress() throws IOException { 176 List<String> result = super.getSnapshotsInProgress(); 177 count.incrementAndGet(); 178 return result; 179 } 180 181 @Override public void triggerCacheRefreshForTesting() { 182 super.triggerCacheRefreshForTesting(); 183 } 184 }; 185 186 SnapshotMock.SnapshotBuilder complete = 187 createAndTestSnapshotV1(cache, "snapshot", false, false); 188 189 SnapshotMock.SnapshotBuilder inProgress = 190 createAndTestSnapshotV1(cache, "snapshotInProgress", true, false); 191 192 int countBeforeCheck = count.get(); 193 194 FSUtils.logFileSystemState(fs, rootDir, LOG); 195 196 List<FileStatus> allStoreFiles = getStoreFilesForSnapshot(complete); 197 Iterable<FileStatus> deletableFiles = cache.getUnreferencedFiles(allStoreFiles); 198 assertTrue(Iterables.isEmpty(deletableFiles)); 199 // no need for tmp dir check as all files are accounted for. 200 assertEquals(0, count.get() - countBeforeCheck); 201 202 203 // add a random file to make sure we refresh 204 FileStatus randomFile = mockStoreFile(UUID.randomUUID().toString()); 205 allStoreFiles.add(randomFile); 206 deletableFiles = cache.getUnreferencedFiles(allStoreFiles); 207 assertEquals(randomFile, Iterables.getOnlyElement(deletableFiles)); 208 assertEquals(1, count.get() - countBeforeCheck); // we check the tmp directory 209 } 210 getStoreFilesForSnapshot(SnapshotMock.SnapshotBuilder builder)211 private List<FileStatus> getStoreFilesForSnapshot(SnapshotMock.SnapshotBuilder builder) 212 throws IOException { 213 final List<FileStatus> allStoreFiles = Lists.newArrayList(); 214 SnapshotReferenceUtil 215 .visitReferencedFiles(UTIL.getConfiguration(), fs, builder.getSnapshotsDir(), 216 new SnapshotReferenceUtil.SnapshotVisitor() { 217 @Override public void logFile(String server, String logfile) throws IOException { 218 // do nothing. 219 } 220 221 @Override public void storeFile(HRegionInfo regionInfo, String familyName, 222 SnapshotProtos.SnapshotRegionManifest.StoreFile storeFile) throws IOException { 223 FileStatus status = mockStoreFile(storeFile.getName()); 224 allStoreFiles.add(status); 225 } 226 }); 227 return allStoreFiles; 228 } 229 mockStoreFile(String storeFileName)230 private FileStatus mockStoreFile(String storeFileName) { 231 FileStatus status = mock(FileStatus.class); 232 Path path = mock(Path.class); 233 when(path.getName()).thenReturn(storeFileName); 234 when(status.getPath()).thenReturn(path); 235 return status; 236 } 237 238 class SnapshotFiles implements SnapshotFileCache.SnapshotFileInspector { filesUnderSnapshot(final Path snapshotDir)239 public Collection<String> filesUnderSnapshot(final Path snapshotDir) throws IOException { 240 Collection<String> files = new HashSet<String>(); 241 files.addAll(SnapshotReferenceUtil.getWALNames(fs, snapshotDir)); 242 files.addAll(SnapshotReferenceUtil.getHFileNames(UTIL.getConfiguration(), fs, snapshotDir)); 243 return files; 244 } 245 }; 246 createAndTestSnapshotV1(final SnapshotFileCache cache, final String name, final boolean tmp, final boolean removeOnExit)247 private SnapshotMock.SnapshotBuilder createAndTestSnapshotV1(final SnapshotFileCache cache, 248 final String name, final boolean tmp, final boolean removeOnExit) throws IOException { 249 SnapshotMock snapshotMock = new SnapshotMock(UTIL.getConfiguration(), fs, rootDir); 250 SnapshotMock.SnapshotBuilder builder = snapshotMock.createSnapshotV1(name, name); 251 createAndTestSnapshot(cache, builder, tmp, removeOnExit); 252 return builder; 253 } 254 createAndTestSnapshotV2(final SnapshotFileCache cache, final String name, final boolean tmp, final boolean removeOnExit)255 private void createAndTestSnapshotV2(final SnapshotFileCache cache, final String name, 256 final boolean tmp, final boolean removeOnExit) throws IOException { 257 SnapshotMock snapshotMock = new SnapshotMock(UTIL.getConfiguration(), fs, rootDir); 258 SnapshotMock.SnapshotBuilder builder = snapshotMock.createSnapshotV2(name, name); 259 createAndTestSnapshot(cache, builder, tmp, removeOnExit); 260 } 261 createAndTestSnapshot(final SnapshotFileCache cache, final SnapshotMock.SnapshotBuilder builder, final boolean tmp, final boolean removeOnExit)262 private void createAndTestSnapshot(final SnapshotFileCache cache, 263 final SnapshotMock.SnapshotBuilder builder, 264 final boolean tmp, final boolean removeOnExit) throws IOException { 265 List<Path> files = new ArrayList<Path>(); 266 for (int i = 0; i < 3; ++i) { 267 for (Path filePath: builder.addRegion()) { 268 String fileName = filePath.getName(); 269 if (tmp) { 270 // We should be able to find all the files while the snapshot creation is in-progress 271 FSUtils.logFileSystemState(fs, rootDir, LOG); 272 Iterable<FileStatus> nonSnapshot = getNonSnapshotFiles(cache, filePath); 273 assertFalse("Cache didn't find " + fileName, Iterables.contains(nonSnapshot, fileName)); 274 } 275 files.add(filePath); 276 } 277 } 278 279 // Finalize the snapshot 280 if (!tmp) { 281 builder.commit(); 282 } 283 284 // Make sure that all files are still present 285 for (Path path: files) { 286 Iterable<FileStatus> nonSnapshotFiles = getNonSnapshotFiles(cache, path); 287 assertFalse("Cache didn't find " + path.getName(), 288 Iterables.contains(nonSnapshotFiles, path.getName())); 289 } 290 291 FSUtils.logFileSystemState(fs, rootDir, LOG); 292 if (removeOnExit) { 293 LOG.debug("Deleting snapshot."); 294 fs.delete(builder.getSnapshotsDir(), true); 295 FSUtils.logFileSystemState(fs, rootDir, LOG); 296 297 // The files should be in cache until next refresh 298 for (Path filePath: files) { 299 Iterable<FileStatus> nonSnapshotFiles = getNonSnapshotFiles(cache, filePath); 300 assertFalse("Cache didn't find " + filePath.getName(), Iterables.contains(nonSnapshotFiles, 301 filePath.getName())); 302 } 303 304 // then trigger a refresh 305 cache.triggerCacheRefreshForTesting(); 306 // and not it shouldn't find those files 307 for (Path filePath: files) { 308 Iterable<FileStatus> nonSnapshotFiles = getNonSnapshotFiles(cache, filePath); 309 assertTrue("Cache found '" + filePath.getName() + "', but it shouldn't have.", 310 !Iterables.contains(nonSnapshotFiles, filePath.getName())); 311 } 312 } 313 } 314 getNonSnapshotFiles(SnapshotFileCache cache, Path storeFile)315 private Iterable<FileStatus> getNonSnapshotFiles(SnapshotFileCache cache, Path storeFile) 316 throws IOException { 317 return cache.getUnreferencedFiles( 318 Arrays.asList(FSUtils.listStatus(fs, storeFile.getParent())) 319 ); 320 } 321 } 322