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