/** * 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.snapshot; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.util.Map; import java.util.TreeMap; 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.HConstants; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.SmallTests; import org.apache.hadoop.hbase.regionserver.wal.HLog; import org.apache.hadoop.hbase.regionserver.wal.HLogKey; import org.apache.hadoop.hbase.regionserver.wal.WALEdit; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.FSUtils; import org.junit.*; import org.junit.experimental.categories.Category; /** * Test snapshot log splitter */ @Category(SmallTests.class) public class TestSnapshotLogSplitter { final Log LOG = LogFactory.getLog(getClass()); private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); private byte[] TEST_QUALIFIER = Bytes.toBytes("q"); private byte[] TEST_FAMILY = Bytes.toBytes("f"); private Configuration conf; private FileSystem fs; private Path logFile; @Before public void setup() throws Exception { conf = TEST_UTIL.getConfiguration(); fs = FileSystem.get(conf); logFile = new Path(TEST_UTIL.getDataTestDir(), "test.log"); writeTestLog(logFile); } @After public void tearDown() throws Exception { fs.delete(logFile, false); } @Test public void testSplitLogs() throws IOException { Map<byte[], byte[]> regionsMap = new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR); splitTestLogs(getTableName(5), regionsMap); } @Test public void testSplitLogsOnDifferentTable() throws IOException { byte[] tableName = getTableName(1); Map<byte[], byte[]> regionsMap = new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR); for (int j = 0; j < 10; ++j) { byte[] regionName = getRegionName(tableName, j); byte[] newRegionName = getNewRegionName(tableName, j); regionsMap.put(regionName, newRegionName); } splitTestLogs(tableName, regionsMap); } /* * Split and verify test logs for the specified table */ private void splitTestLogs(final byte[] tableName, final Map<byte[], byte[]> regionsMap) throws IOException { Path tableDir = new Path(TEST_UTIL.getDataTestDir(), Bytes.toString(tableName)); SnapshotLogSplitter logSplitter = new SnapshotLogSplitter(conf, fs, tableDir, tableName, regionsMap); try { logSplitter.splitLog(logFile); } finally { logSplitter.close(); } verifyRecoverEdits(tableDir, tableName, regionsMap); } /* * Verify that every logs in the table directory has just the specified table and regions. */ private void verifyRecoverEdits(final Path tableDir, final byte[] tableName, final Map<byte[], byte[]> regionsMap) throws IOException { for (FileStatus regionStatus: FSUtils.listStatus(fs, tableDir)) { assertTrue(regionStatus.getPath().getName().startsWith(Bytes.toString(tableName))); Path regionEdits = HLog.getRegionDirRecoveredEditsDir(regionStatus.getPath()); byte[] regionName = Bytes.toBytes(regionStatus.getPath().getName()); assertFalse(regionsMap.containsKey(regionName)); for (FileStatus logStatus: FSUtils.listStatus(fs, regionEdits)) { HLog.Reader reader = HLog.getReader(fs, logStatus.getPath(), conf); try { HLog.Entry entry; while ((entry = reader.next()) != null) { HLogKey key = entry.getKey(); assertArrayEquals(tableName, key.getTablename()); assertArrayEquals(regionName, key.getEncodedRegionName()); } } finally { reader.close(); } } } } /* * Write some entries in the log file. * 7 different tables with name "testtb-%d" * 10 region per table with name "tableName-region-%d" * 50 entry with row key "row-%d" */ private void writeTestLog(final Path logFile) throws IOException { fs.mkdirs(logFile.getParent()); HLog.Writer writer = HLog.createWriter(fs, logFile, conf); try { for (int i = 0; i < 7; ++i) { byte[] tableName = getTableName(i); for (int j = 0; j < 10; ++j) { byte[] regionName = getRegionName(tableName, j); for (int k = 0; k < 50; ++k) { byte[] rowkey = Bytes.toBytes("row-" + k); HLogKey key = new HLogKey(regionName, tableName, (long)k, System.currentTimeMillis(), HConstants.DEFAULT_CLUSTER_ID); WALEdit edit = new WALEdit(); edit.add(new KeyValue(rowkey, TEST_FAMILY, TEST_QUALIFIER, rowkey)); writer.append(new HLog.Entry(key, edit)); } } } } finally { writer.close(); } } private byte[] getTableName(int tableId) { return Bytes.toBytes("testtb-" + tableId); } private byte[] getRegionName(final byte[] tableName, int regionId) { return Bytes.toBytes(Bytes.toString(tableName) + "-region-" + regionId); } private byte[] getNewRegionName(final byte[] tableName, int regionId) { return Bytes.toBytes(Bytes.toString(tableName) + "-new-region-" + regionId); } }