Repository: hbase Updated Branches: refs/heads/HBASE-7912 cd2712e43 -> 7c6b10ff4
HBASE-16255 Backup/Restore IT (Vladimir Rodionov) Project: http://git-wip-us.apache.org/repos/asf/hbase/repo Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/7c6b10ff Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/7c6b10ff Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/7c6b10ff Branch: refs/heads/HBASE-7912 Commit: 7c6b10ff44404db384987698155d7b96741dd1d8 Parents: cd2712e Author: tedyu <yuzhih...@gmail.com> Authored: Mon Aug 22 18:40:10 2016 -0700 Committer: tedyu <yuzhih...@gmail.com> Committed: Mon Aug 22 18:40:10 2016 -0700 ---------------------------------------------------------------------- .../hbase/IntegrationTestBackupRestore.java | 293 +++++++++++++++++++ .../hadoop/hbase/HBaseTestingUtility.java | 31 +- 2 files changed, 321 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/hbase/blob/7c6b10ff/hbase-it/src/test/java/org/apache/hadoop/hbase/IntegrationTestBackupRestore.java ---------------------------------------------------------------------- diff --git a/hbase-it/src/test/java/org/apache/hadoop/hbase/IntegrationTestBackupRestore.java b/hbase-it/src/test/java/org/apache/hadoop/hbase/IntegrationTestBackupRestore.java new file mode 100644 index 0000000..52f1c32 --- /dev/null +++ b/hbase-it/src/test/java/org/apache/hadoop/hbase/IntegrationTestBackupRestore.java @@ -0,0 +1,293 @@ +/** + * 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; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.List; +import java.util.Set; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hbase.backup.BackupInfo; +import org.apache.hadoop.hbase.backup.BackupInfo.BackupState; +import org.apache.hadoop.hbase.backup.BackupRequest; +import org.apache.hadoop.hbase.backup.BackupType; +import org.apache.hadoop.hbase.backup.RestoreRequest; +import org.apache.hadoop.hbase.backup.impl.BackupSystemTable; +import org.apache.hadoop.hbase.client.Admin; +import org.apache.hadoop.hbase.client.BackupAdmin; +import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.client.HBaseAdmin; +import org.apache.hadoop.hbase.client.HTable; +import org.apache.hadoop.hbase.testclassification.IntegrationTests; +import org.apache.hadoop.hbase.util.RegionSplitter; +import org.apache.hadoop.hbase.util.RegionSplitter.SplitAlgorithm; +import org.apache.hadoop.util.ToolRunner; +import org.hamcrest.CoreMatchers; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import com.google.common.base.Objects; +import com.google.common.collect.Lists; + +/** + * An integration test to detect regressions in HBASE-7912. Create + * a table with many regions, load data, perform series backup/load operations, + * then restore and verify data + * @see <a href="https://issues.apache.org/jira/browse/HBASE-7912">HBASE-7912</a> + */ +@Category(IntegrationTests.class) +public class IntegrationTestBackupRestore extends IntegrationTestBase { + private static final String CLASS_NAME = IntegrationTestBackupRestore.class.getSimpleName(); + protected static final Log LOG = LogFactory.getLog(IntegrationTestBackupRestore.class); + protected static final TableName TABLE_NAME1 = TableName.valueOf(CLASS_NAME + ".table1"); + protected static final TableName TABLE_NAME2 = TableName.valueOf(CLASS_NAME + ".table2"); + protected static final String COLUMN_NAME = "f"; + protected static final String REGION_COUNT_KEY = String.format("hbase.%s.regions.perRS", CLASS_NAME); + protected static final String REGIONSERVER_COUNT_KEY = String.format("hbase.%s.regionServers", + CLASS_NAME); + protected static final int DEFAULT_REGION_COUNT = 10; + protected static final int DEFAULT_REGIONSERVER_COUNT = 5; + protected static int regionsCountPerServer; + protected static int regionServerCount; + protected static final String NB_ROWS_IN_BATCH_KEY = + String.format("hbase.%s.rows-in-batch", CLASS_NAME); + protected static final int DEFAULT_NB_ROWS_IN_BATCH = 20000; + private static int rowsInBatch; + private static String BACKUP_ROOT_DIR = "backupIT"; + + @Before + public void setUp() throws Exception { + util = new IntegrationTestingUtility(); + regionsCountPerServer = util.getConfiguration().getInt(REGION_COUNT_KEY, DEFAULT_REGION_COUNT); + regionServerCount = + util.getConfiguration().getInt(REGIONSERVER_COUNT_KEY, DEFAULT_REGIONSERVER_COUNT); + rowsInBatch = util.getConfiguration().getInt(NB_ROWS_IN_BATCH_KEY, DEFAULT_NB_ROWS_IN_BATCH); + LOG.info(String.format("Initializing cluster with %d region servers.", regionServerCount)); + util.initializeCluster(regionServerCount); + LOG.info("Cluster initialized"); + util.deleteTableIfAny(TABLE_NAME1); + util.deleteTableIfAny(TABLE_NAME2); + util.waitTableAvailable(BackupSystemTable.getTableName()); + LOG.info("Cluster ready"); + } + + + @After + public void tearDown() throws IOException { + LOG.info("Cleaning up after test."); + util.deleteTableIfAny(TABLE_NAME1); + util.deleteTableIfAny(TABLE_NAME2); + cleanUpBackupDir(); + LOG.info("Restoring cluster."); + util.restoreCluster(); + LOG.info("Cluster restored."); + } + + private void cleanUpBackupDir() throws IOException { + FileSystem fs = FileSystem.get(util.getConfiguration()); + fs.delete(new Path(BACKUP_ROOT_DIR), true); + } + + + @Test + public void testBackupRestore() throws Exception { + BACKUP_ROOT_DIR = util.getConfiguration().get("fs.defaultFS") + BACKUP_ROOT_DIR; + createTable(TABLE_NAME1); + createTable(TABLE_NAME2); + runTest(); + } + + private void createTable(TableName tableName) throws Exception { + long startTime, endTime; + HTableDescriptor desc = new HTableDescriptor(tableName); + HColumnDescriptor[] columns = + new HColumnDescriptor[]{new HColumnDescriptor(COLUMN_NAME)}; + SplitAlgorithm algo = new RegionSplitter.UniformSplit(); + LOG.info(String.format("Creating table %s with %d splits.", tableName, + regionsCountPerServer)); + startTime = System.currentTimeMillis(); + HBaseTestingUtility.createPreSplitLoadTestTable(conf, desc, columns, + algo, regionsCountPerServer); + util.waitTableAvailable(tableName); + endTime = System.currentTimeMillis(); + LOG.info(String.format("Pre-split table created successfully in %dms.", + (endTime - startTime))); + } + + private void loadData(TableName table, int numRows) throws IOException { + Connection conn = util.getConnection(); + // #0- insert some data to a table + HTable t1 = (HTable) conn.getTable(table); + util.loadRandomRows(t1, new byte[]{'f'}, 100, numRows); + t1.flushCommits(); + } + + private void runTest() throws IOException { + Connection conn = util.getConnection(); + // #0- insert some data to table TABLE_NAME1, TABLE_NAME2 + loadData(TABLE_NAME1, rowsInBatch); + loadData(TABLE_NAME2, rowsInBatch); + // #1 - create full backup for all tables + LOG.info("create full backup image for all tables"); + List<TableName> tables = Lists.newArrayList(TABLE_NAME1, TABLE_NAME2); + HBaseAdmin admin = null; + admin = (HBaseAdmin) conn.getAdmin(); + BackupRequest request = new BackupRequest(); + request.setBackupType(BackupType.FULL).setTableList(tables).setTargetRootDir(BACKUP_ROOT_DIR); + String backupIdFull = admin.getBackupAdmin().backupTables(request); + assertTrue(checkSucceeded(backupIdFull)); + // #2 - insert some data to table + loadData(TABLE_NAME1, rowsInBatch); + loadData(TABLE_NAME2, rowsInBatch); + HTable t1 = (HTable) conn.getTable(TABLE_NAME1); + Assert.assertThat(util.countRows(t1), CoreMatchers.equalTo(rowsInBatch * 2)); + t1.close(); + HTable t2 = (HTable) conn.getTable(TABLE_NAME2); + Assert.assertThat(util.countRows(t2), CoreMatchers.equalTo(rowsInBatch * 2)); + t2.close(); + // #3 - incremental backup for tables + tables = Lists.newArrayList(TABLE_NAME1, TABLE_NAME2); + request = new BackupRequest(); + request.setBackupType(BackupType.INCREMENTAL).setTableList(tables) + .setTargetRootDir(BACKUP_ROOT_DIR); + String backupIdIncMultiple = admin.getBackupAdmin().backupTables(request); + assertTrue(checkSucceeded(backupIdIncMultiple)); + // #4 - restore full backup for all tables, without overwrite + TableName[] tablesRestoreFull = new TableName[] { TABLE_NAME1, TABLE_NAME2 }; + BackupAdmin client = util.getAdmin().getBackupAdmin(); + client.restore(createRestoreRequest(BACKUP_ROOT_DIR, backupIdFull, false, tablesRestoreFull, + null, false)); + // #5.1 - check tables for full restore + Admin hAdmin = util.getConnection().getAdmin(); + assertTrue(hAdmin.tableExists(TABLE_NAME1)); + assertTrue(hAdmin.tableExists(TABLE_NAME2)); + hAdmin.close(); + // #5.2 - checking row count of tables for full restore + HTable hTable = (HTable) conn.getTable(TABLE_NAME1); + Assert.assertThat(util.countRows(hTable), CoreMatchers.equalTo(rowsInBatch)); + hTable.close(); + hTable = (HTable) conn.getTable(TABLE_NAME2); + Assert.assertThat(util.countRows(hTable), CoreMatchers.equalTo(rowsInBatch)); + hTable.close(); + // #6 - restore incremental backup for multiple tables, with overwrite + TableName[] tablesRestoreIncMultiple = new TableName[] { TABLE_NAME1, TABLE_NAME2 }; + client.restore(createRestoreRequest(BACKUP_ROOT_DIR, backupIdIncMultiple, false, + tablesRestoreIncMultiple, null, true)); + hTable = (HTable) conn.getTable(TABLE_NAME1); + Assert.assertThat(util.countRows(hTable), CoreMatchers.equalTo(rowsInBatch * 2)); + hTable.close(); + hTable = (HTable) conn.getTable(TABLE_NAME2); + Assert.assertThat(util.countRows(hTable), CoreMatchers.equalTo(rowsInBatch * 2)); + hTable.close(); + admin.close(); + conn.close(); + } + + protected boolean checkSucceeded(String backupId) throws IOException { + BackupInfo status = getBackupContext(backupId); + if (status == null) return false; + return status.getState() == BackupState.COMPLETE; + } + + private BackupInfo getBackupContext(String backupId) throws IOException { + try (BackupSystemTable table = new BackupSystemTable(util.getConnection())) { + BackupInfo status = table.readBackupInfo(backupId); + return status; + } + } + + /** + * Get restore request. + */ + public RestoreRequest createRestoreRequest(String backupRootDir, String backupId, boolean check, + TableName[] fromTables, TableName[] toTables, boolean isOverwrite) { + RestoreRequest request = new RestoreRequest(); + request.setBackupRootDir(backupRootDir).setBackupId(backupId).setCheck(check) + .setFromTables(fromTables).setToTables(toTables).setOverwrite(isOverwrite); + return request; + } + + @Override + public void setUpCluster() throws Exception { + util = getTestingUtil(getConf()); + LOG.debug("Initializing/checking cluster has " + regionServerCount + " servers"); + util.initializeCluster(regionServerCount); + LOG.debug("Done initializing/checking cluster"); + } + + @Override + public int runTestFromCommandLine() throws Exception { + testBackupRestore(); + return 0; + } + + @Override + public TableName getTablename() { + return null; + } + + @Override + protected Set<String> getColumnFamilies() { + return null; + } + + @Override + protected void addOptions() { + addOptWithArg(REGIONSERVER_COUNT_KEY, "Total number of region servers. Default: '" + + DEFAULT_REGIONSERVER_COUNT + "'"); + addOptWithArg(REGION_COUNT_KEY, "Total number of regions. Default: " + DEFAULT_REGION_COUNT); + addOptWithArg(NB_ROWS_IN_BATCH_KEY, "Total number of data rows to be loaded (per table/batch." + + " Total number of batches=2). Default: " + DEFAULT_NB_ROWS_IN_BATCH); + + } + + @Override + protected void processOptions(CommandLine cmd) { + super.processOptions(cmd); + regionsCountPerServer = + Integer.parseInt(cmd.getOptionValue(REGION_COUNT_KEY, + Integer.toString(DEFAULT_REGION_COUNT))); + regionServerCount = + Integer.parseInt(cmd.getOptionValue(REGIONSERVER_COUNT_KEY, + Integer.toString(DEFAULT_REGIONSERVER_COUNT))); + rowsInBatch = + Integer.parseInt(cmd.getOptionValue(NB_ROWS_IN_BATCH_KEY, + Integer.toString(DEFAULT_NB_ROWS_IN_BATCH))); + LOG.info(Objects.toStringHelper("Parsed Options").add(REGION_COUNT_KEY, regionsCountPerServer) + .add(REGIONSERVER_COUNT_KEY, regionServerCount).add(NB_ROWS_IN_BATCH_KEY, rowsInBatch) + .toString()); + } + + public static void main(String[] args) throws Exception { + Configuration conf = HBaseConfiguration.create(); + IntegrationTestingUtility.setUseDistributedCluster(conf); + int status = ToolRunner.run(conf, new IntegrationTestBackupRestore(), args); + System.exit(status); + } +} http://git-wip-us.apache.org/repos/asf/hbase/blob/7c6b10ff/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java index 0d830f2..2ab8cb8 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java @@ -115,6 +115,7 @@ import org.apache.hadoop.hbase.util.JVMClusterUtil.MasterThread; import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; import org.apache.hadoop.hbase.util.Pair; import org.apache.hadoop.hbase.util.RegionSplitter; +import org.apache.hadoop.hbase.util.RegionSplitter.SplitAlgorithm; import org.apache.hadoop.hbase.util.RetryCounter; import org.apache.hadoop.hbase.util.Threads; import org.apache.hadoop.hbase.wal.WAL; @@ -2076,6 +2077,18 @@ public class HBaseTestingUtility extends HBaseCommonTestingUtility { } } + public void loadRandomRows(final Table t, final byte[] f, int rowSize, int totalRows) + throws IOException { + Random r = new Random(); + byte[] row = new byte[rowSize]; + for (int i = 0; i < totalRows; i++) { + r.nextBytes(row); + Put put = new Put(row); + put.addColumn(f, new byte[]{0}, new byte[]{0}); + t.put(put); + } + } + public void verifyNumericRows(Table table, final byte[] f, int startRow, int endRow, int replicaId) throws IOException { @@ -3632,7 +3645,7 @@ public class HBaseTestingUtility extends HBaseCommonTestingUtility { return createPreSplitLoadTestTable(conf, desc, new HColumnDescriptor[] {hcd}, numRegionsPerServer); } - + /** * Creates a pre-split table for load testing. If the table already exists, * logs a warning and continues. @@ -3640,12 +3653,24 @@ public class HBaseTestingUtility extends HBaseCommonTestingUtility { */ public static int createPreSplitLoadTestTable(Configuration conf, HTableDescriptor desc, HColumnDescriptor[] hcds, int numRegionsPerServer) throws IOException { + + return createPreSplitLoadTestTable(conf, desc, hcds, + new RegionSplitter.HexStringSplit(), numRegionsPerServer); + } + + /** + * Creates a pre-split table for load testing. If the table already exists, + * logs a warning and continues. + * @return the number of regions the table was split into + */ + public static int createPreSplitLoadTestTable(Configuration conf, + HTableDescriptor desc, HColumnDescriptor[] hcds, + SplitAlgorithm splitter, int numRegionsPerServer) throws IOException { for (HColumnDescriptor hcd : hcds) { if (!desc.hasFamily(hcd.getName())) { desc.addFamily(hcd); } } - int totalNumberOfRegions = 0; Connection unmanagedConnection = ConnectionFactory.createConnection(conf); Admin admin = unmanagedConnection.getAdmin(); @@ -3664,7 +3689,7 @@ public class HBaseTestingUtility extends HBaseCommonTestingUtility { "pre-splitting table into " + totalNumberOfRegions + " regions " + "(regions per server: " + numRegionsPerServer + ")"); - byte[][] splits = new RegionSplitter.HexStringSplit().split( + byte[][] splits = splitter.split( totalNumberOfRegions); admin.createTable(desc, splits);