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