/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.regionserver.HRegion; import org.apache.hadoop.hbase.regionserver.Store; import org.apache.hadoop.hbase.regionserver.HStore; /** * Test helper for testing archiving of HFiles */ public class HFileArchiveTestingUtil { private static final Log LOG = LogFactory.getLog(HFileArchiveTestingUtil.class); private HFileArchiveTestingUtil() { // NOOP private ctor since this is just a utility class } public static boolean compareArchiveToOriginal(FileStatus[] previous, FileStatus[] archived, FileSystem fs, boolean hasTimedBackup) { List<List<String>> lists = getFileLists(previous, archived); List<String> original = lists.get(0); Collections.sort(original); List<String> currentFiles = lists.get(1); Collections.sort(currentFiles); List<String> backedup = lists.get(2); Collections.sort(backedup); // check the backed up files versus the current (should match up, less the // backup time in the name) if (!hasTimedBackup == (backedup.size() > 0)) { LOG.debug("backedup files doesn't match expected."); return false; } String msg = null; if (hasTimedBackup) { msg = assertArchiveEquality(original, backedup); if (msg != null) { LOG.debug(msg); return false; } } msg = assertArchiveEquality(original, currentFiles); if (msg != null) { LOG.debug(msg); return false; } return true; } /** * Compare the archived files to the files in the original directory * @param expected original files that should have been archived * @param actual files that were archived * @param fs filessystem on which the archiving took place * @throws IOException */ public static void assertArchiveEqualToOriginal(FileStatus[] expected, FileStatus[] actual, FileSystem fs) throws IOException { assertArchiveEqualToOriginal(expected, actual, fs, false); } /** * Compare the archived files to the files in the original directory * @param expected original files that should have been archived * @param actual files that were archived * @param fs {@link FileSystem} on which the archiving took place * @param hasTimedBackup <tt>true</tt> if we expect to find an archive backup directory with a * copy of the files in the archive directory (and the original files). * @throws IOException */ public static void assertArchiveEqualToOriginal(FileStatus[] expected, FileStatus[] actual, FileSystem fs, boolean hasTimedBackup) throws IOException { List<List<String>> lists = getFileLists(expected, actual); List<String> original = lists.get(0); Collections.sort(original); List<String> currentFiles = lists.get(1); Collections.sort(currentFiles); List<String> backedup = lists.get(2); Collections.sort(backedup); // check the backed up files versus the current (should match up, less the // backup time in the name) assertEquals("Didn't expect any backup files, but got: " + backedup, hasTimedBackup, backedup.size() > 0); String msg = null; if (hasTimedBackup) { assertArchiveEquality(original, backedup); assertNull(msg, msg); } // do the rest of the comparison msg = assertArchiveEquality(original, currentFiles); assertNull(msg, msg); } private static String assertArchiveEquality(List<String> expected, List<String> archived) { String compare = compareFileLists(expected, archived); if (!(expected.size() == archived.size())) return "Not the same number of current files\n" + compare; if (!expected.equals(archived)) return "Different backup files, but same amount\n" + compare; return null; } /** * @return <expected, gotten, backup>, where each is sorted */ private static List<List<String>> getFileLists(FileStatus[] previous, FileStatus[] archived) { List<List<String>> files = new ArrayList<List<String>>(); // copy over the original files List<String> originalFileNames = convertToString(previous); files.add(originalFileNames); List<String> currentFiles = new ArrayList<String>(previous.length); List<FileStatus> backedupFiles = new ArrayList<FileStatus>(previous.length); for (FileStatus f : archived) { String name = f.getPath().getName(); // if the file has been backed up if (name.contains(".")) { Path parent = f.getPath().getParent(); String shortName = name.split("[.]")[0]; Path modPath = new Path(parent, shortName); FileStatus file = new FileStatus(f.getLen(), f.isDir(), f.getReplication(), f.getBlockSize(), f.getModificationTime(), modPath); backedupFiles.add(file); } else { // otherwise, add it to the list to compare to the original store files currentFiles.add(name); } } files.add(currentFiles); files.add(convertToString(backedupFiles)); return files; } private static List<String> convertToString(FileStatus[] files) { return convertToString(Arrays.asList(files)); } private static List<String> convertToString(List<FileStatus> files) { List<String> originalFileNames = new ArrayList<String>(files.size()); for (FileStatus f : files) { originalFileNames.add(f.getPath().getName()); } return originalFileNames; } /* Get a pretty representation of the differences */ private static String compareFileLists(List<String> expected, List<String> gotten) { StringBuilder sb = new StringBuilder("Expected (" + expected.size() + "): \t\t Gotten (" + gotten.size() + "):\n"); List<String> notFound = new ArrayList<String>(); for (String s : expected) { if (gotten.contains(s)) sb.append(s + "\t\t" + s + "\n"); else notFound.add(s); } sb.append("Not Found:\n"); for (String s : notFound) { sb.append(s + "\n"); } sb.append("\nExtra:\n"); for (String s : gotten) { if (!expected.contains(s)) sb.append(s + "\n"); } return sb.toString(); } /** * Helper method to get the archive directory for the specified region * @param conf {@link Configuration} to check for the name of the archive directory * @param region region that is being archived * @return {@link Path} to the archive directory for the given region */ public static Path getRegionArchiveDir(Configuration conf, HRegion region) { return HFileArchiveUtil.getRegionArchiveDir(conf, region.getTableDir(), region.getRegionDir()); } /** * Helper method to get the store archive directory for the specified region * @param conf {@link Configuration} to check for the name of the archive directory * @param region region that is being archived * @param store store that is archiving files * @return {@link Path} to the store archive directory for the given region */ public static Path getStoreArchivePath(Configuration conf, HRegion region, Store store) { return HFileArchiveUtil.getStoreArchivePath(conf, region, store.getFamily().getName()); } public static Path getStoreArchivePath(HBaseTestingUtility util, String tableName, byte[] storeName) throws IOException { byte[] table = Bytes.toBytes(tableName); // get the RS and region serving our table List<HRegion> servingRegions = util.getHBaseCluster().getRegions(table); HRegion region = servingRegions.get(0); // check that we actually have some store files that were archived Store store = region.getStore(storeName); return HFileArchiveTestingUtil.getStoreArchivePath(util.getConfiguration(), region, store); } }