001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.mapreduce; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertNotNull; 022import static org.junit.Assert.assertNull; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025 026import java.io.ByteArrayOutputStream; 027import java.io.IOException; 028import java.io.PrintStream; 029import org.apache.hadoop.conf.Configuration; 030import org.apache.hadoop.hbase.Cell; 031import org.apache.hadoop.hbase.CellUtil; 032import org.apache.hadoop.hbase.HBaseClassTestRule; 033import org.apache.hadoop.hbase.HBaseTestingUtility; 034import org.apache.hadoop.hbase.TableName; 035import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 036import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 037import org.apache.hadoop.hbase.client.Get; 038import org.apache.hadoop.hbase.client.Put; 039import org.apache.hadoop.hbase.client.Result; 040import org.apache.hadoop.hbase.client.Table; 041import org.apache.hadoop.hbase.client.TableDescriptor; 042import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 043import org.apache.hadoop.hbase.mob.MobTestUtil; 044import org.apache.hadoop.hbase.testclassification.LargeTests; 045import org.apache.hadoop.hbase.testclassification.MapReduceTests; 046import org.apache.hadoop.hbase.util.Bytes; 047import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 048import org.apache.hadoop.hbase.util.LauncherSecurityManager; 049import org.apache.hadoop.util.ToolRunner; 050import org.junit.AfterClass; 051import org.junit.Assert; 052import org.junit.BeforeClass; 053import org.junit.ClassRule; 054import org.junit.Rule; 055import org.junit.Test; 056import org.junit.experimental.categories.Category; 057import org.junit.rules.TestName; 058 059/** 060 * Basic test for the CopyTable M/R tool 061 */ 062@Category({ MapReduceTests.class, LargeTests.class }) 063public class TestCopyTable { 064 065 @ClassRule 066 public static final HBaseClassTestRule CLASS_RULE = 067 HBaseClassTestRule.forClass(TestCopyTable.class); 068 069 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 070 private static final byte[] ROW1 = Bytes.toBytes("row1"); 071 private static final byte[] ROW2 = Bytes.toBytes("row2"); 072 private static final String FAMILY_A_STRING = "a"; 073 private static final String FAMILY_B_STRING = "b"; 074 private static final byte[] FAMILY_A = Bytes.toBytes(FAMILY_A_STRING); 075 private static final byte[] FAMILY_B = Bytes.toBytes(FAMILY_B_STRING); 076 private static final byte[] QUALIFIER = Bytes.toBytes("q"); 077 078 @Rule 079 public TestName name = new TestName(); 080 081 @BeforeClass 082 public static void beforeClass() throws Exception { 083 TEST_UTIL.startMiniCluster(3); 084 } 085 086 @AfterClass 087 public static void afterClass() throws Exception { 088 TEST_UTIL.shutdownMiniCluster(); 089 } 090 091 private void doCopyTableTest(boolean bulkload) throws Exception { 092 final TableName tableName1 = TableName.valueOf(name.getMethodName() + "1"); 093 final TableName tableName2 = TableName.valueOf(name.getMethodName() + "2"); 094 final byte[] FAMILY = Bytes.toBytes("family"); 095 final byte[] COLUMN1 = Bytes.toBytes("c1"); 096 097 try (Table t1 = TEST_UTIL.createTable(tableName1, FAMILY); 098 Table t2 = TEST_UTIL.createTable(tableName2, FAMILY);) { 099 // put rows into the first table 100 loadData(t1, FAMILY, COLUMN1); 101 102 CopyTable copy = new CopyTable(); 103 int code; 104 if (bulkload) { 105 code = ToolRunner.run(new Configuration(TEST_UTIL.getConfiguration()), copy, 106 new String[] { "--new.name=" + tableName2.getNameAsString(), "--bulkload", 107 tableName1.getNameAsString() }); 108 } else { 109 code = ToolRunner.run(new Configuration(TEST_UTIL.getConfiguration()), copy, new String[] { 110 "--new.name=" + tableName2.getNameAsString(), tableName1.getNameAsString() }); 111 } 112 assertEquals("copy job failed", 0, code); 113 114 // verify the data was copied into table 2 115 verifyRows(t2, FAMILY, COLUMN1); 116 } finally { 117 TEST_UTIL.deleteTable(tableName1); 118 TEST_UTIL.deleteTable(tableName2); 119 } 120 } 121 122 private void doCopyTableTestWithMob(boolean bulkload) throws Exception { 123 final TableName tableName1 = TableName.valueOf(name.getMethodName() + "1"); 124 final TableName tableName2 = TableName.valueOf(name.getMethodName() + "2"); 125 final byte[] FAMILY = Bytes.toBytes("mob"); 126 final byte[] COLUMN1 = Bytes.toBytes("c1"); 127 128 ColumnFamilyDescriptorBuilder cfd = ColumnFamilyDescriptorBuilder.newBuilder(FAMILY); 129 130 cfd.setMobEnabled(true); 131 cfd.setMobThreshold(5); 132 TableDescriptor desc1 = 133 TableDescriptorBuilder.newBuilder(tableName1).setColumnFamily(cfd.build()).build(); 134 TableDescriptor desc2 = 135 TableDescriptorBuilder.newBuilder(tableName2).setColumnFamily(cfd.build()).build(); 136 137 try (Table t1 = TEST_UTIL.createTable(desc1, null); 138 Table t2 = TEST_UTIL.createTable(desc2, null);) { 139 140 // put rows into the first table 141 for (int i = 0; i < 10; i++) { 142 Put p = new Put(Bytes.toBytes("row" + i)); 143 p.addColumn(FAMILY, COLUMN1, COLUMN1); 144 t1.put(p); 145 } 146 147 CopyTable copy = new CopyTable(); 148 149 int code; 150 if (bulkload) { 151 code = ToolRunner.run(new Configuration(TEST_UTIL.getConfiguration()), copy, 152 new String[] { "--new.name=" + tableName2.getNameAsString(), "--bulkload", 153 tableName1.getNameAsString() }); 154 } else { 155 code = ToolRunner.run(new Configuration(TEST_UTIL.getConfiguration()), copy, new String[] { 156 "--new.name=" + tableName2.getNameAsString(), tableName1.getNameAsString() }); 157 } 158 assertEquals("copy job failed", 0, code); 159 160 // verify the data was copied into table 2 161 for (int i = 0; i < 10; i++) { 162 Get g = new Get(Bytes.toBytes("row" + i)); 163 Result r = t2.get(g); 164 assertEquals(1, r.size()); 165 assertTrue(CellUtil.matchingQualifier(r.rawCells()[0], COLUMN1)); 166 assertEquals("compare row values between two tables", 167 t1.getDescriptor().getValue("row" + i), t2.getDescriptor().getValue("row" + i)); 168 } 169 170 assertEquals("compare count of mob rows after table copy", 171 MobTestUtil.countMobRows(TEST_UTIL, t1), MobTestUtil.countMobRows(TEST_UTIL, t2)); 172 assertEquals("compare count of mob row values between two tables", 173 t1.getDescriptor().getValues().size(), t2.getDescriptor().getValues().size()); 174 assertTrue("The mob row count is 0 but should be > 0", 175 MobTestUtil.countMobRows(TEST_UTIL, t2) > 0); 176 } finally { 177 TEST_UTIL.deleteTable(tableName1); 178 TEST_UTIL.deleteTable(tableName2); 179 } 180 } 181 182 /** 183 * Simple end-to-end test 184 */ 185 @Test 186 public void testCopyTable() throws Exception { 187 doCopyTableTest(false); 188 } 189 190 /** 191 * Simple end-to-end test with bulkload. 192 */ 193 @Test 194 public void testCopyTableWithBulkload() throws Exception { 195 doCopyTableTest(true); 196 } 197 198 /** 199 * Simple end-to-end test on table with MOB 200 */ 201 @Test 202 public void testCopyTableWithMob() throws Exception { 203 doCopyTableTestWithMob(false); 204 } 205 206 /** 207 * Simple end-to-end test with bulkload on table with MOB. 208 */ 209 @Test 210 public void testCopyTableWithBulkloadWithMob() throws Exception { 211 doCopyTableTestWithMob(true); 212 } 213 214 @Test 215 public void testStartStopRow() throws Exception { 216 final TableName tableName1 = TableName.valueOf(name.getMethodName() + "1"); 217 final TableName tableName2 = TableName.valueOf(name.getMethodName() + "2"); 218 final byte[] FAMILY = Bytes.toBytes("family"); 219 final byte[] COLUMN1 = Bytes.toBytes("c1"); 220 final byte[] ROW0 = Bytes.toBytesBinary("\\x01row0"); 221 final byte[] ROW1 = Bytes.toBytesBinary("\\x01row1"); 222 final byte[] ROW2 = Bytes.toBytesBinary("\\x01row2"); 223 224 Table t1 = TEST_UTIL.createTable(tableName1, FAMILY); 225 Table t2 = TEST_UTIL.createTable(tableName2, FAMILY); 226 227 // put rows into the first table 228 Put p = new Put(ROW0); 229 p.addColumn(FAMILY, COLUMN1, COLUMN1); 230 t1.put(p); 231 p = new Put(ROW1); 232 p.addColumn(FAMILY, COLUMN1, COLUMN1); 233 t1.put(p); 234 p = new Put(ROW2); 235 p.addColumn(FAMILY, COLUMN1, COLUMN1); 236 t1.put(p); 237 238 CopyTable copy = new CopyTable(); 239 assertEquals(0, 240 ToolRunner.run(new Configuration(TEST_UTIL.getConfiguration()), copy, 241 new String[] { "--new.name=" + tableName2, "--startrow=\\x01row1", "--stoprow=\\x01row2", 242 tableName1.getNameAsString() })); 243 244 // verify the data was copied into table 2 245 // row1 exist, row0, row2 do not exist 246 Get g = new Get(ROW1); 247 Result r = t2.get(g); 248 assertEquals(1, r.size()); 249 assertTrue(CellUtil.matchingQualifier(r.rawCells()[0], COLUMN1)); 250 251 g = new Get(ROW0); 252 r = t2.get(g); 253 assertEquals(0, r.size()); 254 255 g = new Get(ROW2); 256 r = t2.get(g); 257 assertEquals(0, r.size()); 258 259 t1.close(); 260 t2.close(); 261 TEST_UTIL.deleteTable(tableName1); 262 TEST_UTIL.deleteTable(tableName2); 263 } 264 265 /** 266 * Test copy of table from sourceTable to targetTable all rows from family a 267 */ 268 @Test 269 public void testRenameFamily() throws Exception { 270 final TableName sourceTable = TableName.valueOf(name.getMethodName() + "source"); 271 final TableName targetTable = TableName.valueOf(name.getMethodName() + "-target"); 272 273 byte[][] families = { FAMILY_A, FAMILY_B }; 274 275 Table t = TEST_UTIL.createTable(sourceTable, families); 276 Table t2 = TEST_UTIL.createTable(targetTable, families); 277 Put p = new Put(ROW1); 278 p.addColumn(FAMILY_A, QUALIFIER, Bytes.toBytes("Data11")); 279 p.addColumn(FAMILY_B, QUALIFIER, Bytes.toBytes("Data12")); 280 p.addColumn(FAMILY_A, QUALIFIER, Bytes.toBytes("Data13")); 281 t.put(p); 282 p = new Put(ROW2); 283 p.addColumn(FAMILY_B, QUALIFIER, Bytes.toBytes("Dat21")); 284 p.addColumn(FAMILY_A, QUALIFIER, Bytes.toBytes("Data22")); 285 p.addColumn(FAMILY_B, QUALIFIER, Bytes.toBytes("Data23")); 286 t.put(p); 287 288 long currentTime = EnvironmentEdgeManager.currentTime(); 289 String[] args = new String[] { "--new.name=" + targetTable, "--families=a:b", "--all.cells", 290 "--starttime=" + (currentTime - 100000), "--endtime=" + (currentTime + 100000), 291 "--versions=1", sourceTable.getNameAsString() }; 292 assertNull(t2.get(new Get(ROW1)).getRow()); 293 294 assertTrue(runCopy(args)); 295 296 assertNotNull(t2.get(new Get(ROW1)).getRow()); 297 Result res = t2.get(new Get(ROW1)); 298 byte[] b1 = res.getValue(FAMILY_B, QUALIFIER); 299 assertEquals("Data13", new String(b1)); 300 assertNotNull(t2.get(new Get(ROW2)).getRow()); 301 res = t2.get(new Get(ROW2)); 302 b1 = res.getValue(FAMILY_A, QUALIFIER); 303 // Data from the family of B is not copied 304 assertNull(b1); 305 306 } 307 308 /** 309 * Test main method of CopyTable. 310 */ 311 @Test 312 public void testMainMethod() throws Exception { 313 String[] emptyArgs = { "-h" }; 314 PrintStream oldWriter = System.err; 315 ByteArrayOutputStream data = new ByteArrayOutputStream(); 316 PrintStream writer = new PrintStream(data); 317 System.setErr(writer); 318 SecurityManager SECURITY_MANAGER = System.getSecurityManager(); 319 LauncherSecurityManager newSecurityManager = new LauncherSecurityManager(); 320 System.setSecurityManager(newSecurityManager); 321 try { 322 CopyTable.main(emptyArgs); 323 fail("should be exit"); 324 } catch (SecurityException e) { 325 assertEquals(1, newSecurityManager.getExitCode()); 326 } finally { 327 System.setErr(oldWriter); 328 System.setSecurityManager(SECURITY_MANAGER); 329 } 330 assertTrue(data.toString().contains("rs.class")); 331 // should print usage information 332 assertTrue(data.toString().contains("Usage:")); 333 } 334 335 private boolean runCopy(String[] args) throws Exception { 336 int status = 337 ToolRunner.run(new Configuration(TEST_UTIL.getConfiguration()), new CopyTable(), args); 338 return status == 0; 339 } 340 341 private void loadData(Table t, byte[] family, byte[] column) throws IOException { 342 for (int i = 0; i < 10; i++) { 343 byte[] row = Bytes.toBytes("row" + i); 344 Put p = new Put(row); 345 p.addColumn(family, column, row); 346 t.put(p); 347 } 348 } 349 350 private void verifyRows(Table t, byte[] family, byte[] column) throws IOException { 351 for (int i = 0; i < 10; i++) { 352 byte[] row = Bytes.toBytes("row" + i); 353 Get g = new Get(row).addFamily(family); 354 Result r = t.get(g); 355 Assert.assertNotNull(r); 356 Assert.assertEquals(1, r.size()); 357 Cell cell = r.rawCells()[0]; 358 Assert.assertTrue(CellUtil.matchingQualifier(cell, column)); 359 Assert.assertEquals(Bytes.compareTo(cell.getValueArray(), cell.getValueOffset(), 360 cell.getValueLength(), row, 0, row.length), 0); 361 } 362 } 363 364 private Table createTable(TableName tableName, byte[] family, boolean isMob) throws IOException { 365 if (isMob) { 366 ColumnFamilyDescriptor cfd = ColumnFamilyDescriptorBuilder.newBuilder(family) 367 .setMobEnabled(true).setMobThreshold(1).build(); 368 TableDescriptor desc = 369 TableDescriptorBuilder.newBuilder(tableName).setColumnFamily(cfd).build(); 370 return TEST_UTIL.createTable(desc, null); 371 } else { 372 return TEST_UTIL.createTable(tableName, family); 373 } 374 } 375 376 private void testCopyTableBySnapshot(String tablePrefix, boolean bulkLoad, boolean isMob) 377 throws Exception { 378 TableName table1 = TableName.valueOf(tablePrefix + 1); 379 TableName table2 = TableName.valueOf(tablePrefix + 2); 380 Table t1 = createTable(table1, FAMILY_A, isMob); 381 Table t2 = createTable(table2, FAMILY_A, isMob); 382 loadData(t1, FAMILY_A, Bytes.toBytes("qualifier")); 383 String snapshot = tablePrefix + "_snapshot"; 384 TEST_UTIL.getAdmin().snapshot(snapshot, table1); 385 boolean success; 386 if (bulkLoad) { 387 success = 388 runCopy(new String[] { "--snapshot", "--new.name=" + table2, "--bulkload", snapshot }); 389 } else { 390 success = runCopy(new String[] { "--snapshot", "--new.name=" + table2, snapshot }); 391 } 392 Assert.assertTrue(success); 393 verifyRows(t2, FAMILY_A, Bytes.toBytes("qualifier")); 394 } 395 396 @Test 397 public void testLoadingSnapshotToTable() throws Exception { 398 testCopyTableBySnapshot("testLoadingSnapshotToTable", false, false); 399 } 400 401 @Test 402 public void tsetLoadingSnapshotToMobTable() throws Exception { 403 testCopyTableBySnapshot("testLoadingSnapshotToMobTable", false, true); 404 } 405 406 @Test 407 public void testLoadingSnapshotAndBulkLoadToTable() throws Exception { 408 testCopyTableBySnapshot("testLoadingSnapshotAndBulkLoadToTable", true, false); 409 } 410 411 @Test 412 public void testLoadingSnapshotAndBulkLoadToMobTable() throws Exception { 413 testCopyTableBySnapshot("testLoadingSnapshotAndBulkLoadToMobTable", true, true); 414 } 415 416 @Test 417 public void testLoadingSnapshotToRemoteCluster() throws Exception { 418 Assert.assertFalse(runCopy( 419 new String[] { "--snapshot", "--peerAdr=hbase://remoteHBase", "sourceSnapshotName" })); 420 } 421 422 @Test 423 public void testLoadingSnapshotWithoutSnapshotName() throws Exception { 424 Assert.assertFalse(runCopy(new String[] { "--snapshot", "--peerAdr=hbase://remoteHBase" })); 425 } 426 427 @Test 428 public void testLoadingSnapshotWithoutDestTable() throws Exception { 429 Assert.assertFalse(runCopy(new String[] { "--snapshot", "sourceSnapshotName" })); 430 } 431}