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}