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