smengcl commented on code in PR #3980:
URL: https://github.com/apache/ozone/pull/3980#discussion_r1139883959
##########
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMDbCheckpointServlet.java:
##########
@@ -257,63 +291,271 @@ public void testSpnegoEnabled() throws Exception {
}
@Test
- public void testWriteCheckpointToOutputStream() throws Exception {
+ public void testWriteDbDataToStream() throws Exception {
+ prepSnapshotData();
+ // Set http param to include snapshot data.
+ when(requestMock.getParameter(OZONE_DB_CHECKPOINT_INCLUDE_SNAPSHOT_DATA))
+ .thenReturn("true");
+ // Get the tarball.
+ try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
+ omDbCheckpointServletMock.writeDbDataToStream(dbCheckpoint, requestMock,
+ fileOutputStream);
+ }
+
+ // Untar the file into a temp folder to be examined.
String testDirName = folder.newFolder().getAbsolutePath();
- File checkpoint = new File(testDirName, "checkpoint");
- checkpoint.mkdir();
- File file = new File(checkpoint, "temp1.txt");
- OutputStreamWriter writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 1");
- writer.close();
-
- file = new File(checkpoint, "/temp2.txt");
- writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 2");
- writer.close();
-
- File outputFile =
- new File(Paths.get(testDirName, "output_file.tar").toString());
- TestDBCheckpoint dbCheckpoint = new TestDBCheckpoint(
- checkpoint.toPath());
- writeDBCheckpointToStream(dbCheckpoint,
- new FileOutputStream(outputFile));
- assertNotNull(outputFile);
+ int testDirLength = testDirName.length() + 1;
Review Comment:
What is this `+ 1` for? To get rid of trailing `/`?
##########
hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/DBCheckpointServlet.java:
##########
@@ -188,4 +189,11 @@ public void doGet(HttpServletRequest request,
HttpServletResponse response) {
}
}
+ public void writeDbDataToStream(DBCheckpoint checkpoint,
+ HttpServletRequest ignoredRequest,
Review Comment:
What is this `ignoredRequest` for?
##########
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMDbCheckpointServlet.java:
##########
@@ -257,63 +291,271 @@ public void testSpnegoEnabled() throws Exception {
}
@Test
- public void testWriteCheckpointToOutputStream() throws Exception {
+ public void testWriteDbDataToStream() throws Exception {
+ prepSnapshotData();
+ // Set http param to include snapshot data.
+ when(requestMock.getParameter(OZONE_DB_CHECKPOINT_INCLUDE_SNAPSHOT_DATA))
+ .thenReturn("true");
+ // Get the tarball.
+ try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
+ omDbCheckpointServletMock.writeDbDataToStream(dbCheckpoint, requestMock,
+ fileOutputStream);
+ }
+
+ // Untar the file into a temp folder to be examined.
String testDirName = folder.newFolder().getAbsolutePath();
- File checkpoint = new File(testDirName, "checkpoint");
- checkpoint.mkdir();
- File file = new File(checkpoint, "temp1.txt");
- OutputStreamWriter writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 1");
- writer.close();
-
- file = new File(checkpoint, "/temp2.txt");
- writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 2");
- writer.close();
-
- File outputFile =
- new File(Paths.get(testDirName, "output_file.tar").toString());
- TestDBCheckpoint dbCheckpoint = new TestDBCheckpoint(
- checkpoint.toPath());
- writeDBCheckpointToStream(dbCheckpoint,
- new FileOutputStream(outputFile));
- assertNotNull(outputFile);
+ int testDirLength = testDirName.length() + 1;
+ String newDbDirName = testDirName + OM_KEY_PREFIX + OM_DB_NAME;
+ int newDbDirLength = newDbDirName.length() + 1;
+ File newDbDir = new File(newDbDirName);
+ newDbDir.mkdirs();
+ FileUtil.unTar(tempFile, newDbDir);
+
+ // Move snapshot dir to correct location.
+ new File(newDbDirName, OM_SNAPSHOT_DIR)
+ .renameTo(new File(newDbDir.getParent(), OM_SNAPSHOT_DIR));
+
Review Comment:
```suggestion
Assert.assertTrue(new File(newDbDirName, OM_SNAPSHOT_DIR)
.renameTo(new File(newDbDir.getParent(), OM_SNAPSHOT_DIR)));
```
##########
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMDbCheckpointServlet.java:
##########
@@ -257,63 +291,271 @@ public void testSpnegoEnabled() throws Exception {
}
@Test
- public void testWriteCheckpointToOutputStream() throws Exception {
+ public void testWriteDbDataToStream() throws Exception {
+ prepSnapshotData();
+ // Set http param to include snapshot data.
+ when(requestMock.getParameter(OZONE_DB_CHECKPOINT_INCLUDE_SNAPSHOT_DATA))
+ .thenReturn("true");
+ // Get the tarball.
+ try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
+ omDbCheckpointServletMock.writeDbDataToStream(dbCheckpoint, requestMock,
+ fileOutputStream);
+ }
+
+ // Untar the file into a temp folder to be examined.
String testDirName = folder.newFolder().getAbsolutePath();
- File checkpoint = new File(testDirName, "checkpoint");
- checkpoint.mkdir();
- File file = new File(checkpoint, "temp1.txt");
- OutputStreamWriter writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 1");
- writer.close();
-
- file = new File(checkpoint, "/temp2.txt");
- writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 2");
- writer.close();
-
- File outputFile =
- new File(Paths.get(testDirName, "output_file.tar").toString());
- TestDBCheckpoint dbCheckpoint = new TestDBCheckpoint(
- checkpoint.toPath());
- writeDBCheckpointToStream(dbCheckpoint,
- new FileOutputStream(outputFile));
- assertNotNull(outputFile);
+ int testDirLength = testDirName.length() + 1;
+ String newDbDirName = testDirName + OM_KEY_PREFIX + OM_DB_NAME;
+ int newDbDirLength = newDbDirName.length() + 1;
+ File newDbDir = new File(newDbDirName);
+ newDbDir.mkdirs();
+ FileUtil.unTar(tempFile, newDbDir);
+
+ // Move snapshot dir to correct location.
+ new File(newDbDirName, OM_SNAPSHOT_DIR)
+ .renameTo(new File(newDbDir.getParent(), OM_SNAPSHOT_DIR));
+
+
+ // Confirm the checkpoint directories match, (after remove extras).
+ Path checkpointLocation = dbCheckpoint.getCheckpointLocation();
+ Set<String> initialCheckpointSet = getFiles(checkpointLocation,
+ checkpointLocation.toString().length() + 1);
+ Path finalCheckpointLocation = Paths.get(newDbDirName);
+ Set<String> finalCheckpointSet = getFiles(finalCheckpointLocation,
+ newDbDirLength);
+
+ Assert.assertTrue("hardlink file exists in checkpoint dir",
+ finalCheckpointSet.contains(OM_HARDLINK_FILE));
+ finalCheckpointSet.remove(OM_HARDLINK_FILE);
+ Assert.assertEquals(initialCheckpointSet, finalCheckpointSet);
+
+ int metaDirLength = metaDir.toString().length() + 1;
+ String shortSnapshotLocation =
+ truncateFileName(metaDirLength, Paths.get(snapshotDirName));
+ String shortSnapshotLocation2 =
+ truncateFileName(metaDirLength, Paths.get(snapshotDirName2));
+ String shortCompactionDirLocation =
+ truncateFileName(metaDirLength, compactionDirPath);
+
+ Set<String> finalFullSet =
+ getFiles(Paths.get(testDirName, OM_SNAPSHOT_DIR), testDirLength);
+
+ // Check each line in the hard link file.
+ Stream<String> lines = Files.lines(Paths.get(newDbDirName,
+ OM_HARDLINK_FILE));
+
+ List<String> fabricatedLinkLines = new ArrayList<>();
+ for (String line: lines.collect(Collectors.toList())) {
+ Assert.assertFalse("CURRENT file is not a hard link",
+ line.contains("CURRENT"));
+ if (line.contains("fabricatedFile")) {
+ fabricatedLinkLines.add(line);
+ } else {
+ checkLine(shortSnapshotLocation, shortSnapshotLocation2, line);
+ // add links to the final set
+ finalFullSet.add(line.split("\t")[0]);
+ }
+ }
+
+ Set<String> directories = Sets.newHashSet(
+ shortSnapshotLocation, shortSnapshotLocation2,
+ shortCompactionDirLocation);
+ checkFabricatedLines(directories, fabricatedLinkLines, testDirName);
+
+ Set<String> initialFullSet =
+ getFiles(Paths.get(metaDir.toString(), OM_SNAPSHOT_DIR),
metaDirLength);
+ Assert.assertEquals("found expected snapshot files",
+ initialFullSet, finalFullSet);
Review Comment:
iirc the message is printed in `AssertionError`. so should it say the other
way?
```suggestion
Assert.assertEquals("found expected snapshot files",
initialFullSet, finalFullSet);
```
##########
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMDbCheckpointServlet.java:
##########
@@ -257,63 +291,271 @@ public void testSpnegoEnabled() throws Exception {
}
@Test
- public void testWriteCheckpointToOutputStream() throws Exception {
+ public void testWriteDbDataToStream() throws Exception {
+ prepSnapshotData();
+ // Set http param to include snapshot data.
+ when(requestMock.getParameter(OZONE_DB_CHECKPOINT_INCLUDE_SNAPSHOT_DATA))
+ .thenReturn("true");
+ // Get the tarball.
+ try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
+ omDbCheckpointServletMock.writeDbDataToStream(dbCheckpoint, requestMock,
+ fileOutputStream);
+ }
+
+ // Untar the file into a temp folder to be examined.
String testDirName = folder.newFolder().getAbsolutePath();
- File checkpoint = new File(testDirName, "checkpoint");
- checkpoint.mkdir();
- File file = new File(checkpoint, "temp1.txt");
- OutputStreamWriter writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 1");
- writer.close();
-
- file = new File(checkpoint, "/temp2.txt");
- writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 2");
- writer.close();
-
- File outputFile =
- new File(Paths.get(testDirName, "output_file.tar").toString());
- TestDBCheckpoint dbCheckpoint = new TestDBCheckpoint(
- checkpoint.toPath());
- writeDBCheckpointToStream(dbCheckpoint,
- new FileOutputStream(outputFile));
- assertNotNull(outputFile);
+ int testDirLength = testDirName.length() + 1;
+ String newDbDirName = testDirName + OM_KEY_PREFIX + OM_DB_NAME;
+ int newDbDirLength = newDbDirName.length() + 1;
+ File newDbDir = new File(newDbDirName);
+ newDbDir.mkdirs();
+ FileUtil.unTar(tempFile, newDbDir);
+
+ // Move snapshot dir to correct location.
+ new File(newDbDirName, OM_SNAPSHOT_DIR)
+ .renameTo(new File(newDbDir.getParent(), OM_SNAPSHOT_DIR));
+
+
+ // Confirm the checkpoint directories match, (after remove extras).
+ Path checkpointLocation = dbCheckpoint.getCheckpointLocation();
+ Set<String> initialCheckpointSet = getFiles(checkpointLocation,
+ checkpointLocation.toString().length() + 1);
+ Path finalCheckpointLocation = Paths.get(newDbDirName);
+ Set<String> finalCheckpointSet = getFiles(finalCheckpointLocation,
+ newDbDirLength);
+
+ Assert.assertTrue("hardlink file exists in checkpoint dir",
+ finalCheckpointSet.contains(OM_HARDLINK_FILE));
+ finalCheckpointSet.remove(OM_HARDLINK_FILE);
+ Assert.assertEquals(initialCheckpointSet, finalCheckpointSet);
+
+ int metaDirLength = metaDir.toString().length() + 1;
+ String shortSnapshotLocation =
+ truncateFileName(metaDirLength, Paths.get(snapshotDirName));
+ String shortSnapshotLocation2 =
+ truncateFileName(metaDirLength, Paths.get(snapshotDirName2));
+ String shortCompactionDirLocation =
+ truncateFileName(metaDirLength, compactionDirPath);
+
+ Set<String> finalFullSet =
+ getFiles(Paths.get(testDirName, OM_SNAPSHOT_DIR), testDirLength);
+
+ // Check each line in the hard link file.
+ Stream<String> lines = Files.lines(Paths.get(newDbDirName,
+ OM_HARDLINK_FILE));
+
+ List<String> fabricatedLinkLines = new ArrayList<>();
+ for (String line: lines.collect(Collectors.toList())) {
+ Assert.assertFalse("CURRENT file is not a hard link",
+ line.contains("CURRENT"));
+ if (line.contains("fabricatedFile")) {
+ fabricatedLinkLines.add(line);
+ } else {
+ checkLine(shortSnapshotLocation, shortSnapshotLocation2, line);
+ // add links to the final set
+ finalFullSet.add(line.split("\t")[0]);
+ }
+ }
+
+ Set<String> directories = Sets.newHashSet(
+ shortSnapshotLocation, shortSnapshotLocation2,
+ shortCompactionDirLocation);
+ checkFabricatedLines(directories, fabricatedLinkLines, testDirName);
+
+ Set<String> initialFullSet =
+ getFiles(Paths.get(metaDir.toString(), OM_SNAPSHOT_DIR),
metaDirLength);
+ Assert.assertEquals("found expected snapshot files",
+ initialFullSet, finalFullSet);
}
-}
-class TestDBCheckpoint implements DBCheckpoint {
+ @Test
+ public void testWriteDbDataWithoutOmSnapshot()
+ throws Exception {
+ prepSnapshotData();
+
+ // Set http param to exclude snapshot data.
+ when(requestMock.getParameter(OZONE_DB_CHECKPOINT_INCLUDE_SNAPSHOT_DATA))
+ .thenReturn(null);
+
+ // Get the tarball.
+ try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
+ omDbCheckpointServletMock.writeDbDataToStream(dbCheckpoint, requestMock,
+ fileOutputStream);
+ }
+
+ // Untar the file into a temp folder to be examined.
+ String testDirName = folder.newFolder().getAbsolutePath();
+ int testDirLength = testDirName.length() + 1;
+ FileUtil.unTar(tempFile, new File(testDirName));
+
+ // Confirm the checkpoint directories match.
+ Path checkpointLocation = dbCheckpoint.getCheckpointLocation();
+ Set<String> initialCheckpointSet = getFiles(checkpointLocation,
+ checkpointLocation.toString().length() + 1);
+ Path finalCheckpointLocation = Paths.get(testDirName);
+ Set<String> finalCheckpointSet = getFiles(finalCheckpointLocation,
+ testDirLength);
+
+ Assert.assertEquals(initialCheckpointSet, finalCheckpointSet);
+ }
+
+
Review Comment:
nit
```suggestion
```
##########
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMDbCheckpointServlet.java:
##########
@@ -257,63 +291,271 @@ public void testSpnegoEnabled() throws Exception {
}
@Test
- public void testWriteCheckpointToOutputStream() throws Exception {
+ public void testWriteDbDataToStream() throws Exception {
+ prepSnapshotData();
+ // Set http param to include snapshot data.
+ when(requestMock.getParameter(OZONE_DB_CHECKPOINT_INCLUDE_SNAPSHOT_DATA))
+ .thenReturn("true");
+ // Get the tarball.
+ try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
+ omDbCheckpointServletMock.writeDbDataToStream(dbCheckpoint, requestMock,
+ fileOutputStream);
+ }
+
+ // Untar the file into a temp folder to be examined.
String testDirName = folder.newFolder().getAbsolutePath();
- File checkpoint = new File(testDirName, "checkpoint");
- checkpoint.mkdir();
- File file = new File(checkpoint, "temp1.txt");
- OutputStreamWriter writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 1");
- writer.close();
-
- file = new File(checkpoint, "/temp2.txt");
- writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 2");
- writer.close();
-
- File outputFile =
- new File(Paths.get(testDirName, "output_file.tar").toString());
- TestDBCheckpoint dbCheckpoint = new TestDBCheckpoint(
- checkpoint.toPath());
- writeDBCheckpointToStream(dbCheckpoint,
- new FileOutputStream(outputFile));
- assertNotNull(outputFile);
+ int testDirLength = testDirName.length() + 1;
+ String newDbDirName = testDirName + OM_KEY_PREFIX + OM_DB_NAME;
+ int newDbDirLength = newDbDirName.length() + 1;
+ File newDbDir = new File(newDbDirName);
+ newDbDir.mkdirs();
+ FileUtil.unTar(tempFile, newDbDir);
+
+ // Move snapshot dir to correct location.
+ new File(newDbDirName, OM_SNAPSHOT_DIR)
+ .renameTo(new File(newDbDir.getParent(), OM_SNAPSHOT_DIR));
+
+
+ // Confirm the checkpoint directories match, (after remove extras).
+ Path checkpointLocation = dbCheckpoint.getCheckpointLocation();
+ Set<String> initialCheckpointSet = getFiles(checkpointLocation,
+ checkpointLocation.toString().length() + 1);
+ Path finalCheckpointLocation = Paths.get(newDbDirName);
+ Set<String> finalCheckpointSet = getFiles(finalCheckpointLocation,
+ newDbDirLength);
+
+ Assert.assertTrue("hardlink file exists in checkpoint dir",
+ finalCheckpointSet.contains(OM_HARDLINK_FILE));
+ finalCheckpointSet.remove(OM_HARDLINK_FILE);
+ Assert.assertEquals(initialCheckpointSet, finalCheckpointSet);
+
+ int metaDirLength = metaDir.toString().length() + 1;
+ String shortSnapshotLocation =
+ truncateFileName(metaDirLength, Paths.get(snapshotDirName));
+ String shortSnapshotLocation2 =
+ truncateFileName(metaDirLength, Paths.get(snapshotDirName2));
+ String shortCompactionDirLocation =
+ truncateFileName(metaDirLength, compactionDirPath);
+
+ Set<String> finalFullSet =
+ getFiles(Paths.get(testDirName, OM_SNAPSHOT_DIR), testDirLength);
+
+ // Check each line in the hard link file.
+ Stream<String> lines = Files.lines(Paths.get(newDbDirName,
+ OM_HARDLINK_FILE));
+
+ List<String> fabricatedLinkLines = new ArrayList<>();
+ for (String line: lines.collect(Collectors.toList())) {
+ Assert.assertFalse("CURRENT file is not a hard link",
+ line.contains("CURRENT"));
+ if (line.contains("fabricatedFile")) {
+ fabricatedLinkLines.add(line);
+ } else {
+ checkLine(shortSnapshotLocation, shortSnapshotLocation2, line);
+ // add links to the final set
+ finalFullSet.add(line.split("\t")[0]);
+ }
+ }
Review Comment:
`Stream<>` needs to be closed. Best wrap it in try-with-resource for better
error handling and guarantee closure even when thrown exceptions.
Example:
https://github.com/apache/ozone/blob/7923e25be44ca89e581aa493443f9f9310483883/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueContainerMetadataInspector.java#L308-L310
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMDBCheckpointServlet.java:
##########
@@ -82,4 +113,166 @@ public void init() throws ServletException {
allowedGroups,
om.isSpnegoEnabled());
}
+
+ @Override
+ public void writeDbDataToStream(DBCheckpoint checkpoint,
+ HttpServletRequest request,
+ OutputStream destination)
+ throws IOException, InterruptedException {
+ // Map of inodes to path.
+ Map<Object, Path> copyFiles = new HashMap<>();
+ // Map of link to path.
+ Map<Path, Path> hardLinkFiles = new HashMap<>();
+
+ getFilesForArchive(checkpoint, copyFiles, hardLinkFiles,
+ includeSnapshotData(request));
+
+ try (TarArchiveOutputStream archiveOutputStream =
+ new TarArchiveOutputStream(destination)) {
+ archiveOutputStream
+ .setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
+ writeFilesToArchive(copyFiles, hardLinkFiles, archiveOutputStream);
+ }
+ }
+
+ private void getFilesForArchive(DBCheckpoint checkpoint,
+ Map<Object, Path> copyFiles,
+ Map<Path, Path> hardLinkFiles,
+ boolean includeSnapshotData)
+ throws IOException, InterruptedException {
+
+ // Get the active fs files.
+ Path dir = checkpoint.getCheckpointLocation();
+ processDir(dir, copyFiles, hardLinkFiles, new HashSet<>());
+
+ if (!includeSnapshotData) {
+ return;
+ }
+
+ // Get the snapshot files.
+ Set<Path> snapshotPaths = waitForSnapshotDirs(checkpoint);
+ Path snapshotDir = Paths.get(OMStorage.getOmDbDir(getConf()).toString(),
+ OM_SNAPSHOT_DIR);
+ processDir(snapshotDir, copyFiles, hardLinkFiles, snapshotPaths);
+ }
+
+ /**
+ * The snapshotInfo table may contain a snapshot that
+ * doesn't yet exist on the fs, so wait a few seconds for it.
+ * @param checkpoint Checkpoint containing snapshot entries expected.
+ * @return Set of expected snapshot dirs.
+ */
+ private Set<Path> waitForSnapshotDirs(DBCheckpoint checkpoint)
+ throws IOException, InterruptedException {
+
+ OzoneConfiguration conf = getConf();
+
+ Set<Path> snapshotPaths = new HashSet<>();
+
+ // get snapshotInfo entries
+ OmMetadataManagerImpl checkpointMetadataManager =
+ OmMetadataManagerImpl.createCheckpointMetadataManager(
+ conf, checkpoint);
+ try (TableIterator<String, ? extends Table.KeyValue<String, SnapshotInfo>>
+ iterator = checkpointMetadataManager
+ .getSnapshotInfoTable().iterator()) {
+
+ // For each entry, wait for corresponding directory.
+ while (iterator.hasNext()) {
+ Table.KeyValue<String, SnapshotInfo> entry = iterator.next();
+ Path path = Paths.get(getSnapshotPath(conf, entry.getValue()));
+ waitForDirToExist(path);
+ snapshotPaths.add(path);
+ }
+ }
+ return snapshotPaths;
+ }
+
+ private void waitForDirToExist(Path dir)
+ throws IOException, InterruptedException {
+ long endTime = System.currentTimeMillis() +
+ Duration.parse(DURATION_TO_WAIT_FOR_DIRECTORY).toMillis();
+ while (!dir.toFile().exists()) {
+ Thread.sleep(100);
+ if (System.currentTimeMillis() > endTime) {
+ throw new IOException("snapshot dir doesn't exist: " + dir);
+ }
+ }
+ }
+
+ @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
+ private void processDir(Path dir, Map<Object, Path> copyFiles,
Review Comment:
Is it possible to remove the suppress annotation and fix the findbugs? since
this is not test code like the others.
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMDBCheckpointServlet.java:
##########
@@ -82,4 +113,166 @@ public void init() throws ServletException {
allowedGroups,
om.isSpnegoEnabled());
}
+
+ @Override
+ public void writeDbDataToStream(DBCheckpoint checkpoint,
+ HttpServletRequest request,
+ OutputStream destination)
+ throws IOException, InterruptedException {
+ // Map of inodes to path.
+ Map<Object, Path> copyFiles = new HashMap<>();
+ // Map of link to path.
+ Map<Path, Path> hardLinkFiles = new HashMap<>();
+
+ getFilesForArchive(checkpoint, copyFiles, hardLinkFiles,
+ includeSnapshotData(request));
+
+ try (TarArchiveOutputStream archiveOutputStream =
+ new TarArchiveOutputStream(destination)) {
+ archiveOutputStream
+ .setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
+ writeFilesToArchive(copyFiles, hardLinkFiles, archiveOutputStream);
+ }
+ }
+
+ private void getFilesForArchive(DBCheckpoint checkpoint,
+ Map<Object, Path> copyFiles,
+ Map<Path, Path> hardLinkFiles,
+ boolean includeSnapshotData)
+ throws IOException, InterruptedException {
+
+ // Get the active fs files.
+ Path dir = checkpoint.getCheckpointLocation();
+ processDir(dir, copyFiles, hardLinkFiles, new HashSet<>());
+
+ if (!includeSnapshotData) {
+ return;
+ }
+
+ // Get the snapshot files.
+ Set<Path> snapshotPaths = waitForSnapshotDirs(checkpoint);
+ Path snapshotDir = Paths.get(OMStorage.getOmDbDir(getConf()).toString(),
+ OM_SNAPSHOT_DIR);
+ processDir(snapshotDir, copyFiles, hardLinkFiles, snapshotPaths);
+ }
+
+ /**
+ * The snapshotInfo table may contain a snapshot that
+ * doesn't yet exist on the fs, so wait a few seconds for it.
+ * @param checkpoint Checkpoint containing snapshot entries expected.
+ * @return Set of expected snapshot dirs.
+ */
+ private Set<Path> waitForSnapshotDirs(DBCheckpoint checkpoint)
+ throws IOException, InterruptedException {
+
+ OzoneConfiguration conf = getConf();
+
+ Set<Path> snapshotPaths = new HashSet<>();
+
+ // get snapshotInfo entries
+ OmMetadataManagerImpl checkpointMetadataManager =
+ OmMetadataManagerImpl.createCheckpointMetadataManager(
+ conf, checkpoint);
+ try (TableIterator<String, ? extends Table.KeyValue<String, SnapshotInfo>>
+ iterator = checkpointMetadataManager
+ .getSnapshotInfoTable().iterator()) {
+
+ // For each entry, wait for corresponding directory.
+ while (iterator.hasNext()) {
+ Table.KeyValue<String, SnapshotInfo> entry = iterator.next();
+ Path path = Paths.get(getSnapshotPath(conf, entry.getValue()));
+ waitForDirToExist(path);
+ snapshotPaths.add(path);
+ }
+ }
+ return snapshotPaths;
+ }
+
+ private void waitForDirToExist(Path dir)
+ throws IOException, InterruptedException {
+ long endTime = System.currentTimeMillis() +
+ Duration.parse(DURATION_TO_WAIT_FOR_DIRECTORY).toMillis();
+ while (!dir.toFile().exists()) {
+ Thread.sleep(100);
+ if (System.currentTimeMillis() > endTime) {
+ throw new IOException("snapshot dir doesn't exist: " + dir);
+ }
+ }
+ }
+
+ @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
+ private void processDir(Path dir, Map<Object, Path> copyFiles,
+ Map<Path, Path> hardLinkFiles,
+ Set<Path> snapshotPaths)
+ throws IOException {
+ try (Stream<Path> files = Files.list(dir)) {
+ for (Path file : files.collect(Collectors.toList())) {
+ if (file.toFile().isDirectory()) {
+ // Skip any unexpected snapshot files.
+ if (file.getParent().toString().endsWith(OM_SNAPSHOT_CHECKPOINT_DIR)
+ && !snapshotPaths.contains(file)) {
+ continue;
+ }
+ processDir(file, copyFiles, hardLinkFiles, snapshotPaths);
+ } else {
+ processFile(file, copyFiles, hardLinkFiles);
+ }
+ }
+ }
+ }
+
+ private void processFile(Path file, Map<Object, Path> copyFiles,
+ Map<Path, Path> hardLinkFiles) throws IOException {
+ // Get the inode.
+ Object key = OmSnapshotManager.getINode(file);
+ // If we already have the inode, store as hard link.
+ if (copyFiles.containsKey(key)) {
+ hardLinkFiles.put(file, copyFiles.get(key));
+ } else {
+ copyFiles.put(key, file);
+ }
+ }
+
+ // Returns value of http request parameter.
+ private boolean includeSnapshotData(HttpServletRequest request) {
+ String includeParam =
+ request.getParameter(OZONE_DB_CHECKPOINT_INCLUDE_SNAPSHOT_DATA);
+ if (StringUtils.isNotEmpty(includeParam)) {
+ return Boolean.parseBoolean(includeParam);
+ }
+ return false;
+ }
+
+ @SuppressFBWarnings({"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
+ private void writeFilesToArchive(Map<Object, Path> copyFiles,
Review Comment:
Same as above
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java:
##########
@@ -326,11 +333,22 @@ private OmMetadataManagerImpl(OzoneConfiguration conf,
String snapshotDirName)
*/
public static OmMetadataManagerImpl createSnapshotMetadataManager(
OzoneConfiguration conf, String snapshotDirName) throws IOException {
+ File snapshotDir = new File(OMStorage.getOmDbDir(conf) +
+ OM_KEY_PREFIX + OM_SNAPSHOT_CHECKPOINT_DIR);
OmMetadataManagerImpl smm = new OmMetadataManagerImpl(conf,
- snapshotDirName);
+ snapshotDir, OM_DB_NAME + snapshotDirName);
return smm;
}
+ @SuppressFBWarnings({"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
+ public static OmMetadataManagerImpl createCheckpointMetadataManager(
+ OzoneConfiguration conf, DBCheckpoint checkpoint) throws IOException {
Review Comment:
Same here
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmSnapshotManager.java:
##########
@@ -279,6 +307,92 @@ private void verifySnapshotInfoForSnapDiff(final
SnapshotInfo fromSnapshot,
}
}
+ /**
+ * Create file of links to add to tarball.
+ * Format of entries are either:
+ * dir1/fileTo fileFrom
+ * for files in active db or:
+ * dir1/fileTo dir2/fileFrom
+ * for files in another directory, (either another snapshot dir or
+ * sst compaction backup directory)
+ * @param truncateLength - Length of initial path to trim in file path.
+ * @param hardLinkFiles - Map of link->file paths.
+ * @return Path to the file of links created.
+ */
+ @SuppressFBWarnings({"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
+ static Path createHardLinkList(int truncateLength,
+ Map<Path, Path> hardLinkFiles)
+ throws IOException {
+ Path data = Files.createTempFile("data", "txt");
Review Comment:
Do we need to randomize the temp file name just in case other processes
happens to use the same temp file name in tmp dir? (or is the tmp dir per-JVM
already, rather than rooted under shared `/tmp/`)
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmSnapshotManager.java:
##########
@@ -279,6 +307,92 @@ private void verifySnapshotInfoForSnapDiff(final
SnapshotInfo fromSnapshot,
}
}
+ /**
+ * Create file of links to add to tarball.
+ * Format of entries are either:
+ * dir1/fileTo fileFrom
+ * for files in active db or:
+ * dir1/fileTo dir2/fileFrom
+ * for files in another directory, (either another snapshot dir or
+ * sst compaction backup directory)
+ * @param truncateLength - Length of initial path to trim in file path.
+ * @param hardLinkFiles - Map of link->file paths.
+ * @return Path to the file of links created.
+ */
+ @SuppressFBWarnings({"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
+ static Path createHardLinkList(int truncateLength,
+ Map<Path, Path> hardLinkFiles)
+ throws IOException {
+ Path data = Files.createTempFile("data", "txt");
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<Path, Path> entry : hardLinkFiles.entrySet()) {
+ String fixedFile = truncateFileName(truncateLength, entry.getValue());
+ // If this file is from the active db, strip the path.
+ if (fixedFile.startsWith(OM_CHECKPOINT_DIR)) {
+ fixedFile = Paths.get(fixedFile).getFileName().toString();
+ }
+ sb.append(truncateFileName(truncateLength, entry.getKey()))
+ .append("\t")
+ .append(fixedFile)
+ .append("\n");
+ }
+ Files.write(data, sb.toString().getBytes(StandardCharsets.UTF_8));
+ return data;
+ }
+
+ /**
+ * Get the filename without the introductory metadata directory.
+ *
+ * @param truncateLength Length to remove.
+ * @param file File to remove prefix from.
+ * @return Truncated string.
+ */
+ static String truncateFileName(int truncateLength, Path file) {
+ return file.toString().substring(truncateLength);
+ }
+
+ /**
+ * Create hard links listed in OM_HARDLINK_FILE.
+ *
+ * @param dbPath Path to db to have links created.
+ */
+ static void createHardLinks(Path dbPath) throws IOException {
+ File hardLinkFile = new File(dbPath.toString(),
+ OM_HARDLINK_FILE);
+ if (hardLinkFile.exists()) {
+ // Read file.
+ List<String> lines =
+ Files.lines(hardLinkFile.toPath()).collect(Collectors.toList());
Review Comment:
Need try-with-resources to properly close the stream as well
##########
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMDbCheckpointServlet.java:
##########
@@ -257,63 +291,271 @@ public void testSpnegoEnabled() throws Exception {
}
@Test
- public void testWriteCheckpointToOutputStream() throws Exception {
+ public void testWriteDbDataToStream() throws Exception {
+ prepSnapshotData();
+ // Set http param to include snapshot data.
+ when(requestMock.getParameter(OZONE_DB_CHECKPOINT_INCLUDE_SNAPSHOT_DATA))
+ .thenReturn("true");
+ // Get the tarball.
+ try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
+ omDbCheckpointServletMock.writeDbDataToStream(dbCheckpoint, requestMock,
+ fileOutputStream);
+ }
+
+ // Untar the file into a temp folder to be examined.
String testDirName = folder.newFolder().getAbsolutePath();
- File checkpoint = new File(testDirName, "checkpoint");
- checkpoint.mkdir();
- File file = new File(checkpoint, "temp1.txt");
- OutputStreamWriter writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 1");
- writer.close();
-
- file = new File(checkpoint, "/temp2.txt");
- writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 2");
- writer.close();
-
- File outputFile =
- new File(Paths.get(testDirName, "output_file.tar").toString());
- TestDBCheckpoint dbCheckpoint = new TestDBCheckpoint(
- checkpoint.toPath());
- writeDBCheckpointToStream(dbCheckpoint,
- new FileOutputStream(outputFile));
- assertNotNull(outputFile);
+ int testDirLength = testDirName.length() + 1;
+ String newDbDirName = testDirName + OM_KEY_PREFIX + OM_DB_NAME;
+ int newDbDirLength = newDbDirName.length() + 1;
+ File newDbDir = new File(newDbDirName);
+ newDbDir.mkdirs();
+ FileUtil.unTar(tempFile, newDbDir);
+
+ // Move snapshot dir to correct location.
+ new File(newDbDirName, OM_SNAPSHOT_DIR)
+ .renameTo(new File(newDbDir.getParent(), OM_SNAPSHOT_DIR));
+
+
+ // Confirm the checkpoint directories match, (after remove extras).
+ Path checkpointLocation = dbCheckpoint.getCheckpointLocation();
+ Set<String> initialCheckpointSet = getFiles(checkpointLocation,
+ checkpointLocation.toString().length() + 1);
+ Path finalCheckpointLocation = Paths.get(newDbDirName);
+ Set<String> finalCheckpointSet = getFiles(finalCheckpointLocation,
+ newDbDirLength);
+
+ Assert.assertTrue("hardlink file exists in checkpoint dir",
+ finalCheckpointSet.contains(OM_HARDLINK_FILE));
+ finalCheckpointSet.remove(OM_HARDLINK_FILE);
+ Assert.assertEquals(initialCheckpointSet, finalCheckpointSet);
+
+ int metaDirLength = metaDir.toString().length() + 1;
+ String shortSnapshotLocation =
+ truncateFileName(metaDirLength, Paths.get(snapshotDirName));
+ String shortSnapshotLocation2 =
+ truncateFileName(metaDirLength, Paths.get(snapshotDirName2));
+ String shortCompactionDirLocation =
+ truncateFileName(metaDirLength, compactionDirPath);
+
+ Set<String> finalFullSet =
+ getFiles(Paths.get(testDirName, OM_SNAPSHOT_DIR), testDirLength);
+
+ // Check each line in the hard link file.
+ Stream<String> lines = Files.lines(Paths.get(newDbDirName,
+ OM_HARDLINK_FILE));
+
+ List<String> fabricatedLinkLines = new ArrayList<>();
+ for (String line: lines.collect(Collectors.toList())) {
+ Assert.assertFalse("CURRENT file is not a hard link",
+ line.contains("CURRENT"));
+ if (line.contains("fabricatedFile")) {
+ fabricatedLinkLines.add(line);
+ } else {
+ checkLine(shortSnapshotLocation, shortSnapshotLocation2, line);
+ // add links to the final set
+ finalFullSet.add(line.split("\t")[0]);
+ }
+ }
+
+ Set<String> directories = Sets.newHashSet(
+ shortSnapshotLocation, shortSnapshotLocation2,
+ shortCompactionDirLocation);
+ checkFabricatedLines(directories, fabricatedLinkLines, testDirName);
+
+ Set<String> initialFullSet =
+ getFiles(Paths.get(metaDir.toString(), OM_SNAPSHOT_DIR),
metaDirLength);
+ Assert.assertEquals("found expected snapshot files",
+ initialFullSet, finalFullSet);
}
-}
-class TestDBCheckpoint implements DBCheckpoint {
+ @Test
+ public void testWriteDbDataWithoutOmSnapshot()
+ throws Exception {
+ prepSnapshotData();
+
+ // Set http param to exclude snapshot data.
+ when(requestMock.getParameter(OZONE_DB_CHECKPOINT_INCLUDE_SNAPSHOT_DATA))
+ .thenReturn(null);
+
+ // Get the tarball.
+ try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
+ omDbCheckpointServletMock.writeDbDataToStream(dbCheckpoint, requestMock,
+ fileOutputStream);
+ }
+
+ // Untar the file into a temp folder to be examined.
+ String testDirName = folder.newFolder().getAbsolutePath();
+ int testDirLength = testDirName.length() + 1;
+ FileUtil.unTar(tempFile, new File(testDirName));
+
+ // Confirm the checkpoint directories match.
+ Path checkpointLocation = dbCheckpoint.getCheckpointLocation();
+ Set<String> initialCheckpointSet = getFiles(checkpointLocation,
+ checkpointLocation.toString().length() + 1);
+ Path finalCheckpointLocation = Paths.get(testDirName);
+ Set<String> finalCheckpointSet = getFiles(finalCheckpointLocation,
+ testDirLength);
+
+ Assert.assertEquals(initialCheckpointSet, finalCheckpointSet);
+ }
+
+
- private final Path checkpointFile;
+ private void prepSnapshotData() throws Exception {
+ setupCluster();
+ metaDir = OMStorage.getOmDbDir(conf);
+
+ OzoneBucket bucket = TestDataUtil.createVolumeAndBucket(cluster);
+
+ // Create dummy keys for snapshotting.
+ TestDataUtil.createKey(bucket, UUID.randomUUID().toString(),
+ "content");
+ TestDataUtil.createKey(bucket, UUID.randomUUID().toString(),
+ "content");
+
+ // This sleep can be removed after this is fixed:
+ // https://issues.apache.org/jira/browse/HDDS-7279
+ Thread.sleep(2000);
+ snapshotDirName =
+ createSnapshot(bucket.getVolumeName(), bucket.getName());
+ snapshotDirName2 =
+ createSnapshot(bucket.getVolumeName(), bucket.getName());
+
+ // Create dummy snapshot to make sure it is not included.
+ Path fabricatedSnapshot = Paths.get(
+ new File(snapshotDirName).getParent(),
+ "fabricatedSnapshot");
+ fabricatedSnapshot.toFile().mkdirs();
+ Paths.get(fabricatedSnapshot.toString(), "fabricatedFile")
+ .toFile().createNewFile();
Review Comment:
```suggestion
Assert.assertTrue(Paths.get(fabricatedSnapshot.toString(),
"fabricatedFile")
.toFile().createNewFile());
```
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmSnapshotManager.java:
##########
@@ -279,6 +307,92 @@ private void verifySnapshotInfoForSnapDiff(final
SnapshotInfo fromSnapshot,
}
}
+ /**
+ * Create file of links to add to tarball.
+ * Format of entries are either:
+ * dir1/fileTo fileFrom
+ * for files in active db or:
+ * dir1/fileTo dir2/fileFrom
+ * for files in another directory, (either another snapshot dir or
+ * sst compaction backup directory)
+ * @param truncateLength - Length of initial path to trim in file path.
+ * @param hardLinkFiles - Map of link->file paths.
+ * @return Path to the file of links created.
+ */
+ @SuppressFBWarnings({"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
+ static Path createHardLinkList(int truncateLength,
+ Map<Path, Path> hardLinkFiles)
+ throws IOException {
+ Path data = Files.createTempFile("data", "txt");
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<Path, Path> entry : hardLinkFiles.entrySet()) {
+ String fixedFile = truncateFileName(truncateLength, entry.getValue());
+ // If this file is from the active db, strip the path.
+ if (fixedFile.startsWith(OM_CHECKPOINT_DIR)) {
+ fixedFile = Paths.get(fixedFile).getFileName().toString();
+ }
+ sb.append(truncateFileName(truncateLength, entry.getKey()))
+ .append("\t")
+ .append(fixedFile)
+ .append("\n");
+ }
+ Files.write(data, sb.toString().getBytes(StandardCharsets.UTF_8));
+ return data;
+ }
+
+ /**
+ * Get the filename without the introductory metadata directory.
+ *
+ * @param truncateLength Length to remove.
+ * @param file File to remove prefix from.
+ * @return Truncated string.
+ */
+ static String truncateFileName(int truncateLength, Path file) {
+ return file.toString().substring(truncateLength);
+ }
+
+ /**
+ * Create hard links listed in OM_HARDLINK_FILE.
+ *
+ * @param dbPath Path to db to have links created.
+ */
+ static void createHardLinks(Path dbPath) throws IOException {
+ File hardLinkFile = new File(dbPath.toString(),
+ OM_HARDLINK_FILE);
+ if (hardLinkFile.exists()) {
+ // Read file.
+ List<String> lines =
+ Files.lines(hardLinkFile.toPath()).collect(Collectors.toList());
+
+ // Create a link for each line.
+ for (String l : lines) {
+ String from = l.split("\t")[1];
+ String to = l.split("\t")[0];
+ Path fullFromPath = getFullPath(dbPath, from);
+ Path fullToPath = getFullPath(dbPath, to);
+ Files.createLink(fullToPath, fullFromPath);
+ }
+ if (!hardLinkFile.delete()) {
+ throw new IOException(
+ "Failed to delete: " + hardLinkFile);
+ }
+ }
+ }
+
+
+ // Prepend the full path to the hard link entry entry.
+ @SuppressFBWarnings({"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
+ private static Path getFullPath(Path dbPath, String fileName) {
Review Comment:
Same here
##########
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMRatisSnapshots.java:
##########
@@ -543,6 +559,38 @@ public void testInstallCorruptedCheckpointFailure() throws
Exception {
Assert.assertTrue(logCapture.getOutput().contains(msg));
}
+ private void createSnapshot(OzoneManager leaderOM, List<String> keys)
Review Comment:
We could rename this to `createOzoneSnapshot` in the context of Ratis.
##########
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMDbCheckpointServlet.java:
##########
@@ -257,63 +291,271 @@ public void testSpnegoEnabled() throws Exception {
}
@Test
- public void testWriteCheckpointToOutputStream() throws Exception {
+ public void testWriteDbDataToStream() throws Exception {
+ prepSnapshotData();
+ // Set http param to include snapshot data.
+ when(requestMock.getParameter(OZONE_DB_CHECKPOINT_INCLUDE_SNAPSHOT_DATA))
+ .thenReturn("true");
+ // Get the tarball.
+ try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
+ omDbCheckpointServletMock.writeDbDataToStream(dbCheckpoint, requestMock,
+ fileOutputStream);
+ }
+
+ // Untar the file into a temp folder to be examined.
String testDirName = folder.newFolder().getAbsolutePath();
- File checkpoint = new File(testDirName, "checkpoint");
- checkpoint.mkdir();
- File file = new File(checkpoint, "temp1.txt");
- OutputStreamWriter writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 1");
- writer.close();
-
- file = new File(checkpoint, "/temp2.txt");
- writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 2");
- writer.close();
-
- File outputFile =
- new File(Paths.get(testDirName, "output_file.tar").toString());
- TestDBCheckpoint dbCheckpoint = new TestDBCheckpoint(
- checkpoint.toPath());
- writeDBCheckpointToStream(dbCheckpoint,
- new FileOutputStream(outputFile));
- assertNotNull(outputFile);
+ int testDirLength = testDirName.length() + 1;
+ String newDbDirName = testDirName + OM_KEY_PREFIX + OM_DB_NAME;
+ int newDbDirLength = newDbDirName.length() + 1;
+ File newDbDir = new File(newDbDirName);
+ newDbDir.mkdirs();
+ FileUtil.unTar(tempFile, newDbDir);
+
+ // Move snapshot dir to correct location.
+ new File(newDbDirName, OM_SNAPSHOT_DIR)
+ .renameTo(new File(newDbDir.getParent(), OM_SNAPSHOT_DIR));
+
+
+ // Confirm the checkpoint directories match, (after remove extras).
+ Path checkpointLocation = dbCheckpoint.getCheckpointLocation();
+ Set<String> initialCheckpointSet = getFiles(checkpointLocation,
+ checkpointLocation.toString().length() + 1);
+ Path finalCheckpointLocation = Paths.get(newDbDirName);
+ Set<String> finalCheckpointSet = getFiles(finalCheckpointLocation,
+ newDbDirLength);
+
+ Assert.assertTrue("hardlink file exists in checkpoint dir",
+ finalCheckpointSet.contains(OM_HARDLINK_FILE));
+ finalCheckpointSet.remove(OM_HARDLINK_FILE);
+ Assert.assertEquals(initialCheckpointSet, finalCheckpointSet);
+
+ int metaDirLength = metaDir.toString().length() + 1;
+ String shortSnapshotLocation =
+ truncateFileName(metaDirLength, Paths.get(snapshotDirName));
+ String shortSnapshotLocation2 =
+ truncateFileName(metaDirLength, Paths.get(snapshotDirName2));
+ String shortCompactionDirLocation =
+ truncateFileName(metaDirLength, compactionDirPath);
+
+ Set<String> finalFullSet =
+ getFiles(Paths.get(testDirName, OM_SNAPSHOT_DIR), testDirLength);
+
+ // Check each line in the hard link file.
+ Stream<String> lines = Files.lines(Paths.get(newDbDirName,
+ OM_HARDLINK_FILE));
+
+ List<String> fabricatedLinkLines = new ArrayList<>();
+ for (String line: lines.collect(Collectors.toList())) {
+ Assert.assertFalse("CURRENT file is not a hard link",
+ line.contains("CURRENT"));
+ if (line.contains("fabricatedFile")) {
+ fabricatedLinkLines.add(line);
+ } else {
+ checkLine(shortSnapshotLocation, shortSnapshotLocation2, line);
+ // add links to the final set
+ finalFullSet.add(line.split("\t")[0]);
+ }
+ }
+
+ Set<String> directories = Sets.newHashSet(
+ shortSnapshotLocation, shortSnapshotLocation2,
+ shortCompactionDirLocation);
+ checkFabricatedLines(directories, fabricatedLinkLines, testDirName);
+
+ Set<String> initialFullSet =
+ getFiles(Paths.get(metaDir.toString(), OM_SNAPSHOT_DIR),
metaDirLength);
+ Assert.assertEquals("found expected snapshot files",
+ initialFullSet, finalFullSet);
}
-}
-class TestDBCheckpoint implements DBCheckpoint {
+ @Test
+ public void testWriteDbDataWithoutOmSnapshot()
+ throws Exception {
+ prepSnapshotData();
+
+ // Set http param to exclude snapshot data.
+ when(requestMock.getParameter(OZONE_DB_CHECKPOINT_INCLUDE_SNAPSHOT_DATA))
+ .thenReturn(null);
+
+ // Get the tarball.
+ try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
+ omDbCheckpointServletMock.writeDbDataToStream(dbCheckpoint, requestMock,
+ fileOutputStream);
+ }
+
+ // Untar the file into a temp folder to be examined.
+ String testDirName = folder.newFolder().getAbsolutePath();
+ int testDirLength = testDirName.length() + 1;
+ FileUtil.unTar(tempFile, new File(testDirName));
+
+ // Confirm the checkpoint directories match.
+ Path checkpointLocation = dbCheckpoint.getCheckpointLocation();
+ Set<String> initialCheckpointSet = getFiles(checkpointLocation,
+ checkpointLocation.toString().length() + 1);
+ Path finalCheckpointLocation = Paths.get(testDirName);
+ Set<String> finalCheckpointSet = getFiles(finalCheckpointLocation,
+ testDirLength);
+
+ Assert.assertEquals(initialCheckpointSet, finalCheckpointSet);
+ }
+
+
- private final Path checkpointFile;
+ private void prepSnapshotData() throws Exception {
+ setupCluster();
+ metaDir = OMStorage.getOmDbDir(conf);
+
+ OzoneBucket bucket = TestDataUtil.createVolumeAndBucket(cluster);
+
+ // Create dummy keys for snapshotting.
+ TestDataUtil.createKey(bucket, UUID.randomUUID().toString(),
+ "content");
+ TestDataUtil.createKey(bucket, UUID.randomUUID().toString(),
+ "content");
+
+ // This sleep can be removed after this is fixed:
+ // https://issues.apache.org/jira/browse/HDDS-7279
+ Thread.sleep(2000);
Review Comment:
Looks like we can remove this now that HDDS-7279 is resolved :)
##########
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMDbCheckpointServlet.java:
##########
@@ -257,63 +291,271 @@ public void testSpnegoEnabled() throws Exception {
}
@Test
- public void testWriteCheckpointToOutputStream() throws Exception {
+ public void testWriteDbDataToStream() throws Exception {
+ prepSnapshotData();
+ // Set http param to include snapshot data.
+ when(requestMock.getParameter(OZONE_DB_CHECKPOINT_INCLUDE_SNAPSHOT_DATA))
+ .thenReturn("true");
+ // Get the tarball.
+ try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
+ omDbCheckpointServletMock.writeDbDataToStream(dbCheckpoint, requestMock,
+ fileOutputStream);
+ }
+
+ // Untar the file into a temp folder to be examined.
String testDirName = folder.newFolder().getAbsolutePath();
- File checkpoint = new File(testDirName, "checkpoint");
- checkpoint.mkdir();
- File file = new File(checkpoint, "temp1.txt");
- OutputStreamWriter writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 1");
- writer.close();
-
- file = new File(checkpoint, "/temp2.txt");
- writer = new OutputStreamWriter(
- new FileOutputStream(file), StandardCharsets.UTF_8);
- writer.write("Test data 2");
- writer.close();
-
- File outputFile =
- new File(Paths.get(testDirName, "output_file.tar").toString());
- TestDBCheckpoint dbCheckpoint = new TestDBCheckpoint(
- checkpoint.toPath());
- writeDBCheckpointToStream(dbCheckpoint,
- new FileOutputStream(outputFile));
- assertNotNull(outputFile);
+ int testDirLength = testDirName.length() + 1;
+ String newDbDirName = testDirName + OM_KEY_PREFIX + OM_DB_NAME;
+ int newDbDirLength = newDbDirName.length() + 1;
+ File newDbDir = new File(newDbDirName);
+ newDbDir.mkdirs();
+ FileUtil.unTar(tempFile, newDbDir);
+
+ // Move snapshot dir to correct location.
+ new File(newDbDirName, OM_SNAPSHOT_DIR)
+ .renameTo(new File(newDbDir.getParent(), OM_SNAPSHOT_DIR));
+
+
+ // Confirm the checkpoint directories match, (after remove extras).
+ Path checkpointLocation = dbCheckpoint.getCheckpointLocation();
+ Set<String> initialCheckpointSet = getFiles(checkpointLocation,
+ checkpointLocation.toString().length() + 1);
+ Path finalCheckpointLocation = Paths.get(newDbDirName);
+ Set<String> finalCheckpointSet = getFiles(finalCheckpointLocation,
+ newDbDirLength);
+
+ Assert.assertTrue("hardlink file exists in checkpoint dir",
+ finalCheckpointSet.contains(OM_HARDLINK_FILE));
+ finalCheckpointSet.remove(OM_HARDLINK_FILE);
+ Assert.assertEquals(initialCheckpointSet, finalCheckpointSet);
+
+ int metaDirLength = metaDir.toString().length() + 1;
+ String shortSnapshotLocation =
+ truncateFileName(metaDirLength, Paths.get(snapshotDirName));
+ String shortSnapshotLocation2 =
+ truncateFileName(metaDirLength, Paths.get(snapshotDirName2));
+ String shortCompactionDirLocation =
+ truncateFileName(metaDirLength, compactionDirPath);
+
+ Set<String> finalFullSet =
+ getFiles(Paths.get(testDirName, OM_SNAPSHOT_DIR), testDirLength);
+
+ // Check each line in the hard link file.
+ Stream<String> lines = Files.lines(Paths.get(newDbDirName,
+ OM_HARDLINK_FILE));
+
+ List<String> fabricatedLinkLines = new ArrayList<>();
+ for (String line: lines.collect(Collectors.toList())) {
+ Assert.assertFalse("CURRENT file is not a hard link",
+ line.contains("CURRENT"));
+ if (line.contains("fabricatedFile")) {
+ fabricatedLinkLines.add(line);
+ } else {
+ checkLine(shortSnapshotLocation, shortSnapshotLocation2, line);
+ // add links to the final set
+ finalFullSet.add(line.split("\t")[0]);
+ }
+ }
+
+ Set<String> directories = Sets.newHashSet(
+ shortSnapshotLocation, shortSnapshotLocation2,
+ shortCompactionDirLocation);
+ checkFabricatedLines(directories, fabricatedLinkLines, testDirName);
+
+ Set<String> initialFullSet =
+ getFiles(Paths.get(metaDir.toString(), OM_SNAPSHOT_DIR),
metaDirLength);
+ Assert.assertEquals("found expected snapshot files",
+ initialFullSet, finalFullSet);
}
-}
-class TestDBCheckpoint implements DBCheckpoint {
+ @Test
+ public void testWriteDbDataWithoutOmSnapshot()
+ throws Exception {
+ prepSnapshotData();
+
+ // Set http param to exclude snapshot data.
+ when(requestMock.getParameter(OZONE_DB_CHECKPOINT_INCLUDE_SNAPSHOT_DATA))
+ .thenReturn(null);
+
+ // Get the tarball.
+ try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
+ omDbCheckpointServletMock.writeDbDataToStream(dbCheckpoint, requestMock,
+ fileOutputStream);
+ }
+
+ // Untar the file into a temp folder to be examined.
+ String testDirName = folder.newFolder().getAbsolutePath();
+ int testDirLength = testDirName.length() + 1;
+ FileUtil.unTar(tempFile, new File(testDirName));
+
+ // Confirm the checkpoint directories match.
+ Path checkpointLocation = dbCheckpoint.getCheckpointLocation();
+ Set<String> initialCheckpointSet = getFiles(checkpointLocation,
+ checkpointLocation.toString().length() + 1);
+ Path finalCheckpointLocation = Paths.get(testDirName);
+ Set<String> finalCheckpointSet = getFiles(finalCheckpointLocation,
+ testDirLength);
+
+ Assert.assertEquals(initialCheckpointSet, finalCheckpointSet);
+ }
+
+
- private final Path checkpointFile;
+ private void prepSnapshotData() throws Exception {
+ setupCluster();
+ metaDir = OMStorage.getOmDbDir(conf);
+
+ OzoneBucket bucket = TestDataUtil.createVolumeAndBucket(cluster);
+
+ // Create dummy keys for snapshotting.
+ TestDataUtil.createKey(bucket, UUID.randomUUID().toString(),
+ "content");
+ TestDataUtil.createKey(bucket, UUID.randomUUID().toString(),
+ "content");
+
+ // This sleep can be removed after this is fixed:
+ // https://issues.apache.org/jira/browse/HDDS-7279
+ Thread.sleep(2000);
+ snapshotDirName =
+ createSnapshot(bucket.getVolumeName(), bucket.getName());
+ snapshotDirName2 =
+ createSnapshot(bucket.getVolumeName(), bucket.getName());
+
+ // Create dummy snapshot to make sure it is not included.
+ Path fabricatedSnapshot = Paths.get(
+ new File(snapshotDirName).getParent(),
+ "fabricatedSnapshot");
+ fabricatedSnapshot.toFile().mkdirs();
+ Paths.get(fabricatedSnapshot.toString(), "fabricatedFile")
+ .toFile().createNewFile();
+
+ // Create fabricated links to snapshot dirs
+ // to confirm that links are recognized even if
+ // they are don't point to the checkpoint directory.
+ Path fabricatedFile = Paths.get(snapshotDirName, "fabricatedFile");
+ Path fabricatedLink = Paths.get(snapshotDirName2, "fabricatedFile");
+
+ Files.write(fabricatedFile,
+ "fabricatedData".getBytes(StandardCharsets.UTF_8));
+ Files.createLink(fabricatedLink, fabricatedFile);
+
+ // Simulate links from the compaction dir.
+ compactionDirPath = Paths.get(metaDir.toString(),
+ OM_SNAPSHOT_DIFF_DIR, OM_COMPACTION_BACKUP_DIR);
+ Path fabricatedLink2 = Paths.get(compactionDirPath.toString(),
+ "fabricatedFile");
+ Files.createLink(fabricatedLink2, fabricatedFile);
+ Path currentFile = Paths.get(metaDir.toString(),
+ OM_DB_NAME, "CURRENT");
+ Path currentLink = Paths.get(compactionDirPath.toString(), "CURRENT");
+ Files.createLink(currentLink, currentFile);
+
+ dbCheckpoint = cluster.getOzoneManager()
+ .getMetadataManager().getStore()
+ .getCheckpoint(true);
- TestDBCheckpoint(Path checkpointFile) {
- this.checkpointFile = checkpointFile;
}
- @Override
- public Path getCheckpointLocation() {
- return checkpointFile;
+ private String createSnapshot(String vname, String bname)
+ throws IOException, InterruptedException, TimeoutException {
+ final OzoneManager om = cluster.getOzoneManager();
+ String snapshotName = UUID.randomUUID().toString();
+ OzoneManagerProtocol writeClient = cluster.getRpcClient().getObjectStore()
+ .getClientProxy().getOzoneManagerClient();
+
+ writeClient.createSnapshot(vname, bname, snapshotName);
+ SnapshotInfo snapshotInfo = om.getMetadataManager().getSnapshotInfoTable()
+ .get(SnapshotInfo.getTableKey(vname, bname, snapshotName));
+ String snapshotPath = getSnapshotPath(conf, snapshotInfo)
+ + OM_KEY_PREFIX;
+ GenericTestUtils.waitFor(() -> new File(snapshotPath).exists(),
+ 100, 2000);
+ return snapshotPath;
}
- @Override
- public long getCheckpointTimestamp() {
- return 0;
+ private Set<String> getFiles(Path path, int truncateLength)
+ throws IOException {
+ return getFiles(path, truncateLength, new HashSet<>());
}
- @Override
- public long getLatestSequenceNumber() {
- return 0;
+ // Get all files below path, recursively, (skipping fabricated files).
+ @SuppressFBWarnings({"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
+ private Set<String> getFiles(Path path, int truncateLength,
+ Set<String> fileSet) throws IOException {
+ try (Stream<Path> files = Files.list(path)) {
+ for (Path file : files.collect(Collectors.toList())) {
+ if (file.toFile().isDirectory()) {
+ getFiles(file, truncateLength, fileSet);
+ }
+ if (!file.getFileName().toString().startsWith("fabricated")) {
+ fileSet.add(truncateFileName(truncateLength, file));
+ }
+ }
+ }
+ return fileSet;
}
- @Override
- public long checkpointCreationTimeTaken() {
- return 0;
+ /**
+ * Confirm fabricated link lines in hardlink file are properly
+ * formatted: "dir1/fabricatedFile dir2/fabricatedFile".
+ *
+ * The "fabricated" files/links are ones I've created by hand to
+ * fully test the code, (as opposed to the "natural" files/links
+ * created by the create snapshot process).
+ *
+ * @param directories Possible directories for the links to exist in.
+ * @param lines Text lines defining the link paths.
+ * @param testDirName Name of test directory.
+ */
+ @SuppressFBWarnings({"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
+ private void checkFabricatedLines(Set<String> directories, List<String>
lines,
+ String testDirName) {
+ // find the real file
Review Comment:
Excellent javadoc!
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMDBCheckpointServlet.java:
##########
@@ -82,4 +113,166 @@ public void init() throws ServletException {
allowedGroups,
om.isSpnegoEnabled());
}
+
+ @Override
+ public void writeDbDataToStream(DBCheckpoint checkpoint,
+ HttpServletRequest request,
+ OutputStream destination)
+ throws IOException, InterruptedException {
+ // Map of inodes to path.
+ Map<Object, Path> copyFiles = new HashMap<>();
+ // Map of link to path.
+ Map<Path, Path> hardLinkFiles = new HashMap<>();
+
+ getFilesForArchive(checkpoint, copyFiles, hardLinkFiles,
+ includeSnapshotData(request));
+
+ try (TarArchiveOutputStream archiveOutputStream =
+ new TarArchiveOutputStream(destination)) {
+ archiveOutputStream
+ .setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
+ writeFilesToArchive(copyFiles, hardLinkFiles, archiveOutputStream);
+ }
+ }
+
+ private void getFilesForArchive(DBCheckpoint checkpoint,
+ Map<Object, Path> copyFiles,
+ Map<Path, Path> hardLinkFiles,
+ boolean includeSnapshotData)
+ throws IOException, InterruptedException {
+
+ // Get the active fs files.
+ Path dir = checkpoint.getCheckpointLocation();
+ processDir(dir, copyFiles, hardLinkFiles, new HashSet<>());
+
+ if (!includeSnapshotData) {
+ return;
+ }
+
+ // Get the snapshot files.
+ Set<Path> snapshotPaths = waitForSnapshotDirs(checkpoint);
+ Path snapshotDir = Paths.get(OMStorage.getOmDbDir(getConf()).toString(),
+ OM_SNAPSHOT_DIR);
+ processDir(snapshotDir, copyFiles, hardLinkFiles, snapshotPaths);
+ }
+
+ /**
+ * The snapshotInfo table may contain a snapshot that
+ * doesn't yet exist on the fs, so wait a few seconds for it.
+ * @param checkpoint Checkpoint containing snapshot entries expected.
+ * @return Set of expected snapshot dirs.
+ */
+ private Set<Path> waitForSnapshotDirs(DBCheckpoint checkpoint)
+ throws IOException, InterruptedException {
+
+ OzoneConfiguration conf = getConf();
+
+ Set<Path> snapshotPaths = new HashSet<>();
+
+ // get snapshotInfo entries
+ OmMetadataManagerImpl checkpointMetadataManager =
+ OmMetadataManagerImpl.createCheckpointMetadataManager(
+ conf, checkpoint);
+ try (TableIterator<String, ? extends Table.KeyValue<String, SnapshotInfo>>
+ iterator = checkpointMetadataManager
+ .getSnapshotInfoTable().iterator()) {
+
+ // For each entry, wait for corresponding directory.
+ while (iterator.hasNext()) {
+ Table.KeyValue<String, SnapshotInfo> entry = iterator.next();
+ Path path = Paths.get(getSnapshotPath(conf, entry.getValue()));
+ waitForDirToExist(path);
+ snapshotPaths.add(path);
+ }
+ }
+ return snapshotPaths;
+ }
+
+ private void waitForDirToExist(Path dir)
+ throws IOException, InterruptedException {
+ long endTime = System.currentTimeMillis() +
+ Duration.parse(DURATION_TO_WAIT_FOR_DIRECTORY).toMillis();
+ while (!dir.toFile().exists()) {
+ Thread.sleep(100);
+ if (System.currentTimeMillis() > endTime) {
+ throw new IOException("snapshot dir doesn't exist: " + dir);
+ }
+ }
+ }
Review Comment:
Ideally we would want to use something like
[fsnotify](https://github.com/fsnotify/fsnotify#readme). But if there is no
easy way to do this in Java, I guess this is good enough for now.
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMDBCheckpointServlet.java:
##########
@@ -82,4 +113,166 @@ public void init() throws ServletException {
allowedGroups,
om.isSpnegoEnabled());
}
+
+ @Override
+ public void writeDbDataToStream(DBCheckpoint checkpoint,
+ HttpServletRequest request,
+ OutputStream destination)
+ throws IOException, InterruptedException {
+ // Map of inodes to path.
+ Map<Object, Path> copyFiles = new HashMap<>();
+ // Map of link to path.
+ Map<Path, Path> hardLinkFiles = new HashMap<>();
+
+ getFilesForArchive(checkpoint, copyFiles, hardLinkFiles,
+ includeSnapshotData(request));
+
+ try (TarArchiveOutputStream archiveOutputStream =
+ new TarArchiveOutputStream(destination)) {
+ archiveOutputStream
+ .setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
+ writeFilesToArchive(copyFiles, hardLinkFiles, archiveOutputStream);
+ }
+ }
+
+ private void getFilesForArchive(DBCheckpoint checkpoint,
+ Map<Object, Path> copyFiles,
+ Map<Path, Path> hardLinkFiles,
+ boolean includeSnapshotData)
+ throws IOException, InterruptedException {
+
+ // Get the active fs files.
+ Path dir = checkpoint.getCheckpointLocation();
+ processDir(dir, copyFiles, hardLinkFiles, new HashSet<>());
+
+ if (!includeSnapshotData) {
+ return;
+ }
+
+ // Get the snapshot files.
+ Set<Path> snapshotPaths = waitForSnapshotDirs(checkpoint);
+ Path snapshotDir = Paths.get(OMStorage.getOmDbDir(getConf()).toString(),
+ OM_SNAPSHOT_DIR);
+ processDir(snapshotDir, copyFiles, hardLinkFiles, snapshotPaths);
+ }
+
+ /**
+ * The snapshotInfo table may contain a snapshot that
+ * doesn't yet exist on the fs, so wait a few seconds for it.
+ * @param checkpoint Checkpoint containing snapshot entries expected.
+ * @return Set of expected snapshot dirs.
+ */
+ private Set<Path> waitForSnapshotDirs(DBCheckpoint checkpoint)
+ throws IOException, InterruptedException {
+
+ OzoneConfiguration conf = getConf();
+
+ Set<Path> snapshotPaths = new HashSet<>();
+
+ // get snapshotInfo entries
+ OmMetadataManagerImpl checkpointMetadataManager =
+ OmMetadataManagerImpl.createCheckpointMetadataManager(
+ conf, checkpoint);
+ try (TableIterator<String, ? extends Table.KeyValue<String, SnapshotInfo>>
+ iterator = checkpointMetadataManager
+ .getSnapshotInfoTable().iterator()) {
+
+ // For each entry, wait for corresponding directory.
+ while (iterator.hasNext()) {
+ Table.KeyValue<String, SnapshotInfo> entry = iterator.next();
+ Path path = Paths.get(getSnapshotPath(conf, entry.getValue()));
+ waitForDirToExist(path);
+ snapshotPaths.add(path);
+ }
+ }
+ return snapshotPaths;
+ }
+
+ private void waitForDirToExist(Path dir)
+ throws IOException, InterruptedException {
+ long endTime = System.currentTimeMillis() +
+ Duration.parse(DURATION_TO_WAIT_FOR_DIRECTORY).toMillis();
+ while (!dir.toFile().exists()) {
+ Thread.sleep(100);
+ if (System.currentTimeMillis() > endTime) {
+ throw new IOException("snapshot dir doesn't exist: " + dir);
+ }
+ }
+ }
+
+ @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
+ private void processDir(Path dir, Map<Object, Path> copyFiles,
+ Map<Path, Path> hardLinkFiles,
+ Set<Path> snapshotPaths)
+ throws IOException {
+ try (Stream<Path> files = Files.list(dir)) {
+ for (Path file : files.collect(Collectors.toList())) {
+ if (file.toFile().isDirectory()) {
+ // Skip any unexpected snapshot files.
+ if (file.getParent().toString().endsWith(OM_SNAPSHOT_CHECKPOINT_DIR)
+ && !snapshotPaths.contains(file)) {
+ continue;
+ }
Review Comment:
Do we want to log those "unexpected snapshot files"? Is this worth a
`LOG.warn()`? Or at least `LOG.debug()` for debuggability.
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmSnapshotManager.java:
##########
@@ -279,6 +307,92 @@ private void verifySnapshotInfoForSnapDiff(final
SnapshotInfo fromSnapshot,
}
}
+ /**
+ * Create file of links to add to tarball.
+ * Format of entries are either:
+ * dir1/fileTo fileFrom
+ * for files in active db or:
+ * dir1/fileTo dir2/fileFrom
+ * for files in another directory, (either another snapshot dir or
+ * sst compaction backup directory)
+ * @param truncateLength - Length of initial path to trim in file path.
+ * @param hardLinkFiles - Map of link->file paths.
+ * @return Path to the file of links created.
+ */
+ @SuppressFBWarnings({"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
+ static Path createHardLinkList(int truncateLength,
+ Map<Path, Path> hardLinkFiles)
+ throws IOException {
Review Comment:
Same here
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmSnapshotManager.java:
##########
@@ -279,6 +307,92 @@ private void verifySnapshotInfoForSnapDiff(final
SnapshotInfo fromSnapshot,
}
}
+ /**
+ * Create file of links to add to tarball.
+ * Format of entries are either:
+ * dir1/fileTo fileFrom
+ * for files in active db or:
+ * dir1/fileTo dir2/fileFrom
+ * for files in another directory, (either another snapshot dir or
+ * sst compaction backup directory)
+ * @param truncateLength - Length of initial path to trim in file path.
+ * @param hardLinkFiles - Map of link->file paths.
+ * @return Path to the file of links created.
+ */
+ @SuppressFBWarnings({"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
+ static Path createHardLinkList(int truncateLength,
+ Map<Path, Path> hardLinkFiles)
+ throws IOException {
+ Path data = Files.createTempFile("data", "txt");
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<Path, Path> entry : hardLinkFiles.entrySet()) {
+ String fixedFile = truncateFileName(truncateLength, entry.getValue());
+ // If this file is from the active db, strip the path.
+ if (fixedFile.startsWith(OM_CHECKPOINT_DIR)) {
+ fixedFile = Paths.get(fixedFile).getFileName().toString();
+ }
+ sb.append(truncateFileName(truncateLength, entry.getKey()))
+ .append("\t")
+ .append(fixedFile)
+ .append("\n");
+ }
+ Files.write(data, sb.toString().getBytes(StandardCharsets.UTF_8));
+ return data;
+ }
+
+ /**
+ * Get the filename without the introductory metadata directory.
+ *
+ * @param truncateLength Length to remove.
+ * @param file File to remove prefix from.
+ * @return Truncated string.
+ */
+ static String truncateFileName(int truncateLength, Path file) {
+ return file.toString().substring(truncateLength);
+ }
+
+ /**
+ * Create hard links listed in OM_HARDLINK_FILE.
+ *
+ * @param dbPath Path to db to have links created.
+ */
+ static void createHardLinks(Path dbPath) throws IOException {
+ File hardLinkFile = new File(dbPath.toString(),
+ OM_HARDLINK_FILE);
+ if (hardLinkFile.exists()) {
+ // Read file.
+ List<String> lines =
+ Files.lines(hardLinkFile.toPath()).collect(Collectors.toList());
+
+ // Create a link for each line.
+ for (String l : lines) {
+ String from = l.split("\t")[1];
+ String to = l.split("\t")[0];
+ Path fullFromPath = getFullPath(dbPath, from);
+ Path fullToPath = getFullPath(dbPath, to);
+ Files.createLink(fullToPath, fullFromPath);
+ }
+ if (!hardLinkFile.delete()) {
+ throw new IOException(
+ "Failed to delete: " + hardLinkFile);
+ }
+ }
+ }
+
+
+ // Prepend the full path to the hard link entry entry.
+ @SuppressFBWarnings({"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
+ private static Path getFullPath(Path dbPath, String fileName) {
+ File file = new File(fileName);
+ // If there is no directory then this file belongs in the db.
+ if (file.getName().equals(fileName)) {
+ return Paths.get(dbPath.toString(), fileName);
+ }
+ // Else this file belong in a directory parallel to the db.
+ return Paths.get(dbPath.getParent().toString(), fileName);
+ }
+
+
Review Comment:
```suggestion
```
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmSnapshotManager.java:
##########
@@ -279,6 +307,92 @@ private void verifySnapshotInfoForSnapDiff(final
SnapshotInfo fromSnapshot,
}
}
+ /**
+ * Create file of links to add to tarball.
+ * Format of entries are either:
+ * dir1/fileTo fileFrom
+ * for files in active db or:
+ * dir1/fileTo dir2/fileFrom
+ * for files in another directory, (either another snapshot dir or
+ * sst compaction backup directory)
+ * @param truncateLength - Length of initial path to trim in file path.
+ * @param hardLinkFiles - Map of link->file paths.
+ * @return Path to the file of links created.
+ */
+ @SuppressFBWarnings({"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"})
+ static Path createHardLinkList(int truncateLength,
+ Map<Path, Path> hardLinkFiles)
+ throws IOException {
+ Path data = Files.createTempFile("data", "txt");
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<Path, Path> entry : hardLinkFiles.entrySet()) {
+ String fixedFile = truncateFileName(truncateLength, entry.getValue());
+ // If this file is from the active db, strip the path.
+ if (fixedFile.startsWith(OM_CHECKPOINT_DIR)) {
+ fixedFile = Paths.get(fixedFile).getFileName().toString();
+ }
+ sb.append(truncateFileName(truncateLength, entry.getKey()))
+ .append("\t")
+ .append(fixedFile)
+ .append("\n");
+ }
+ Files.write(data, sb.toString().getBytes(StandardCharsets.UTF_8));
+ return data;
+ }
+
+ /**
+ * Get the filename without the introductory metadata directory.
+ *
+ * @param truncateLength Length to remove.
+ * @param file File to remove prefix from.
+ * @return Truncated string.
+ */
+ static String truncateFileName(int truncateLength, Path file) {
+ return file.toString().substring(truncateLength);
+ }
+
+ /**
+ * Create hard links listed in OM_HARDLINK_FILE.
+ *
+ * @param dbPath Path to db to have links created.
+ */
+ static void createHardLinks(Path dbPath) throws IOException {
+ File hardLinkFile = new File(dbPath.toString(),
+ OM_HARDLINK_FILE);
+ if (hardLinkFile.exists()) {
+ // Read file.
+ List<String> lines =
+ Files.lines(hardLinkFile.toPath()).collect(Collectors.toList());
+
+ // Create a link for each line.
+ for (String l : lines) {
+ String from = l.split("\t")[1];
+ String to = l.split("\t")[0];
+ Path fullFromPath = getFullPath(dbPath, from);
+ Path fullToPath = getFullPath(dbPath, to);
+ Files.createLink(fullToPath, fullFromPath);
+ }
+ if (!hardLinkFile.delete()) {
+ throw new IOException(
+ "Failed to delete: " + hardLinkFile);
+ }
+ }
+ }
+
+
Review Comment:
nit
```suggestion
```
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java:
##########
@@ -3736,13 +3755,17 @@ File replaceOMDBWithCheckpoint(long lastAppliedIndex,
File oldDB,
// starting up.
Files.createFile(markerFile);
FileUtils.moveDirectory(checkpointPath, oldDB.toPath());
+ moveOmSnapshotData(oldDB.toPath(), dbSnapshotsDir.toPath());
Review Comment:
Do we have a consensus here?
##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java:
##########
@@ -3716,16 +3721,30 @@ File replaceOMDBWithCheckpoint(long lastAppliedIndex,
File oldDB,
String dbBackupName = OzoneConsts.OM_DB_BACKUP_PREFIX +
lastAppliedIndex + "_" + System.currentTimeMillis();
File dbDir = oldDB.getParentFile();
- File dbBackup = new File(dbDir, dbBackupName);
- try {
- Files.move(oldDB.toPath(), dbBackup.toPath());
- } catch (IOException e) {
- LOG.error("Failed to create a backup of the current DB. Aborting " +
- "snapshot installation.");
- throw e;
+ // Backup the active fs and snapshot dirs.
+ File dbBackupDir = new File(dbDir, dbBackupName);
+ if (!dbBackupDir.mkdirs()) {
+ throw new IOException("Failed to make db backup dir: " +
+ dbBackupDir);
+ }
+ File dbBackup = new File(dbBackupDir, oldDB.getName());
+ File dbSnapshotsDir = new File(dbDir, OM_SNAPSHOT_DIR);
+ File dbSnapshotsBackup = new File(dbBackupDir, OM_SNAPSHOT_DIR);
+ Files.move(oldDB.toPath(), dbBackup.toPath());
+ if (dbSnapshotsDir.exists()) {
+ Files.move(dbSnapshotsDir.toPath(),
+ dbSnapshotsBackup.toPath());
}
+ moveCheckpointFiles(oldDB, checkpointPath, dbDir, dbBackup, dbSnapshotsDir,
+ dbSnapshotsBackup);
+ return dbBackupDir;
+ }
+
+ private void moveCheckpointFiles(File oldDB, Path checkpointPath, File dbDir,
+ File dbBackup, File dbSnapshotsDir,
+ File dbSnapshotsBackup) throws IOException {
Review Comment:
nit: alignment
```suggestion
private void moveCheckpointFiles(File oldDB, Path checkpointPath, File
dbDir,
File dbBackup, File dbSnapshotsDir,
File dbSnapshotsBackup) throws
IOException {
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]