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.client;
019
020import static org.junit.Assert.assertEquals;
021
022import java.io.IOException;
023import java.io.InterruptedIOException;
024import java.util.Optional;
025import org.apache.hadoop.hbase.HBaseClassTestRule;
026import org.apache.hadoop.hbase.HBaseTestingUtility;
027import org.apache.hadoop.hbase.HColumnDescriptor;
028import org.apache.hadoop.hbase.HConstants;
029import org.apache.hadoop.hbase.HTableDescriptor;
030import org.apache.hadoop.hbase.TableName;
031import org.apache.hadoop.hbase.coprocessor.ObserverContext;
032import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor;
033import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
034import org.apache.hadoop.hbase.coprocessor.RegionObserver;
035import org.apache.hadoop.hbase.master.cleaner.TimeToLiveHFileCleaner;
036import org.apache.hadoop.hbase.mob.MobConstants;
037import org.apache.hadoop.hbase.regionserver.FlushLifeCycleTracker;
038import org.apache.hadoop.hbase.snapshot.MobSnapshotTestingUtils;
039import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils;
040import org.apache.hadoop.hbase.testclassification.ClientTests;
041import org.apache.hadoop.hbase.testclassification.LargeTests;
042import org.apache.hadoop.hbase.util.Bytes;
043import org.junit.BeforeClass;
044import org.junit.ClassRule;
045import org.junit.Rule;
046import org.junit.Test;
047import org.junit.experimental.categories.Category;
048import org.junit.rules.TestName;
049
050/**
051 * Test clone snapshots from the client
052 */
053@Category({LargeTests.class, ClientTests.class})
054public class TestMobCloneSnapshotFromClient extends TestCloneSnapshotFromClient {
055
056  @ClassRule
057  public static final HBaseClassTestRule CLASS_RULE =
058      HBaseClassTestRule.forClass(TestMobCloneSnapshotFromClient.class);
059
060  private static boolean delayFlush = false;
061
062  @Rule
063  public TestName name = new TestName();
064
065  protected static void setupConfiguration() {
066    TestCloneSnapshotFromClient.setupConfiguration();
067    TEST_UTIL.getConfiguration().setLong(TimeToLiveHFileCleaner.TTL_CONF_KEY, 0);
068    TEST_UTIL.getConfiguration().setInt(MobConstants.MOB_FILE_CACHE_SIZE_KEY, 0);
069  }
070
071  @BeforeClass
072  public static void setUpBeforeClass() throws Exception {
073    setupConfiguration();
074    TEST_UTIL.startMiniCluster(3);
075  }
076
077  @Override
078  protected void createTableAndSnapshots() throws Exception {
079    // create Table and disable it
080    createMobTable(TEST_UTIL, tableName, SnapshotTestingUtils.getSplitKeys(), getNumReplicas(),
081      FAMILY);
082    delayFlush = false;
083    admin.disableTable(tableName);
084
085    // take an empty snapshot
086    admin.snapshot(emptySnapshot, tableName);
087
088    Connection c = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
089    Table table = c.getTable(tableName);
090    try {
091      // enable table and insert data
092      admin.enableTable(tableName);
093      SnapshotTestingUtils.loadData(TEST_UTIL, tableName, 20, FAMILY);
094      snapshot0Rows = MobSnapshotTestingUtils.countMobRows(table);
095      admin.disableTable(tableName);
096
097      // take a snapshot
098      admin.snapshot(snapshotName0, tableName);
099
100      // enable table and insert more data
101      admin.enableTable(tableName);
102      SnapshotTestingUtils.loadData(TEST_UTIL, tableName, 20, FAMILY);
103      snapshot1Rows = MobSnapshotTestingUtils.countMobRows(table);
104      admin.disableTable(tableName);
105
106      // take a snapshot of the updated table
107      admin.snapshot(snapshotName1, tableName);
108
109      // re-enable table
110      admin.enableTable(tableName);
111    } finally {
112      table.close();
113    }
114  }
115
116  @Test
117  @Override
118  public void testCloneLinksAfterDelete() throws IOException, InterruptedException {
119    // delay the flush to make sure
120    delayFlush = true;
121    SnapshotTestingUtils.loadData(TEST_UTIL, tableName, 20, FAMILY);
122    long tid = System.currentTimeMillis();
123    byte[] snapshotName3 = Bytes.toBytes("snaptb3-" + tid);
124    TableName clonedTableName3 = TableName.valueOf(name.getMethodName() + System.currentTimeMillis());
125    admin.snapshot(snapshotName3, tableName);
126    delayFlush = false;
127    int snapshot3Rows = -1;
128    try (Table table = TEST_UTIL.getConnection().getTable(tableName)) {
129      snapshot3Rows = TEST_UTIL.countRows(table);
130    }
131    admin.cloneSnapshot(snapshotName3, clonedTableName3);
132    admin.deleteSnapshot(snapshotName3);
133    super.testCloneLinksAfterDelete();
134    verifyRowCount(TEST_UTIL, clonedTableName3, snapshot3Rows);
135    admin.disableTable(clonedTableName3);
136    admin.deleteTable(clonedTableName3);
137  }
138
139  @Override
140  protected void verifyRowCount(final HBaseTestingUtility util, final TableName tableName,
141      long expectedRows) throws IOException {
142    MobSnapshotTestingUtils.verifyMobRowCount(util, tableName, expectedRows);
143  }
144
145  /**
146   * This coprocessor is used to delay the flush.
147   */
148  public static class DelayFlushCoprocessor implements RegionCoprocessor, RegionObserver {
149
150    @Override
151    public Optional<RegionObserver> getRegionObserver() {
152      return Optional.of(this);
153    }
154
155    @Override
156    public void preFlush(ObserverContext<RegionCoprocessorEnvironment> e,
157        FlushLifeCycleTracker tracker) throws IOException {
158      if (delayFlush) {
159        try {
160          if (Bytes.compareTo(e.getEnvironment().getRegionInfo().getStartKey(),
161            HConstants.EMPTY_START_ROW) != 0) {
162            Thread.sleep(100);
163          }
164        } catch (InterruptedException e1) {
165          throw new InterruptedIOException(e1.getMessage());
166        }
167      }
168    }
169  }
170
171  private void createMobTable(final HBaseTestingUtility util, final TableName tableName,
172    final byte[][] splitKeys, int regionReplication, final byte[]... families) throws IOException,
173    InterruptedException {
174    HTableDescriptor htd = new HTableDescriptor(tableName);
175    htd.setRegionReplication(regionReplication);
176    htd.addCoprocessor(DelayFlushCoprocessor.class.getName());
177    for (byte[] family : families) {
178      HColumnDescriptor hcd = new HColumnDescriptor(family);
179      hcd.setMobEnabled(true);
180      hcd.setMobThreshold(0L);
181      htd.addFamily(hcd);
182    }
183    util.getAdmin().createTable(htd, splitKeys);
184    SnapshotTestingUtils.waitForTableToBeOnline(util, tableName);
185    assertEquals((splitKeys.length + 1) * regionReplication,
186      util.getAdmin().getTableRegions(tableName).size());
187  }
188}