View Javadoc

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.client;
19  
20  import static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.assertFalse;
22  import static org.junit.Assert.fail;
23  
24  import java.io.IOException;
25  import java.util.HashSet;
26  import java.util.Set;
27  
28  import org.apache.hadoop.fs.Path;
29  import org.apache.hadoop.hbase.CategoryBasedTimeout;
30  import org.apache.hadoop.hbase.HBaseTestingUtility;
31  import org.apache.hadoop.hbase.HColumnDescriptor;
32  import org.apache.hadoop.hbase.HConstants;
33  import org.apache.hadoop.hbase.HRegionInfo;
34  import org.apache.hadoop.hbase.HTableDescriptor;
35  import org.apache.hadoop.hbase.MetaTableAccessor;
36  import org.apache.hadoop.hbase.testclassification.LargeTests;
37  import org.apache.hadoop.hbase.TableName;
38  import org.apache.hadoop.hbase.Waiter;
39  import org.apache.hadoop.hbase.master.AssignmentManager;
40  import org.apache.hadoop.hbase.master.MasterFileSystem;
41  import org.apache.hadoop.hbase.master.RegionState;
42  import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
43  import org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException;
44  import org.apache.hadoop.hbase.snapshot.CorruptedSnapshotException;
45  import org.apache.hadoop.hbase.snapshot.SnapshotDoesNotExistException;
46  import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils;
47  import org.apache.hadoop.hbase.util.Bytes;
48  import org.apache.hadoop.hbase.util.FSUtils;
49  import org.junit.After;
50  import org.junit.AfterClass;
51  import org.junit.Before;
52  import org.junit.BeforeClass;
53  import org.junit.Rule;
54  import org.junit.Test;
55  import org.junit.experimental.categories.Category;
56  import org.junit.rules.TestRule;
57  
58  /**
59   * Test restore snapshots from the client
60   */
61  @Category(LargeTests.class)
62  public class TestRestoreSnapshotFromClient {
63    @Rule public final TestRule timeout = CategoryBasedTimeout.builder()
64        .withTimeout(this.getClass())
65        .withLookingForStuckThread(true)
66        .build();
67  
68    protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
69  
70    protected final byte[] FAMILY = Bytes.toBytes("cf");
71  
72    protected byte[] emptySnapshot;
73    protected byte[] snapshotName0;
74    protected byte[] snapshotName1;
75    protected byte[] snapshotName2;
76    protected int snapshot0Rows;
77    protected int snapshot1Rows;
78    protected TableName tableName;
79    protected Admin admin;
80  
81    @BeforeClass
82    public static void setUpBeforeClass() throws Exception {
83      TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
84      TEST_UTIL.getConfiguration().setBoolean("hbase.online.schema.update.enable", true);
85      TEST_UTIL.getConfiguration().setInt("hbase.hstore.compactionThreshold", 10);
86      TEST_UTIL.getConfiguration().setInt("hbase.regionserver.msginterval", 100);
87      TEST_UTIL.getConfiguration().setInt("hbase.client.pause", 250);
88      TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 6);
89      TEST_UTIL.getConfiguration().setBoolean("hbase.master.enabletable.roundrobin", true);
90      // Setting bigger value to avoid catalog janitor execution (parent region cleanup)
91      TEST_UTIL.getConfiguration().setLong("hbase.catalogjanitor.interval", 1800000);
92      TEST_UTIL.startMiniCluster(3);
93    }
94  
95    @AfterClass
96    public static void tearDownAfterClass() throws Exception {
97      TEST_UTIL.shutdownMiniCluster();
98    }
99  
100   /**
101    * Initialize the tests with a table filled with some data
102    * and two snapshots (snapshotName0, snapshotName1) of different states.
103    * The tableName, snapshotNames and the number of rows in the snapshot are initialized.
104    */
105   @Before
106   public void setup() throws Exception {
107     this.admin = TEST_UTIL.getHBaseAdmin();
108 
109     long tid = System.currentTimeMillis();
110     tableName =
111         TableName.valueOf("testtb-" + tid);
112     emptySnapshot = Bytes.toBytes("emptySnaptb-" + tid);
113     snapshotName0 = Bytes.toBytes("snaptb0-" + tid);
114     snapshotName1 = Bytes.toBytes("snaptb1-" + tid);
115     snapshotName2 = Bytes.toBytes("snaptb2-" + tid);
116 
117     // create Table and disable it
118     SnapshotTestingUtils.createTable(TEST_UTIL, tableName, getNumReplicas(), FAMILY);
119     admin.disableTable(tableName);
120 
121     // take an empty snapshot
122     admin.snapshot(emptySnapshot, tableName);
123 
124     // enable table and insert data
125     admin.enableTable(tableName);
126     SnapshotTestingUtils.loadData(TEST_UTIL, tableName, 500, FAMILY);
127     try (Table table = TEST_UTIL.getConnection().getTable(tableName)) {
128       snapshot0Rows = TEST_UTIL.countRows(table);
129     }
130     admin.disableTable(tableName);
131 
132     // take a snapshot
133     admin.snapshot(snapshotName0, tableName);
134 
135     // enable table and insert more data
136     admin.enableTable(tableName);
137     SnapshotTestingUtils.loadData(TEST_UTIL, tableName, 500, FAMILY);
138     try (Table table = TEST_UTIL.getConnection().getTable(tableName)) {
139       snapshot1Rows = TEST_UTIL.countRows(table);
140     }
141   }
142 
143   @After
144   public void tearDown() throws Exception {
145     TEST_UTIL.deleteTable(tableName);
146     SnapshotTestingUtils.deleteAllSnapshots(TEST_UTIL.getHBaseAdmin());
147     SnapshotTestingUtils.deleteArchiveDirectory(TEST_UTIL);
148   }
149 
150   @Test
151   public void testRestoreSnapshot() throws IOException {
152     SnapshotTestingUtils.verifyRowCount(TEST_UTIL, tableName, snapshot1Rows);
153     admin.disableTable(tableName);
154     admin.snapshot(snapshotName1, tableName);
155     // Restore from snapshot-0
156     admin.restoreSnapshot(snapshotName0);
157     admin.enableTable(tableName);
158     SnapshotTestingUtils.verifyRowCount(TEST_UTIL, tableName, snapshot0Rows);
159     SnapshotTestingUtils.verifyReplicasCameOnline(tableName, admin, getNumReplicas());
160 
161     // Restore from emptySnapshot
162     admin.disableTable(tableName);
163     admin.restoreSnapshot(emptySnapshot);
164     admin.enableTable(tableName);
165     SnapshotTestingUtils.verifyRowCount(TEST_UTIL, tableName, 0);
166     SnapshotTestingUtils.verifyReplicasCameOnline(tableName, admin, getNumReplicas());
167 
168     // Restore from snapshot-1
169     admin.disableTable(tableName);
170     admin.restoreSnapshot(snapshotName1);
171     admin.enableTable(tableName);
172     SnapshotTestingUtils.verifyRowCount(TEST_UTIL, tableName, snapshot1Rows);
173     SnapshotTestingUtils.verifyReplicasCameOnline(tableName, admin, getNumReplicas());
174 
175     // Restore from snapshot-1
176     TEST_UTIL.deleteTable(tableName);
177     admin.restoreSnapshot(snapshotName1);
178     SnapshotTestingUtils.verifyRowCount(TEST_UTIL, tableName, snapshot1Rows);
179     SnapshotTestingUtils.verifyReplicasCameOnline(tableName, admin, getNumReplicas());
180   }
181 
182   protected int getNumReplicas() {
183     return 1;
184   }
185 
186   @Test
187   public void testRestoreSchemaChange() throws Exception {
188     byte[] TEST_FAMILY2 = Bytes.toBytes("cf2");
189 
190     HTable table = new HTable(TEST_UTIL.getConfiguration(), tableName);
191 
192     // Add one column family and put some data in it
193     admin.disableTable(tableName);
194     admin.addColumn(tableName, new HColumnDescriptor(TEST_FAMILY2));
195     admin.enableTable(tableName);
196     assertEquals(2, table.getTableDescriptor().getFamilies().size());
197     HTableDescriptor htd = admin.getTableDescriptor(tableName);
198     assertEquals(2, htd.getFamilies().size());
199     SnapshotTestingUtils.loadData(TEST_UTIL, tableName, 500, TEST_FAMILY2);
200     long snapshot2Rows = snapshot1Rows + 500;
201     assertEquals(snapshot2Rows, TEST_UTIL.countRows(table));
202     assertEquals(500, TEST_UTIL.countRows(table, TEST_FAMILY2));
203     Set<String> fsFamilies = getFamiliesFromFS(tableName);
204     assertEquals(2, fsFamilies.size());
205 
206     // Take a snapshot
207     admin.disableTable(tableName);
208     admin.snapshot(snapshotName2, tableName);
209 
210     // Restore the snapshot (without the cf)
211     admin.restoreSnapshot(snapshotName0);
212     admin.enableTable(tableName);
213     assertEquals(1, table.getTableDescriptor().getFamilies().size());
214     try {
215       TEST_UTIL.countRows(table, TEST_FAMILY2);
216       fail("family '" + Bytes.toString(TEST_FAMILY2) + "' should not exists");
217     } catch (NoSuchColumnFamilyException e) {
218       // expected
219     }
220     assertEquals(snapshot0Rows, TEST_UTIL.countRows(table));
221     htd = admin.getTableDescriptor(tableName);
222     assertEquals(1, htd.getFamilies().size());
223     fsFamilies = getFamiliesFromFS(tableName);
224     assertEquals(1, fsFamilies.size());
225 
226     // Restore back the snapshot (with the cf)
227     admin.disableTable(tableName);
228     admin.restoreSnapshot(snapshotName2);
229     admin.enableTable(tableName);
230     htd = admin.getTableDescriptor(tableName);
231     assertEquals(2, htd.getFamilies().size());
232     assertEquals(2, table.getTableDescriptor().getFamilies().size());
233     assertEquals(500, TEST_UTIL.countRows(table, TEST_FAMILY2));
234     assertEquals(snapshot2Rows, TEST_UTIL.countRows(table));
235     fsFamilies = getFamiliesFromFS(tableName);
236     assertEquals(2, fsFamilies.size());
237     table.close();
238   }
239 
240   @Test
241   public void testCloneSnapshotOfCloned() throws IOException, InterruptedException {
242     TableName clonedTableName =
243         TableName.valueOf("clonedtb-" + System.currentTimeMillis());
244     admin.cloneSnapshot(snapshotName0, clonedTableName);
245     SnapshotTestingUtils.verifyRowCount(TEST_UTIL, clonedTableName, snapshot0Rows);
246     SnapshotTestingUtils.verifyReplicasCameOnline(clonedTableName, admin, getNumReplicas());
247     admin.disableTable(clonedTableName);
248     admin.snapshot(snapshotName2, clonedTableName);
249     TEST_UTIL.deleteTable(clonedTableName);
250     waitCleanerRun();
251 
252     admin.cloneSnapshot(snapshotName2, clonedTableName);
253     SnapshotTestingUtils.verifyRowCount(TEST_UTIL, clonedTableName, snapshot0Rows);
254     SnapshotTestingUtils.verifyReplicasCameOnline(clonedTableName, admin, getNumReplicas());
255     TEST_UTIL.deleteTable(clonedTableName);
256   }
257 
258   @Test
259   public void testCloneAndRestoreSnapshot() throws IOException, InterruptedException {
260     TEST_UTIL.deleteTable(tableName);
261     waitCleanerRun();
262 
263     admin.cloneSnapshot(snapshotName0, tableName);
264     SnapshotTestingUtils.verifyRowCount(TEST_UTIL, tableName, snapshot0Rows);
265     SnapshotTestingUtils.verifyReplicasCameOnline(tableName, admin, getNumReplicas());
266     waitCleanerRun();
267 
268     admin.disableTable(tableName);
269     admin.restoreSnapshot(snapshotName0);
270     admin.enableTable(tableName);
271     SnapshotTestingUtils.verifyRowCount(TEST_UTIL, tableName, snapshot0Rows);
272     SnapshotTestingUtils.verifyReplicasCameOnline(tableName, admin, getNumReplicas());
273   }
274 
275   @Test
276   public void testCorruptedSnapshot() throws IOException, InterruptedException {
277     SnapshotTestingUtils.corruptSnapshot(TEST_UTIL, Bytes.toString(snapshotName0));
278     TableName cloneName = TableName.valueOf("corruptedClone-" + System.currentTimeMillis());
279     try {
280       admin.cloneSnapshot(snapshotName0, cloneName);
281       fail("Expected CorruptedSnapshotException, got succeeded cloneSnapshot()");
282     } catch (CorruptedSnapshotException e) {
283       // Got the expected corruption exception.
284       // check for no references of the cloned table.
285       assertFalse(admin.tableExists(cloneName));
286     } catch (Exception e) {
287       fail("Expected CorruptedSnapshotException got: " + e);
288     }
289   }
290 
291   @Test(timeout = 300000)
292   public void testRestoreSnapshotAfterSplit() throws Exception {
293     Admin admin = null;
294     try {
295       admin = TEST_UTIL.getHBaseAdmin();
296       final int regionReplication = admin.getTableDescriptor(tableName).getRegionReplication();
297       // Region count before split
298       final int primaryRegionCountBeforeSplit = MetaTableAccessor
299           .getTableRegions(TEST_UTIL.getZooKeeperWatcher(), TEST_UTIL.getConnection(), tableName)
300           .size() / regionReplication;
301 
302       admin.split(tableName, "m".getBytes());
303       final AssignmentManager am = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager();
304       // Wait for replica region to become online
305       TEST_UTIL.waitFor(60000, 500, new Waiter.Predicate<IOException>() {
306         @Override
307         public boolean evaluate() throws IOException {
308           return am.getRegionStates().getRegionByStateOfTable(tableName).get(RegionState.State.OPEN)
309               .size() == ((primaryRegionCountBeforeSplit + 1) * regionReplication);
310         }
311       });
312 
313       int regionCountAfterSplit = MetaTableAccessor
314           .getTableRegions(TEST_UTIL.getZooKeeperWatcher(), TEST_UTIL.getConnection(), tableName)
315           .size() / regionReplication;
316       // regionCountAfterSplit will contain parent region, so primaryregionCountBeforeSplit + 2
317       assertEquals(primaryRegionCountBeforeSplit + 2, regionCountAfterSplit);
318 
319       String snapshotName = "testRestoreSnapshotAfterSplit-snap";
320       // Create snapshot after table split
321       admin.snapshot(snapshotName, tableName);
322       assertEquals(1,
323         admin.listTableSnapshots("testtb-.*", "testRestoreSnapshotAfterSplit-snap*").size());
324       // Restore snapshot
325       admin.disableTable(tableName);
326       admin.restoreSnapshot(snapshotName);
327 
328       int regionCountAfterRestoreSnapshot = MetaTableAccessor
329           .getTableRegions(TEST_UTIL.getZooKeeperWatcher(), TEST_UTIL.getConnection(), tableName)
330           .size();
331       assertEquals(primaryRegionCountBeforeSplit + 2, regionCountAfterRestoreSnapshot);
332 
333       // Enable the table
334       admin.enableTable(tableName);
335       assertEquals((primaryRegionCountBeforeSplit + 1) * regionReplication,
336         am.getRegionStates().getRegionByStateOfTable(tableName).get(RegionState.State.OPEN).size());
337 
338     } finally {
339       if (admin != null) {
340         try {
341           admin.deleteTableSnapshots("testtb-.*", "testRestoreSnapshotAfterSplit-snap*");
342         } catch (SnapshotDoesNotExistException ignore) {
343         }
344         admin.close();
345       }
346     }
347   }
348 
349   // ==========================================================================
350   //  Helpers
351   // ==========================================================================
352   private void waitCleanerRun() throws InterruptedException {
353     TEST_UTIL.getMiniHBaseCluster().getMaster().getHFileCleaner().choreForTesting();
354   }
355 
356   private Set<String> getFamiliesFromFS(final TableName tableName) throws IOException {
357     MasterFileSystem mfs = TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterFileSystem();
358     Set<String> families = new HashSet<String>();
359     Path tableDir = FSUtils.getTableDir(mfs.getRootDir(), tableName);
360     for (Path regionDir: FSUtils.getRegionDirs(mfs.getFileSystem(), tableDir)) {
361       for (Path familyDir: FSUtils.getFamilyDirs(mfs.getFileSystem(), regionDir)) {
362         families.add(familyDir.getName());
363       }
364     }
365     return families;
366   }
367 
368   protected void splitRegion(final HRegionInfo regionInfo) throws IOException {
369     byte[][] splitPoints = Bytes.split(regionInfo.getStartKey(), regionInfo.getEndKey(), 1);
370     admin.split(regionInfo.getTable(), splitPoints[1]);
371   }
372 }