1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.client;
20
21 import java.util.List;
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.apache.hadoop.conf.Configuration;
25 import org.apache.hadoop.fs.FileSystem;
26 import org.apache.hadoop.fs.Path;
27 import org.apache.hadoop.hbase.HBaseTestingUtility;
28 import org.apache.hadoop.hbase.HColumnDescriptor;
29 import org.apache.hadoop.hbase.HConstants;
30 import org.apache.hadoop.hbase.HRegionInfo;
31 import org.apache.hadoop.hbase.HTableDescriptor;
32 import org.apache.hadoop.hbase.TableName;
33 import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
34 import org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy;
35 import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils;
36 import org.apache.hadoop.hbase.snapshot.TestRestoreFlushSnapshotFromClient;
37 import org.apache.hadoop.hbase.testclassification.ClientTests;
38 import org.apache.hadoop.hbase.testclassification.MediumTests;
39 import org.apache.hadoop.hbase.util.Bytes;
40 import org.apache.hadoop.hbase.util.Threads;
41 import org.junit.After;
42 import org.junit.AfterClass;
43 import org.junit.Assert;
44 import org.junit.Before;
45 import org.junit.BeforeClass;
46 import org.junit.Rule;
47 import org.junit.Test;
48 import org.junit.Ignore;
49 import org.junit.experimental.categories.Category;
50 import org.junit.rules.TestName;
51 import org.junit.rules.Timeout;
52
53
54
55
56 @Category({MediumTests.class, ClientTests.class})
57 public class TestSnapshotCloneIndependence {
58 private static final Log LOG = LogFactory.getLog(TestSnapshotCloneIndependence.class);
59
60 @Rule
61 public Timeout globalTimeout = Timeout.seconds(60);
62
63 @Rule
64 public TestName testName = new TestName();
65
66 protected static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
67
68 protected static final int NUM_RS = 2;
69 private static final String STRING_TABLE_NAME = "test";
70 private static final String TEST_FAM_STR = "fam";
71 protected static final byte[] TEST_FAM = Bytes.toBytes(TEST_FAM_STR);
72 private static final int CLEANER_INTERVAL = 100;
73
74 private FileSystem fs;
75 private Path rootDir;
76 private Admin admin;
77 private TableName originalTableName;
78 private Table originalTable;
79 private TableName cloneTableName;
80 private int countOriginalTable;
81 String snapshotNameAsString;
82 byte[] snapshotName;
83
84
85
86
87 @BeforeClass
88 public static void setupCluster() throws Exception {
89 setupConf(UTIL.getConfiguration());
90 UTIL.startMiniCluster(NUM_RS);
91 }
92
93 static void setupConf(Configuration conf) {
94
95 conf.setInt(HConstants.REGION_SERVER_HIGH_PRIORITY_HANDLER_COUNT, 15);
96
97 conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
98
99 conf.setInt("hbase.regionsever.info.port", -1);
100 conf.setInt("hbase.master.info.port", -1);
101
102 conf.setInt("hbase.hregion.memstore.flush.size", 25000);
103
104
105 conf.setInt("hbase.hstore.compaction.min", 10);
106 conf.setInt("hbase.hstore.compactionThreshold", 10);
107
108 conf.setInt("hbase.hstore.blockingStoreFiles", 12);
109 conf.setInt("hbase.regionserver.msginterval", 100);
110 conf.setBoolean("hbase.master.enabletable.roundrobin", true);
111
112 conf.set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY,
113 ConstantSizeRegionSplitPolicy.class.getName());
114
115 conf.setInt("hbase.master.cleaner.interval", CLEANER_INTERVAL);
116 conf.setInt("hbase.master.hfilecleaner.plugins.snapshot.period", CLEANER_INTERVAL);
117
118
119
120 conf.setInt("hbase.master.hfilecleaner.ttl", CLEANER_INTERVAL);
121 }
122
123 @Before
124 public void setup() throws Exception {
125 fs = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getFileSystem();
126 rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();
127
128 admin = UTIL.getHBaseAdmin();
129 originalTableName = TableName.valueOf("test" + testName.getMethodName());
130 cloneTableName = TableName.valueOf("test-clone-" + originalTableName);
131 snapshotNameAsString = "snapshot_" + originalTableName;
132 snapshotName = Bytes.toBytes(snapshotNameAsString);
133
134 originalTable = createTable(originalTableName, TEST_FAM);
135 loadData(originalTable, TEST_FAM);
136 countOriginalTable = countRows(originalTable);
137 System.out.println("Original table has: " + countOriginalTable + " rows");
138 }
139
140 @After
141 public void tearDown() throws Exception {
142 UTIL.deleteTable(originalTableName);
143 UTIL.deleteTable(cloneTableName);
144 SnapshotTestingUtils.deleteAllSnapshots(UTIL.getHBaseAdmin());
145 SnapshotTestingUtils.deleteArchiveDirectory(UTIL);
146 }
147
148 @AfterClass
149 public static void cleanupTest() throws Exception {
150 try {
151 UTIL.shutdownMiniCluster();
152 } catch (Exception e) {
153 LOG.warn("failure shutting down cluster", e);
154 }
155 }
156
157
158
159
160
161 @Test
162 public void testOnlineSnapshotAppendIndependent() throws Exception {
163 createAndCloneSnapshot(true);
164 runTestSnapshotAppendIndependent();
165 }
166
167
168
169
170
171 @Test
172 public void testOfflineSnapshotAppendIndependent() throws Exception {
173 createAndCloneSnapshot(false);
174 runTestSnapshotAppendIndependent();
175 }
176
177
178
179
180
181 @Test
182 public void testOnlineSnapshotMetadataChangesIndependent() throws Exception {
183 createAndCloneSnapshot(true);
184 runTestSnapshotMetadataChangesIndependent();
185 }
186
187
188
189
190
191 @Test
192 public void testOfflineSnapshotMetadataChangesIndependent() throws Exception {
193 createAndCloneSnapshot(false);
194 runTestSnapshotMetadataChangesIndependent();
195 }
196
197
198
199
200
201 @Test
202 public void testOfflineSnapshotRegionOperationsIndependent() throws Exception {
203 createAndCloneSnapshot(false);
204 runTestRegionOperationsIndependent();
205 }
206
207
208
209
210
211 @Test
212 public void testOnlineSnapshotRegionOperationsIndependent() throws Exception {
213 createAndCloneSnapshot(true);
214 runTestRegionOperationsIndependent();
215 }
216
217 @Test
218 public void testOfflineSnapshotDeleteIndependent() throws Exception {
219 createAndCloneSnapshot(false);
220 runTestSnapshotDeleteIndependent();
221 }
222
223 @Test
224 public void testOnlineSnapshotDeleteIndependent() throws Exception {
225 createAndCloneSnapshot(true);
226 runTestSnapshotDeleteIndependent();
227 }
228
229 private static void waitOnSplit(Connection c, final Table t, int originalCount) throws Exception {
230 for (int i = 0; i < 200; i++) {
231 Threads.sleepWithoutInterrupt(500);
232 try (RegionLocator locator = c.getRegionLocator(t.getName())) {
233 if (locator.getAllRegionLocations().size() > originalCount) {
234 return;
235 }
236 }
237 }
238 throw new Exception("Split did not increase the number of regions");
239 }
240
241
242
243
244
245
246
247 private void createAndCloneSnapshot(boolean online) throws Exception {
248 SnapshotTestingUtils.createSnapshotAndValidate(admin, originalTableName, TEST_FAM_STR,
249 snapshotNameAsString, rootDir, fs, online);
250
251
252 if (!online) {
253 admin.enableTable(originalTableName);
254 UTIL.waitTableAvailable(originalTableName);
255 }
256
257 admin.cloneSnapshot(snapshotName, cloneTableName);
258 UTIL.waitUntilAllRegionsAssigned(cloneTableName);
259 }
260
261
262
263
264 private void runTestSnapshotAppendIndependent() throws Exception {
265 try (Table clonedTable = UTIL.getConnection().getTable(cloneTableName)) {
266 final int clonedTableRowCount = countRows(clonedTable);
267
268 Assert.assertEquals(
269 "The line counts of original and cloned tables do not match after clone. ",
270 countOriginalTable, clonedTableRowCount);
271
272
273 Put p = new Put(Bytes.toBytes("new-row-" + System.currentTimeMillis()));
274 p.addColumn(TEST_FAM, Bytes.toBytes("someQualifier"), Bytes.toBytes("someString"));
275 originalTable.put(p);
276
277
278 Assert.assertEquals("The row count of the original table was not modified by the put",
279 countOriginalTable + 1, countRows(originalTable));
280 Assert.assertEquals(
281 "The row count of the cloned table changed as a result of addition to the original",
282 clonedTableRowCount, countRows(clonedTable));
283
284 Put p2 = new Put(Bytes.toBytes("new-row-" + System.currentTimeMillis()));
285 p2.addColumn(TEST_FAM, Bytes.toBytes("someQualifier"), Bytes.toBytes("someString"));
286 clonedTable.put(p2);
287
288
289 Assert.assertEquals(
290 "The row count of the original table was modified by the put to the clone",
291 countOriginalTable + 1, countRows(originalTable));
292 Assert.assertEquals("The row count of the cloned table was not modified by the put",
293 clonedTableRowCount + 1, countRows(clonedTable));
294 }
295 }
296
297
298
299
300 private void runTestRegionOperationsIndependent() throws Exception {
301
302 ((ClusterConnection) UTIL.getConnection()).clearRegionCache();
303 List<HRegionInfo> originalTableHRegions = admin.getTableRegions(originalTableName);
304
305 final int originalRegionCount = originalTableHRegions.size();
306 final int cloneTableRegionCount = admin.getTableRegions(cloneTableName).size();
307 Assert.assertEquals(
308 "The number of regions in the cloned table is different than in the original table.",
309 originalRegionCount, cloneTableRegionCount);
310
311
312 admin.splitRegion(originalTableHRegions.get(0).getRegionName());
313 waitOnSplit(UTIL.getConnection(), originalTable, originalRegionCount);
314
315
316 final int cloneTableRegionCount2 = admin.getTableRegions(cloneTableName).size();
317 Assert.assertEquals(
318 "The number of regions in the cloned table changed though none of its regions were split.",
319 cloneTableRegionCount, cloneTableRegionCount2);
320 }
321
322
323
324
325 private void runTestSnapshotMetadataChangesIndependent() throws Exception {
326
327 byte[] TEST_FAM_2 = Bytes.toBytes("fam2");
328 HColumnDescriptor hcd = new HColumnDescriptor(TEST_FAM_2);
329
330 admin.disableTable(originalTableName);
331 admin.addColumn(originalTableName, hcd);
332
333
334 admin.enableTable(originalTableName);
335 UTIL.waitTableAvailable(originalTableName);
336
337
338
339
340 HTableDescriptor originalTableDescriptor = originalTable.getTableDescriptor();
341 HTableDescriptor clonedTableDescriptor = admin.getTableDescriptor(cloneTableName);
342
343 Assert.assertTrue("The original family was not found. There is something wrong. ",
344 originalTableDescriptor.hasFamily(TEST_FAM));
345 Assert.assertTrue("The original family was not found in the clone. There is something wrong. ",
346 clonedTableDescriptor.hasFamily(TEST_FAM));
347
348 Assert.assertTrue("The new family was not found. ",
349 originalTableDescriptor.hasFamily(TEST_FAM_2));
350 Assert.assertTrue("The new family was not found. ",
351 !clonedTableDescriptor.hasFamily(TEST_FAM_2));
352 }
353
354
355
356
357 private void runTestSnapshotDeleteIndependent() throws Exception {
358
359 admin.majorCompact(originalTableName);
360
361
362 admin.deleteSnapshot(snapshotName);
363
364
365 do {
366 Thread.sleep(5000);
367 } while (!admin.listSnapshots(snapshotNameAsString).isEmpty());
368
369 try (Table original = UTIL.getConnection().getTable(originalTableName)) {
370 try (Table clonedTable = UTIL.getConnection().getTable(cloneTableName)) {
371
372 final int origTableRowCount = countRows(original);
373 final int clonedTableRowCount = countRows(clonedTable);
374 Assert.assertEquals(origTableRowCount, clonedTableRowCount);
375 }
376 }
377 }
378
379 protected Table createTable(final TableName table, byte[] family) throws Exception {
380 Table t = UTIL.createTable(table, family);
381
382 UTIL.waitUntilAllRegionsAssigned(table);
383
384
385 return t;
386 }
387
388 public void loadData(final Table table, byte[]... families) throws Exception {
389 UTIL.loadTable(originalTable, TEST_FAM);
390 }
391
392 protected int countRows(final Table table, final byte[]... families) throws Exception {
393 return UTIL.countRows(table, families);
394 }
395 }