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;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertTrue;
023import static org.junit.Assert.fail;
024
025import java.io.IOException;
026import java.util.HashMap;
027import java.util.List;
028import java.util.Map;
029import org.apache.hadoop.conf.Configuration;
030import org.apache.hadoop.hbase.HBaseClassTestRule;
031import org.apache.hadoop.hbase.HBaseTestingUtil;
032import org.apache.hadoop.hbase.TableName;
033import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
034import org.apache.hadoop.hbase.snapshot.SnapshotTTLExpiredException;
035import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils;
036import org.apache.hadoop.hbase.testclassification.ClientTests;
037import org.apache.hadoop.hbase.testclassification.LargeTests;
038import org.apache.hadoop.hbase.util.Bytes;
039import org.apache.hadoop.hbase.util.Threads;
040import org.junit.After;
041import org.junit.AfterClass;
042import org.junit.Before;
043import org.junit.BeforeClass;
044import org.junit.ClassRule;
045import org.junit.Test;
046import org.junit.experimental.categories.Category;
047import org.slf4j.Logger;
048import org.slf4j.LoggerFactory;
049
050/**
051 * Test restore/clone snapshots with TTL from the client
052 */
053@Category({ LargeTests.class, ClientTests.class })
054public class TestSnapshotWithTTLFromClient {
055
056  @ClassRule
057  public static final HBaseClassTestRule CLASS_RULE =
058    HBaseClassTestRule.forClass(TestSnapshotWithTTLFromClient.class);
059
060  private static final Logger LOG = LoggerFactory.getLogger(TestSnapshotWithTTLFromClient.class);
061
062  private static final HBaseTestingUtil UTIL = new HBaseTestingUtil();
063  private static final int NUM_RS = 2;
064  private static final String STRING_TABLE_NAME = "test";
065  private static final byte[] TEST_FAM = Bytes.toBytes("fam");
066  private static final TableName TABLE_NAME = TableName.valueOf(STRING_TABLE_NAME);
067  private static final TableName CLONED_TABLE_NAME = TableName.valueOf("clonedTable");
068  private static final String TTL_KEY = "TTL";
069  private static final int CHORE_INTERVAL_SECS = 30;
070
071  /**
072   * Setup the config for the cluster
073   * @throws Exception on failure
074   */
075  @BeforeClass
076  public static void setupCluster() throws Exception {
077    setupConf(UTIL.getConfiguration());
078    UTIL.startMiniCluster(NUM_RS);
079  }
080
081  protected static void setupConf(Configuration conf) {
082    // Enable snapshot
083    conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
084
085    // Set this to high value so that cleaner chore is not triggered
086    conf.setInt("hbase.master.cleaner.snapshot.interval", CHORE_INTERVAL_SECS * 60 * 1000);
087  }
088
089  @Before
090  public void setup() throws Exception {
091    createTable();
092  }
093
094  protected void createTable() throws Exception {
095    UTIL.createTable(TABLE_NAME, new byte[][] { TEST_FAM });
096  }
097
098  @After
099  public void tearDown() throws Exception {
100    UTIL.deleteTableIfAny(TABLE_NAME);
101    UTIL.deleteTableIfAny(CLONED_TABLE_NAME);
102    SnapshotTestingUtils.deleteAllSnapshots(UTIL.getAdmin());
103    SnapshotTestingUtils.deleteArchiveDirectory(UTIL);
104  }
105
106  @AfterClass
107  public static void cleanupTest() throws Exception {
108    try {
109      UTIL.shutdownMiniCluster();
110    } catch (Exception e) {
111      LOG.warn("failure shutting down cluster", e);
112    }
113  }
114
115  @Test
116  public void testRestoreSnapshotWithTTLSuccess() throws Exception {
117    String snapshotName = "nonExpiredTTLRestoreSnapshotTest";
118
119    // table should exist
120    assertTrue(UTIL.getAdmin().tableExists(TABLE_NAME));
121
122    // create snapshot fo given table with specified ttl
123    createSnapshotWithTTL(TABLE_NAME, snapshotName, CHORE_INTERVAL_SECS * 2);
124    Admin admin = UTIL.getAdmin();
125
126    // Disable and drop table
127    admin.disableTable(TABLE_NAME);
128    admin.deleteTable(TABLE_NAME);
129    assertFalse(UTIL.getAdmin().tableExists(TABLE_NAME));
130
131    // restore snapshot
132    admin.restoreSnapshot(snapshotName);
133
134    // table should be created
135    assertTrue(UTIL.getAdmin().tableExists(TABLE_NAME));
136  }
137
138  @Test
139  public void testRestoreSnapshotFailsDueToTTLExpired() throws Exception {
140    String snapshotName = "expiredTTLRestoreSnapshotTest";
141
142    // table should exist
143    assertTrue(UTIL.getAdmin().tableExists(TABLE_NAME));
144
145    // create snapshot fo given table with specified ttl
146    createSnapshotWithTTL(TABLE_NAME, snapshotName, 1);
147    Admin admin = UTIL.getAdmin();
148
149    // Disable and drop table
150    admin.disableTable(TABLE_NAME);
151    admin.deleteTable(TABLE_NAME);
152    assertFalse(UTIL.getAdmin().tableExists(TABLE_NAME));
153
154    // Sleep so that TTL may expire
155    Threads.sleep(2000);
156
157    // restore snapshot which has expired
158    try {
159      admin.restoreSnapshot(snapshotName);
160      fail("Restore snapshot succeeded even though TTL has expired.");
161    } catch (SnapshotTTLExpiredException e) {
162      LOG.info("Correctly failed to restore a TTL expired snapshot table:" + e.getMessage());
163    }
164
165    // table should not be created
166    assertFalse(UTIL.getAdmin().tableExists(TABLE_NAME));
167  }
168
169  @Test
170  public void testCloneSnapshotWithTTLSuccess() throws Exception {
171    String snapshotName = "nonExpiredTTLCloneSnapshotTest";
172
173    // table should exist
174    assertTrue(UTIL.getAdmin().tableExists(TABLE_NAME));
175
176    // create snapshot fo given table with specified ttl
177    createSnapshotWithTTL(TABLE_NAME, snapshotName, CHORE_INTERVAL_SECS * 2);
178    Admin admin = UTIL.getAdmin();
179
180    // restore snapshot
181    admin.cloneSnapshot(snapshotName, CLONED_TABLE_NAME);
182
183    // table should be created
184    assertTrue(UTIL.getAdmin().tableExists(CLONED_TABLE_NAME));
185  }
186
187  @Test
188  public void testCloneSnapshotFailsDueToTTLExpired() throws Exception {
189    String snapshotName = "expiredTTLCloneSnapshotTest";
190
191    // table should exist
192    assertTrue(UTIL.getAdmin().tableExists(TABLE_NAME));
193
194    // create snapshot fo given table with specified ttl
195    createSnapshotWithTTL(TABLE_NAME, snapshotName, 1);
196    Admin admin = UTIL.getAdmin();
197
198    assertTrue(UTIL.getAdmin().tableExists(TABLE_NAME));
199
200    // Sleep so that TTL may expire
201    Threads.sleep(2000);
202
203    // clone snapshot which has expired
204    try {
205      admin.cloneSnapshot(snapshotName, CLONED_TABLE_NAME);
206      fail("Clone snapshot succeeded even though TTL has expired.");
207    } catch (SnapshotTTLExpiredException e) {
208      LOG.info("Correctly failed to clone a TTL expired snapshot table:" + e.getMessage());
209    }
210
211    // table should not be created
212    assertFalse(UTIL.getAdmin().tableExists(CLONED_TABLE_NAME));
213  }
214
215  private void createSnapshotWithTTL(TableName tableName, final String snapshotName,
216    final int snapshotTTL) throws IOException {
217    Admin admin = UTIL.getAdmin();
218
219    // make sure we don't fail on listing snapshots
220    SnapshotTestingUtils.assertNoSnapshots(admin);
221
222    // put some stuff in the table
223    Table table = UTIL.getConnection().getTable(tableName);
224    UTIL.loadTable(table, TEST_FAM);
225
226    Map<String, Object> props = new HashMap<>();
227    props.put(TTL_KEY, snapshotTTL);
228
229    // take a snapshot of the table
230    SnapshotTestingUtils.snapshot(UTIL.getAdmin(), snapshotName, tableName, SnapshotType.FLUSH, 3,
231      props);
232    LOG.debug("Snapshot completed.");
233
234    // make sure we have the snapshot with expectd TTL
235    List<SnapshotDescription> snapshots =
236      SnapshotTestingUtils.assertOneSnapshotThatMatches(admin, snapshotName, tableName);
237    assertEquals(1, snapshots.size());
238    assertEquals(snapshotTTL, snapshots.get(0).getTtl());
239  }
240}