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