1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.snapshot;
19  
20  import java.io.IOException;
21  
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  import org.apache.hadoop.hbase.HBaseTestingUtility;
25  import org.apache.hadoop.hbase.LargeTests;
26  import org.apache.hadoop.hbase.client.HBaseAdmin;
27  import org.apache.hadoop.hbase.client.HTable;
28  import org.apache.hadoop.hbase.master.MasterFileSystem;
29  import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
30  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
31  import org.apache.hadoop.hbase.util.Bytes;
32  import org.apache.hadoop.hbase.util.FSUtils;
33  import org.junit.After;
34  import org.junit.AfterClass;
35  import org.junit.Before;
36  import org.junit.BeforeClass;
37  import org.junit.Test;
38  import org.junit.experimental.categories.Category;
39  
40  /**
41   * Test clone/restore snapshots from the client
42   *
43   * TODO This is essentially a clone of TestRestoreSnapshotFromClient.  This is worth refactoring
44   * this because there will be a few more flavors of snapshots that need to run these tests.
45   */
46  @Category(LargeTests.class)
47  public class TestRestoreFlushSnapshotFromClient {
48    final Log LOG = LogFactory.getLog(getClass());
49  
50    private final static HBaseTestingUtility UTIL = new HBaseTestingUtility();
51  
52    private final byte[] FAMILY = Bytes.toBytes("cf");
53  
54    private byte[] snapshotName0;
55    private byte[] snapshotName1;
56    private byte[] snapshotName2;
57    private int snapshot0Rows;
58    private int snapshot1Rows;
59    private byte[] tableName;
60    private HBaseAdmin admin;
61  
62    @BeforeClass
63    public static void setUpBeforeClass() throws Exception {
64      UTIL.getConfiguration().setBoolean("hbase.online.schema.update.enable", true);
65      UTIL.getConfiguration().setInt("hbase.regionserver.msginterval", 100);
66      UTIL.getConfiguration().setInt("hbase.client.pause", 250);
67      UTIL.getConfiguration().setInt("hbase.client.retries.number", 6);
68      UTIL.getConfiguration().setBoolean(
69          "hbase.master.enabletable.roundrobin", true);
70  
71      // Enable snapshot
72      UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
73  
74      UTIL.startMiniCluster(3);
75    }
76  
77    @AfterClass
78    public static void tearDownAfterClass() throws Exception {
79      UTIL.shutdownMiniCluster();
80    }
81  
82    /**
83     * Initialize the tests with a table filled with some data
84     * and two snapshots (snapshotName0, snapshotName1) of different states.
85     * The tableName, snapshotNames and the number of rows in the snapshot are initialized.
86     */
87    @Before
88    public void setup() throws Exception {
89      this.admin = UTIL.getHBaseAdmin();
90  
91      long tid = System.currentTimeMillis();
92      tableName = Bytes.toBytes("testtb-" + tid);
93      snapshotName0 = Bytes.toBytes("snaptb0-" + tid);
94      snapshotName1 = Bytes.toBytes("snaptb1-" + tid);
95      snapshotName2 = Bytes.toBytes("snaptb2-" + tid);
96  
97      // create Table and disable it
98      SnapshotTestingUtils.createTable(UTIL, tableName, FAMILY);
99      HTable table = new HTable(UTIL.getConfiguration(), tableName);
100     SnapshotTestingUtils.loadData(UTIL, table, 500, FAMILY);
101     snapshot0Rows = UTIL.countRows(table);
102     LOG.info("=== before snapshot with 500 rows");
103     logFSTree();
104 
105     // take a snapshot
106     admin.snapshot(Bytes.toString(snapshotName0), Bytes.toString(tableName),
107         SnapshotDescription.Type.FLUSH);
108 
109     LOG.info("=== after snapshot with 500 rows");
110     logFSTree();
111 
112     // insert more data
113     SnapshotTestingUtils.loadData(UTIL, table, 500, FAMILY);
114     snapshot1Rows = UTIL.countRows(table);
115     LOG.info("=== before snapshot with 1000 rows");
116     logFSTree();
117 
118     // take a snapshot of the updated table
119     admin.snapshot(Bytes.toString(snapshotName1), Bytes.toString(tableName),
120         SnapshotDescription.Type.FLUSH);
121     LOG.info("=== after snapshot with 1000 rows");
122     logFSTree();
123     table.close();
124   }
125 
126   @After
127   public void tearDown() throws Exception {
128     SnapshotTestingUtils.deleteAllSnapshots(UTIL.getHBaseAdmin());
129     SnapshotTestingUtils.deleteArchiveDirectory(UTIL);
130   }
131 
132   @Test
133   public void testTakeFlushSnapshot() throws IOException {
134     // taking happens in setup.
135   }
136 
137   @Test
138   public void testRestoreSnapshot() throws IOException {
139     SnapshotTestingUtils.verifyRowCount(UTIL, tableName, snapshot1Rows);
140 
141     // Restore from snapshot-0
142     admin.disableTable(tableName);
143     admin.restoreSnapshot(snapshotName0);
144     logFSTree();
145     admin.enableTable(tableName);
146     LOG.info("=== after restore with 500 row snapshot");
147     logFSTree();
148     SnapshotTestingUtils.verifyRowCount(UTIL, tableName, snapshot0Rows);
149 
150     // Restore from snapshot-1
151     admin.disableTable(tableName);
152     admin.restoreSnapshot(snapshotName1);
153     admin.enableTable(tableName);
154     SnapshotTestingUtils.verifyRowCount(UTIL, tableName, snapshot1Rows);
155   }
156 
157   @Test(expected=SnapshotDoesNotExistException.class)
158   public void testCloneNonExistentSnapshot() throws IOException, InterruptedException {
159     String snapshotName = "random-snapshot-" + System.currentTimeMillis();
160     String tableName = "random-table-" + System.currentTimeMillis();
161     admin.cloneSnapshot(snapshotName, tableName);
162   }
163 
164   @Test
165   public void testCloneSnapshot() throws IOException, InterruptedException {
166     byte[] clonedTableName = Bytes.toBytes("clonedtb-" + System.currentTimeMillis());
167     testCloneSnapshot(clonedTableName, snapshotName0, snapshot0Rows);
168     testCloneSnapshot(clonedTableName, snapshotName1, snapshot1Rows);
169   }
170 
171   private void testCloneSnapshot(final byte[] tableName, final byte[] snapshotName,
172       int snapshotRows) throws IOException, InterruptedException {
173     // create a new table from snapshot
174     admin.cloneSnapshot(snapshotName, tableName);
175     SnapshotTestingUtils.verifyRowCount(UTIL, tableName, snapshotRows);
176 
177     UTIL.deleteTable(tableName);
178   }
179 
180   @Test
181   public void testRestoreSnapshotOfCloned() throws IOException, InterruptedException {
182     byte[] clonedTableName = Bytes.toBytes("clonedtb-" + System.currentTimeMillis());
183     admin.cloneSnapshot(snapshotName0, clonedTableName);
184     SnapshotTestingUtils.verifyRowCount(UTIL, clonedTableName, snapshot0Rows);
185     admin.snapshot(Bytes.toString(snapshotName2), Bytes.toString(clonedTableName), SnapshotDescription.Type.FLUSH);
186     UTIL.deleteTable(clonedTableName);
187 
188     admin.cloneSnapshot(snapshotName2, clonedTableName);
189     SnapshotTestingUtils.verifyRowCount(UTIL, clonedTableName, snapshot0Rows);
190     UTIL.deleteTable(clonedTableName);
191   }
192 
193   // ==========================================================================
194   //  Helpers
195   // ==========================================================================
196   private void logFSTree() throws IOException {
197     MasterFileSystem mfs = UTIL.getMiniHBaseCluster().getMaster().getMasterFileSystem();
198     FSUtils.logFileSystemState(mfs.getFileSystem(), mfs.getRootDir(), LOG);
199   }
200 }