1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.snapshot;
20
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertTrue;
23
24 import java.io.IOException;
25 import java.net.URI;
26 import java.util.ArrayList;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Set;
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.FileStatus;
35 import org.apache.hadoop.fs.FileSystem;
36 import org.apache.hadoop.fs.Path;
37 import org.apache.hadoop.hbase.HBaseTestingUtility;
38 import org.apache.hadoop.hbase.HConstants;
39 import org.apache.hadoop.hbase.HRegionInfo;
40 import org.apache.hadoop.hbase.testclassification.MediumTests;
41 import org.apache.hadoop.hbase.TableName;
42 import org.apache.hadoop.hbase.client.Admin;
43 import org.apache.hadoop.hbase.client.HTable;
44 import org.apache.hadoop.hbase.client.Table;
45 import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
46 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
47 import org.apache.hadoop.hbase.protobuf.generated.SnapshotProtos.SnapshotFileInfo;
48 import org.apache.hadoop.hbase.protobuf.generated.SnapshotProtos.SnapshotRegionManifest;
49 import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils.SnapshotMock;
50 import org.apache.hadoop.hbase.util.Bytes;
51 import org.apache.hadoop.hbase.util.FSUtils;
52 import org.apache.hadoop.hbase.util.Pair;
53 import org.junit.After;
54 import org.junit.AfterClass;
55 import org.junit.Before;
56 import org.junit.BeforeClass;
57 import org.junit.Test;
58 import org.junit.experimental.categories.Category;
59
60
61
62
63 @Category(MediumTests.class)
64 public class TestExportSnapshot {
65 private final Log LOG = LogFactory.getLog(getClass());
66
67 protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
68
69 private final static byte[] FAMILY = Bytes.toBytes("cf");
70
71 private byte[] emptySnapshotName;
72 private byte[] snapshotName;
73 private int tableNumFiles;
74 private TableName tableName;
75 private Admin admin;
76
77 public static void setUpBaseConf(Configuration conf) {
78 conf.setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true);
79 conf.setInt("hbase.regionserver.msginterval", 100);
80 conf.setInt("hbase.client.pause", 250);
81 conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 6);
82 conf.setBoolean("hbase.master.enabletable.roundrobin", true);
83 conf.setInt("mapreduce.map.maxattempts", 10);
84 }
85
86 @BeforeClass
87 public static void setUpBeforeClass() throws Exception {
88 setUpBaseConf(TEST_UTIL.getConfiguration());
89 TEST_UTIL.startMiniCluster(3);
90 TEST_UTIL.startMiniMapReduceCluster();
91 }
92
93 @AfterClass
94 public static void tearDownAfterClass() throws Exception {
95 TEST_UTIL.shutdownMiniMapReduceCluster();
96 TEST_UTIL.shutdownMiniCluster();
97 }
98
99
100
101
102 @Before
103 public void setUp() throws Exception {
104 this.admin = TEST_UTIL.getHBaseAdmin();
105
106 long tid = System.currentTimeMillis();
107 tableName = TableName.valueOf("testtb-" + tid);
108 snapshotName = Bytes.toBytes("snaptb0-" + tid);
109 emptySnapshotName = Bytes.toBytes("emptySnaptb0-" + tid);
110
111
112 SnapshotTestingUtils.createTable(TEST_UTIL, tableName, FAMILY);
113
114
115 admin.snapshot(emptySnapshotName, tableName);
116
117
118 Table table = new HTable(TEST_UTIL.getConfiguration(), tableName);
119 SnapshotTestingUtils.loadData(TEST_UTIL, tableName, 50, FAMILY);
120 tableNumFiles = admin.getTableRegions(tableName).size();
121
122
123 admin.snapshot(snapshotName, tableName);
124 }
125
126 @After
127 public void tearDown() throws Exception {
128 TEST_UTIL.deleteTable(tableName);
129 SnapshotTestingUtils.deleteAllSnapshots(TEST_UTIL.getHBaseAdmin());
130 SnapshotTestingUtils.deleteArchiveDirectory(TEST_UTIL);
131 }
132
133
134
135
136
137
138
139
140
141
142
143 @Test
144 public void testBalanceSplit() throws Exception {
145
146 List<Pair<SnapshotFileInfo, Long>> files = new ArrayList<Pair<SnapshotFileInfo, Long>>();
147 for (long i = 0; i <= 20; i++) {
148 SnapshotFileInfo fileInfo = SnapshotFileInfo.newBuilder()
149 .setType(SnapshotFileInfo.Type.HFILE)
150 .setHfile("file-" + i)
151 .build();
152 files.add(new Pair<SnapshotFileInfo, Long>(fileInfo, i));
153 }
154
155
156
157
158
159
160
161 List<List<Pair<SnapshotFileInfo, Long>>> splits = ExportSnapshot.getBalancedSplits(files, 5);
162 assertEquals(5, splits.size());
163
164 String[] split0 = new String[] {"file-20", "file-11", "file-10", "file-1", "file-0"};
165 verifyBalanceSplit(splits.get(0), split0, 42);
166 String[] split1 = new String[] {"file-19", "file-12", "file-9", "file-2"};
167 verifyBalanceSplit(splits.get(1), split1, 42);
168 String[] split2 = new String[] {"file-18", "file-13", "file-8", "file-3"};
169 verifyBalanceSplit(splits.get(2), split2, 42);
170 String[] split3 = new String[] {"file-17", "file-14", "file-7", "file-4"};
171 verifyBalanceSplit(splits.get(3), split3, 42);
172 String[] split4 = new String[] {"file-16", "file-15", "file-6", "file-5"};
173 verifyBalanceSplit(splits.get(4), split4, 42);
174 }
175
176 private void verifyBalanceSplit(final List<Pair<SnapshotFileInfo, Long>> split,
177 final String[] expected, final long expectedSize) {
178 assertEquals(expected.length, split.size());
179 long totalSize = 0;
180 for (int i = 0; i < expected.length; ++i) {
181 Pair<SnapshotFileInfo, Long> fileInfo = split.get(i);
182 assertEquals(expected[i], fileInfo.getFirst().getHfile());
183 totalSize += fileInfo.getSecond();
184 }
185 assertEquals(expectedSize, totalSize);
186 }
187
188
189
190
191 @Test
192 public void testExportFileSystemState() throws Exception {
193 testExportFileSystemState(tableName, snapshotName, snapshotName, tableNumFiles);
194 }
195
196 @Test
197 public void testExportFileSystemStateWithSkipTmp() throws Exception {
198 TEST_UTIL.getConfiguration().setBoolean(ExportSnapshot.CONF_SKIP_TMP, true);
199 testExportFileSystemState(tableName, snapshotName, snapshotName, tableNumFiles);
200 }
201
202 @Test
203 public void testEmptyExportFileSystemState() throws Exception {
204 testExportFileSystemState(tableName, emptySnapshotName, emptySnapshotName, 0);
205 }
206
207 @Test
208 public void testConsecutiveExports() throws Exception {
209 Path copyDir = getLocalDestinationDir();
210 testExportFileSystemState(tableName, snapshotName, snapshotName, tableNumFiles, copyDir, false);
211 testExportFileSystemState(tableName, snapshotName, snapshotName, tableNumFiles, copyDir, true);
212 removeExportDir(copyDir);
213 }
214
215 @Test
216 public void testExportWithTargetName() throws Exception {
217 final byte[] targetName = Bytes.toBytes("testExportWithTargetName");
218 testExportFileSystemState(tableName, snapshotName, targetName, tableNumFiles);
219 }
220
221
222
223
224
225 @Test
226 public void testSnapshotWithRefsExportFileSystemState() throws Exception {
227 Configuration conf = TEST_UTIL.getConfiguration();
228
229 Path rootDir = TEST_UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();
230 FileSystem fs = TEST_UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getFileSystem();
231
232 SnapshotMock snapshotMock = new SnapshotMock(TEST_UTIL.getConfiguration(), fs, rootDir);
233 SnapshotMock.SnapshotBuilder builder = snapshotMock.createSnapshotV2("tableWithRefsV1");
234 testSnapshotWithRefsExportFileSystemState(builder);
235
236 snapshotMock = new SnapshotMock(TEST_UTIL.getConfiguration(), fs, rootDir);
237 builder = snapshotMock.createSnapshotV2("tableWithRefsV2");
238 testSnapshotWithRefsExportFileSystemState(builder);
239 }
240
241
242
243
244
245 private void testSnapshotWithRefsExportFileSystemState(SnapshotMock.SnapshotBuilder builder)
246 throws Exception {
247 Path[] r1Files = builder.addRegion();
248 Path[] r2Files = builder.addRegion();
249 builder.commit();
250 int snapshotFilesCount = r1Files.length + r2Files.length;
251
252 byte[] snapshotName = Bytes.toBytes(builder.getSnapshotDescription().getName());
253 TableName tableName = builder.getTableDescriptor().getTableName();
254 testExportFileSystemState(tableName, snapshotName, snapshotName, snapshotFilesCount);
255 }
256
257 private void testExportFileSystemState(final TableName tableName, final byte[] snapshotName,
258 final byte[] targetName, int filesExpected) throws Exception {
259 Path copyDir = getHdfsDestinationDir();
260 testExportFileSystemState(tableName, snapshotName, targetName, filesExpected, copyDir, false);
261 removeExportDir(copyDir);
262 }
263
264
265
266
267 private void testExportFileSystemState(final TableName tableName, final byte[] snapshotName,
268 final byte[] targetName, int filesExpected, Path copyDir, boolean overwrite)
269 throws Exception {
270 URI hdfsUri = FileSystem.get(TEST_UTIL.getConfiguration()).getUri();
271 FileSystem fs = FileSystem.get(copyDir.toUri(), new Configuration());
272 copyDir = copyDir.makeQualified(fs);
273
274 List<String> opts = new ArrayList<String>();
275 opts.add("-snapshot");
276 opts.add(Bytes.toString(snapshotName));
277 opts.add("-copy-to");
278 opts.add(copyDir.toString());
279 if (targetName != snapshotName) {
280 opts.add("-target");
281 opts.add(Bytes.toString(targetName));
282 }
283 if (overwrite) opts.add("-overwrite");
284
285
286 int res = ExportSnapshot.innerMain(TEST_UTIL.getConfiguration(),
287 opts.toArray(new String[opts.size()]));
288 assertEquals(0, res);
289
290
291 FileStatus[] rootFiles = fs.listStatus(copyDir);
292 assertEquals(filesExpected > 0 ? 2 : 1, rootFiles.length);
293 for (FileStatus fileStatus: rootFiles) {
294 String name = fileStatus.getPath().getName();
295 assertTrue(fileStatus.isDirectory());
296 assertTrue(name.equals(HConstants.SNAPSHOT_DIR_NAME) ||
297 name.equals(HConstants.HFILE_ARCHIVE_DIRECTORY));
298 }
299
300
301 final FileSystem hdfs = FileSystem.get(hdfsUri, TEST_UTIL.getConfiguration());
302 final Path snapshotDir = new Path(HConstants.SNAPSHOT_DIR_NAME, Bytes.toString(snapshotName));
303 final Path targetDir = new Path(HConstants.SNAPSHOT_DIR_NAME, Bytes.toString(targetName));
304 verifySnapshotDir(hdfs, new Path(TEST_UTIL.getDefaultRootDirPath(), snapshotDir),
305 fs, new Path(copyDir, targetDir));
306 Set<String> snapshotFiles = verifySnapshot(fs, copyDir, tableName, Bytes.toString(targetName));
307 assertEquals(filesExpected, snapshotFiles.size());
308 }
309
310
311
312
313 @Test
314 public void testExportFailure() throws Exception {
315 assertEquals(1, runExportAndInjectFailures(snapshotName, false));
316 }
317
318
319
320
321 @Test
322 public void testExportRetry() throws Exception {
323 assertEquals(0, runExportAndInjectFailures(snapshotName, true));
324 }
325
326
327
328
329 private int runExportAndInjectFailures(final byte[] snapshotName, boolean retry)
330 throws Exception {
331 Path copyDir = getLocalDestinationDir();
332 URI hdfsUri = FileSystem.get(TEST_UTIL.getConfiguration()).getUri();
333 FileSystem fs = FileSystem.get(copyDir.toUri(), new Configuration());
334 copyDir = copyDir.makeQualified(fs);
335
336 Configuration conf = new Configuration(TEST_UTIL.getConfiguration());
337 conf.setBoolean(ExportSnapshot.CONF_TEST_FAILURE, true);
338 conf.setBoolean(ExportSnapshot.CONF_TEST_RETRY, retry);
339
340
341 Path sourceDir = TEST_UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();
342 int res = ExportSnapshot.innerMain(conf, new String[] {
343 "-snapshot", Bytes.toString(snapshotName),
344 "-copy-from", sourceDir.toString(),
345 "-copy-to", copyDir.toString()
346 });
347 return res;
348 }
349
350
351
352
353 private void verifySnapshotDir(final FileSystem fs1, final Path root1,
354 final FileSystem fs2, final Path root2) throws IOException {
355 assertEquals(listFiles(fs1, root1, root1), listFiles(fs2, root2, root2));
356 }
357
358
359
360
361 private Set<String> verifySnapshot(final FileSystem fs, final Path rootDir,
362 final TableName tableName, final String snapshotName) throws IOException {
363 final Path exportedSnapshot = new Path(rootDir,
364 new Path(HConstants.SNAPSHOT_DIR_NAME, snapshotName));
365 final Set<String> snapshotFiles = new HashSet<String>();
366 final Path exportedArchive = new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY);
367 SnapshotReferenceUtil.visitReferencedFiles(TEST_UTIL.getConfiguration(), fs, exportedSnapshot,
368 new SnapshotReferenceUtil.SnapshotVisitor() {
369 @Override
370 public void storeFile(final HRegionInfo regionInfo, final String family,
371 final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
372 String hfile = storeFile.getName();
373 snapshotFiles.add(hfile);
374 if (storeFile.hasReference()) {
375
376 } else {
377 verifyNonEmptyFile(new Path(exportedArchive,
378 new Path(FSUtils.getTableDir(new Path("./"), tableName),
379 new Path(regionInfo.getEncodedName(), new Path(family, hfile)))));
380 }
381 }
382
383 @Override
384 public void logFile (final String server, final String logfile)
385 throws IOException {
386 snapshotFiles.add(logfile);
387 verifyNonEmptyFile(new Path(exportedSnapshot, new Path(server, logfile)));
388 }
389
390 private void verifyNonEmptyFile(final Path path) throws IOException {
391 assertTrue(path + " should exists", fs.exists(path));
392 assertTrue(path + " should not be empty", fs.getFileStatus(path).getLen() > 0);
393 }
394 });
395
396
397 SnapshotDescription desc = SnapshotDescriptionUtils.readSnapshotInfo(fs, exportedSnapshot);
398 assertTrue(desc.getName().equals(snapshotName));
399 assertTrue(desc.getTable().equals(tableName.getNameAsString()));
400 return snapshotFiles;
401 }
402
403 private Set<String> listFiles(final FileSystem fs, final Path root, final Path dir)
404 throws IOException {
405 Set<String> files = new HashSet<String>();
406 int rootPrefix = root.toString().length();
407 FileStatus[] list = FSUtils.listStatus(fs, dir);
408 if (list != null) {
409 for (FileStatus fstat: list) {
410 LOG.debug(fstat.getPath());
411 if (fstat.isDirectory()) {
412 files.addAll(listFiles(fs, root, fstat.getPath()));
413 } else {
414 files.add(fstat.getPath().toString().substring(rootPrefix));
415 }
416 }
417 }
418 return files;
419 }
420
421 private Path getHdfsDestinationDir() {
422 Path rootDir = TEST_UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir();
423 Path path = new Path(new Path(rootDir, "export-test"), "export-" + System.currentTimeMillis());
424 LOG.info("HDFS export destination path: " + path);
425 return path;
426 }
427
428 private Path getLocalDestinationDir() {
429 Path path = TEST_UTIL.getDataTestDir("local-export-" + System.currentTimeMillis());
430 LOG.info("Local export destination path: " + path);
431 return path;
432 }
433
434 private void removeExportDir(final Path path) throws IOException {
435 FileSystem fs = FileSystem.get(path.toUri(), new Configuration());
436 fs.delete(path, true);
437 }
438 }