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