This is an automated email from the ASF dual-hosted git repository.
nanda pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new 6b8b844dfa HDDS-12009. Merge FSORepairTool and FSORepairCLI (#7639)
6b8b844dfa is described below
commit 6b8b844dfa7eb30df29ab849e834e0a00d3bc97e
Author: Doroszlai, Attila <[email protected]>
AuthorDate: Tue Jan 7 07:42:06 2025 +0100
HDDS-12009. Merge FSORepairTool and FSORepairCLI (#7639)
---
.../hadoop/ozone/repair/om/TestFSORepairTool.java | 25 +-
.../hadoop/ozone/repair/om/FSORepairCLI.java | 78 ---
.../hadoop/ozone/repair/om/FSORepairTool.java | 752 +++++++++++----------
.../apache/hadoop/ozone/repair/om/OMRepair.java | 2 +-
4 files changed, 404 insertions(+), 453 deletions(-)
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/repair/om/TestFSORepairTool.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/repair/om/TestFSORepairTool.java
index d37f8ce57f..fb6472d7bc 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/repair/om/TestFSORepairTool.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/repair/om/TestFSORepairTool.java
@@ -36,7 +36,6 @@ import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.hadoop.ozone.om.helpers.OmDirectoryInfo;
import org.apache.hadoop.ozone.repair.OzoneRepair;
import org.apache.ozone.test.GenericTestUtils;
-import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.BeforeAll;
@@ -89,8 +88,8 @@ public class TestFSORepairTool {
private static FSORepairTool.Report fullReport;
private static FSORepairTool.Report emptyReport;
- private GenericTestUtils.PrintStreamCapturer out;
- private GenericTestUtils.PrintStreamCapturer err;
+ private static GenericTestUtils.PrintStreamCapturer out;
+ private static GenericTestUtils.PrintStreamCapturer err;
@BeforeAll
public static void setup() throws Exception {
@@ -103,6 +102,8 @@ public class TestFSORepairTool {
conf.set(CommonConfigurationKeysPublic.FS_DEFAULT_NAME_KEY, rootPath);
fs = FileSystem.get(conf);
+ out = GenericTestUtils.captureOut();
+ err = GenericTestUtils.captureErr();
cmd = new OzoneRepair().getCmd();
dbPath = new File(OMStorage.getOmDbDir(conf) + "/" + OM_DB_NAME).getPath();
@@ -147,19 +148,13 @@ public class TestFSORepairTool {
@BeforeEach
public void init() throws Exception {
- out = GenericTestUtils.captureOut();
- err = GenericTestUtils.captureErr();
- }
-
- @AfterEach
- public void clean() throws Exception {
- // reset stream after each unit test
- IOUtils.closeQuietly(out, err);
+ out.reset();
+ err.reset();
}
@AfterAll
public static void reset() throws IOException {
- IOUtils.closeQuietly(fs, client, cluster);
+ IOUtils.closeQuietly(fs, client, cluster, out, err);
}
/**
@@ -239,7 +234,7 @@ public class TestFSORepairTool {
// When a non-existent bucket filter is passed
int exitCode = dryRun("--volume", "/vol1", "--bucket", "bucket3");
assertEquals(0, exitCode);
- String cliOutput = out.getOutput();
+ String cliOutput = err.getOutput();
assertThat(cliOutput).contains("Bucket 'bucket3' does not exist in volume
'/vol1'.");
}
@@ -249,7 +244,7 @@ public class TestFSORepairTool {
// When a non-existent volume filter is passed
int exitCode = dryRun("--volume", "/vol5");
assertEquals(0, exitCode);
- String cliOutput = out.getOutput();
+ String cliOutput = err.getOutput();
assertThat(cliOutput).contains("Volume '/vol5' does not exist.");
}
@@ -259,7 +254,7 @@ public class TestFSORepairTool {
// When bucket filter is passed without the volume filter.
int exitCode = dryRun("--bucket", "bucket1");
assertEquals(0, exitCode);
- String cliOutput = out.getOutput();
+ String cliOutput = err.getOutput();
assertThat(cliOutput).contains("--bucket flag cannot be used without
specifying --volume.");
}
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/om/FSORepairCLI.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/om/FSORepairCLI.java
deleted file mode 100644
index fd6d75c713..0000000000
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/om/FSORepairCLI.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.hadoop.ozone.repair.om;
-
-import org.apache.hadoop.ozone.repair.RepairTool;
-import picocli.CommandLine;
-
-/**
- * Parser for scm.db file.
- */
[email protected](
- name = "fso-tree",
- description = "Identify and repair a disconnected FSO tree by marking
unreferenced entries for deletion. " +
- "OM should be stopped while this tool is run."
-)
-public class FSORepairCLI extends RepairTool {
-
- @CommandLine.Option(names = {"--db"},
- required = true,
- description = "Path to OM RocksDB")
- private String dbPath;
-
- @CommandLine.Option(names = {"-r", "--repair"},
- defaultValue = "false",
- description = "Run in repair mode to move unreferenced files and
directories to deleted tables.")
- private boolean repair;
-
- @CommandLine.Option(names = {"-v", "--volume"},
- description = "Filter by volume name. Add '/' before the volume name.")
- private String volume;
-
- @CommandLine.Option(names = {"-b", "--bucket"},
- description = "Filter by bucket name")
- private String bucket;
-
- @CommandLine.Option(names = {"--verbose"},
- description = "Verbose output. Show all intermediate steps and deleted
keys info.")
- private boolean verbose;
-
- @Override
- public void execute() throws Exception {
- if (checkIfServiceIsRunning("OM")) {
- return;
- }
- if (repair) {
- info("FSO Repair Tool is running in repair mode");
- } else {
- info("FSO Repair Tool is running in debug mode");
- }
- try {
- FSORepairTool
- repairTool = new FSORepairTool(dbPath, repair, volume, bucket,
verbose);
- repairTool.run();
- } catch (Exception ex) {
- throw new IllegalArgumentException("FSO repair failed: " +
ex.getMessage());
- }
-
- if (verbose) {
- info("FSO repair finished.");
- }
- }
-}
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/om/FSORepairTool.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/om/FSORepairTool.java
index 7e0fb23f5a..a4068415db 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/om/FSORepairTool.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/om/FSORepairTool.java
@@ -36,9 +36,11 @@ import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
import org.apache.hadoop.ozone.om.helpers.WithObjectID;
import org.apache.hadoop.ozone.om.request.file.OMFileRequest;
+import org.apache.hadoop.ozone.repair.RepairTool;
import org.apache.ratis.util.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import picocli.CommandLine;
import java.io.File;
import java.io.IOException;
@@ -69,402 +71,471 @@ import static
org.apache.hadoop.ozone.OzoneConsts.OM_KEY_PREFIX;
* The tool is idempotent. reachable.db will not be deleted automatically when
the tool finishes,
* in case users want to manually inspect it. It can be safely deleted once
the tool finishes.
*/
-public class FSORepairTool {
[email protected](
+ name = "fso-tree",
+ description = "Identify and repair a disconnected FSO tree by marking
unreferenced entries for deletion. " +
+ "OM should be stopped while this tool is run."
+)
+public class FSORepairTool extends RepairTool {
public static final Logger LOG =
LoggerFactory.getLogger(FSORepairTool.class);
-
- private final String omDBPath;
- private final DBStore store;
- private final Table<String, OmVolumeArgs> volumeTable;
- private final Table<String, OmBucketInfo> bucketTable;
- private final Table<String, OmDirectoryInfo> directoryTable;
- private final Table<String, OmKeyInfo> fileTable;
- private final Table<String, OmKeyInfo> deletedDirectoryTable;
- private final Table<String, RepeatedOmKeyInfo> deletedTable;
- private final Table<String, SnapshotInfo> snapshotInfoTable;
- private final String volumeFilter;
- private final String bucketFilter;
private static final String REACHABLE_TABLE = "reachable";
- private DBStore reachableDB;
- private final ReportStatistics reachableStats;
- private final ReportStatistics unreachableStats;
- private final ReportStatistics unreferencedStats;
- private final boolean repair;
- private final boolean verbose;
-
- public FSORepairTool(String dbPath, boolean repair, String volume, String
bucket, boolean verbose)
- throws IOException {
- this(getStoreFromPath(dbPath), dbPath, repair, volume, bucket, verbose);
- }
- /**
- * Allows passing RocksDB instance from a MiniOzoneCluster directly to this
class for testing.
- */
- public FSORepairTool(DBStore dbStore, String dbPath, boolean repair, String
volume, String bucket, boolean verbose)
- throws IOException {
- this.reachableStats = new ReportStatistics(0, 0, 0);
- this.unreachableStats = new ReportStatistics(0, 0, 0);
- this.unreferencedStats = new ReportStatistics(0, 0, 0);
-
- this.store = dbStore;
- this.omDBPath = dbPath;
- this.repair = repair;
- this.volumeFilter = volume;
- this.bucketFilter = bucket;
- this.verbose = verbose;
- volumeTable = store.getTable(OmMetadataManagerImpl.VOLUME_TABLE,
- String.class,
- OmVolumeArgs.class);
- bucketTable = store.getTable(OmMetadataManagerImpl.BUCKET_TABLE,
- String.class,
- OmBucketInfo.class);
- directoryTable = store.getTable(OmMetadataManagerImpl.DIRECTORY_TABLE,
- String.class,
- OmDirectoryInfo.class);
- fileTable = store.getTable(OmMetadataManagerImpl.FILE_TABLE,
- String.class,
- OmKeyInfo.class);
- deletedDirectoryTable =
store.getTable(OmMetadataManagerImpl.DELETED_DIR_TABLE,
- String.class,
- OmKeyInfo.class);
- deletedTable = store.getTable(OmMetadataManagerImpl.DELETED_TABLE,
- String.class,
- RepeatedOmKeyInfo.class);
- snapshotInfoTable =
store.getTable(OmMetadataManagerImpl.SNAPSHOT_INFO_TABLE,
- String.class,
- SnapshotInfo.class);
- }
+ @CommandLine.Option(names = {"--db"},
+ required = true,
+ description = "Path to OM RocksDB")
+ private String omDBPath;
- protected static DBStore getStoreFromPath(String dbPath) throws IOException {
- File omDBFile = new File(dbPath);
- if (!omDBFile.exists() || !omDBFile.isDirectory()) {
- throw new IOException(String.format("Specified OM DB instance %s does " +
- "not exist or is not a RocksDB directory.", dbPath));
- }
- // Load RocksDB and tables needed.
- return OmMetadataManagerImpl.loadDB(new OzoneConfiguration(), new
File(dbPath).getParentFile(), -1);
- }
+ @CommandLine.Option(names = {"-r", "--repair"},
+ defaultValue = "false",
+ description = "Run in repair mode to move unreferenced files and
directories to deleted tables.")
+ private boolean repair;
+
+ @CommandLine.Option(names = {"-v", "--volume"},
+ description = "Filter by volume name. Add '/' before the volume name.")
+ private String volumeFilter;
- public FSORepairTool.Report run() throws Exception {
+ @CommandLine.Option(names = {"-b", "--bucket"},
+ description = "Filter by bucket name")
+ private String bucketFilter;
+
+ @CommandLine.Option(names = {"--verbose"},
+ description = "Verbose output. Show all intermediate steps and deleted
keys info.")
+ private boolean verbose;
+
+ @Override
+ public void execute() throws Exception {
+ if (checkIfServiceIsRunning("OM")) {
+ return;
+ }
+ if (repair) {
+ info("FSO Repair Tool is running in repair mode");
+ } else {
+ info("FSO Repair Tool is running in debug mode");
+ }
try {
- if (bucketFilter != null && volumeFilter == null) {
- System.out.println("--bucket flag cannot be used without specifying
--volume.");
- return null;
- }
+ Impl repairTool = new Impl();
+ repairTool.run();
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("FSO repair failed: " +
ex.getMessage());
+ }
- if (volumeFilter != null) {
- OmVolumeArgs volumeArgs = volumeTable.getIfExist(volumeFilter);
- if (volumeArgs == null) {
- System.out.println("Volume '" + volumeFilter + "' does not exist.");
+ if (verbose) {
+ info("FSO repair finished.");
+ }
+ }
+
+ private class Impl {
+
+ private final DBStore store;
+ private final Table<String, OmVolumeArgs> volumeTable;
+ private final Table<String, OmBucketInfo> bucketTable;
+ private final Table<String, OmDirectoryInfo> directoryTable;
+ private final Table<String, OmKeyInfo> fileTable;
+ private final Table<String, OmKeyInfo> deletedDirectoryTable;
+ private final Table<String, RepeatedOmKeyInfo> deletedTable;
+ private final Table<String, SnapshotInfo> snapshotInfoTable;
+ private DBStore reachableDB;
+ private final ReportStatistics reachableStats;
+ private final ReportStatistics unreachableStats;
+ private final ReportStatistics unreferencedStats;
+
+ Impl() throws IOException {
+ this.reachableStats = new ReportStatistics(0, 0, 0);
+ this.unreachableStats = new ReportStatistics(0, 0, 0);
+ this.unreferencedStats = new ReportStatistics(0, 0, 0);
+
+ this.store = getStoreFromPath(omDBPath);
+ volumeTable = store.getTable(OmMetadataManagerImpl.VOLUME_TABLE,
+ String.class,
+ OmVolumeArgs.class);
+ bucketTable = store.getTable(OmMetadataManagerImpl.BUCKET_TABLE,
+ String.class,
+ OmBucketInfo.class);
+ directoryTable = store.getTable(OmMetadataManagerImpl.DIRECTORY_TABLE,
+ String.class,
+ OmDirectoryInfo.class);
+ fileTable = store.getTable(OmMetadataManagerImpl.FILE_TABLE,
+ String.class,
+ OmKeyInfo.class);
+ deletedDirectoryTable =
store.getTable(OmMetadataManagerImpl.DELETED_DIR_TABLE,
+ String.class,
+ OmKeyInfo.class);
+ deletedTable = store.getTable(OmMetadataManagerImpl.DELETED_TABLE,
+ String.class,
+ RepeatedOmKeyInfo.class);
+ snapshotInfoTable =
store.getTable(OmMetadataManagerImpl.SNAPSHOT_INFO_TABLE,
+ String.class,
+ SnapshotInfo.class);
+ }
+
+ public Report run() throws Exception {
+ try {
+ if (bucketFilter != null && volumeFilter == null) {
+ error("--bucket flag cannot be used without specifying --volume.");
return null;
}
- }
- // Iterate all volumes or a specific volume if specified
- try (TableIterator<String, ? extends Table.KeyValue<String,
OmVolumeArgs>>
- volumeIterator = volumeTable.iterator()) {
- try {
- openReachableDB();
- } catch (IOException e) {
- System.out.println("Failed to open reachable database: " +
e.getMessage());
- throw e;
+ if (volumeFilter != null) {
+ OmVolumeArgs volumeArgs = volumeTable.getIfExist(volumeFilter);
+ if (volumeArgs == null) {
+ error("Volume '" + volumeFilter + "' does not exist.");
+ return null;
+ }
}
- while (volumeIterator.hasNext()) {
- Table.KeyValue<String, OmVolumeArgs> volumeEntry =
volumeIterator.next();
- String volumeKey = volumeEntry.getKey();
- if (volumeFilter != null && !volumeFilter.equals(volumeKey)) {
- continue;
+ // Iterate all volumes or a specific volume if specified
+ try (TableIterator<String, ? extends Table.KeyValue<String,
OmVolumeArgs>>
+ volumeIterator = volumeTable.iterator()) {
+ try {
+ openReachableDB();
+ } catch (IOException e) {
+ error("Failed to open reachable database: " + e.getMessage());
+ throw e;
}
+ while (volumeIterator.hasNext()) {
+ Table.KeyValue<String, OmVolumeArgs> volumeEntry =
volumeIterator.next();
+ String volumeKey = volumeEntry.getKey();
- System.out.println("Processing volume: " + volumeKey);
-
- if (bucketFilter != null) {
- OmBucketInfo bucketInfo = bucketTable.getIfExist(volumeKey + "/" +
bucketFilter);
- if (bucketInfo == null) {
- //Bucket does not exist in the volume
- System.out.println("Bucket '" + bucketFilter + "' does not exist
in volume '" + volumeKey + "'.");
- return null;
- }
-
- if (bucketInfo.getBucketLayout() !=
BucketLayout.FILE_SYSTEM_OPTIMIZED) {
- System.out.println("Skipping non-FSO bucket " + bucketFilter);
+ if (volumeFilter != null && !volumeFilter.equals(volumeKey)) {
continue;
}
- processBucket(volumeEntry.getValue(), bucketInfo);
- } else {
+ info("Processing volume: " + volumeKey);
- // Iterate all buckets in the volume.
- try (TableIterator<String, ? extends Table.KeyValue<String,
OmBucketInfo>>
- bucketIterator = bucketTable.iterator()) {
- bucketIterator.seek(volumeKey);
- while (bucketIterator.hasNext()) {
- Table.KeyValue<String, OmBucketInfo> bucketEntry =
bucketIterator.next();
- String bucketKey = bucketEntry.getKey();
- OmBucketInfo bucketInfo = bucketEntry.getValue();
-
- if (bucketInfo.getBucketLayout() !=
BucketLayout.FILE_SYSTEM_OPTIMIZED) {
- System.out.println("Skipping non-FSO bucket " + bucketKey);
- continue;
- }
+ if (bucketFilter != null) {
+ OmBucketInfo bucketInfo = bucketTable.getIfExist(volumeKey + "/"
+ bucketFilter);
+ if (bucketInfo == null) {
+ //Bucket does not exist in the volume
+ error("Bucket '" + bucketFilter + "' does not exist in volume
'" + volumeKey + "'.");
+ return null;
+ }
- // Stop this loop once we have seen all buckets in the current
- // volume.
- if (!bucketKey.startsWith(volumeKey)) {
- break;
- }
+ if (bucketInfo.getBucketLayout() !=
BucketLayout.FILE_SYSTEM_OPTIMIZED) {
+ info("Skipping non-FSO bucket " + bucketFilter);
+ continue;
+ }
- processBucket(volumeEntry.getValue(), bucketInfo);
+ processBucket(volumeEntry.getValue(), bucketInfo);
+ } else {
+
+ // Iterate all buckets in the volume.
+ try (TableIterator<String, ? extends Table.KeyValue<String,
OmBucketInfo>>
+ bucketIterator = bucketTable.iterator()) {
+ bucketIterator.seek(volumeKey);
+ while (bucketIterator.hasNext()) {
+ Table.KeyValue<String, OmBucketInfo> bucketEntry =
bucketIterator.next();
+ String bucketKey = bucketEntry.getKey();
+ OmBucketInfo bucketInfo = bucketEntry.getValue();
+
+ if (bucketInfo.getBucketLayout() !=
BucketLayout.FILE_SYSTEM_OPTIMIZED) {
+ info("Skipping non-FSO bucket " + bucketKey);
+ continue;
+ }
+
+ // Stop this loop once we have seen all buckets in the
current
+ // volume.
+ if (!bucketKey.startsWith(volumeKey)) {
+ break;
+ }
+
+ processBucket(volumeEntry.getValue(), bucketInfo);
+ }
}
}
}
}
+ } catch (IOException e) {
+ error("An error occurred while processing" + e.getMessage());
+ throw e;
+ } finally {
+ closeReachableDB();
+ store.close();
}
- } catch (IOException e) {
- System.out.println("An error occurred while processing" +
e.getMessage());
- throw e;
- } finally {
- closeReachableDB();
- store.close();
+
+ return buildReportAndLog();
}
- return buildReportAndLog();
- }
+ private boolean checkIfSnapshotExistsForBucket(String volumeName, String
bucketName) throws IOException {
+ if (snapshotInfoTable == null) {
+ return false;
+ }
- private boolean checkIfSnapshotExistsForBucket(String volumeName, String
bucketName) throws IOException {
- if (snapshotInfoTable == null) {
+ try (TableIterator<String, ? extends Table.KeyValue<String,
SnapshotInfo>> iterator =
+ snapshotInfoTable.iterator()) {
+ while (iterator.hasNext()) {
+ SnapshotInfo snapshotInfo = iterator.next().getValue();
+ String snapshotPath = (volumeName + "/" +
bucketName).replaceFirst("^/", "");
+ if (snapshotInfo.getSnapshotPath().equals(snapshotPath)) {
+ return true;
+ }
+ }
+ }
return false;
}
- try (TableIterator<String, ? extends Table.KeyValue<String, SnapshotInfo>>
iterator =
- snapshotInfoTable.iterator()) {
- while (iterator.hasNext()) {
- SnapshotInfo snapshotInfo = iterator.next().getValue();
- String snapshotPath = (volumeName + "/" +
bucketName).replaceFirst("^/", "");
- if (snapshotInfo.getSnapshotPath().equals(snapshotPath)) {
- return true;
+ private void processBucket(OmVolumeArgs volume, OmBucketInfo bucketInfo)
throws IOException {
+ info("Processing bucket: " + volume.getVolume() + "/" +
bucketInfo.getBucketName());
+ if (checkIfSnapshotExistsForBucket(volume.getVolume(),
bucketInfo.getBucketName())) {
+ if (!repair) {
+ info(
+ "Snapshot detected in bucket '" + volume.getVolume() + "/" +
bucketInfo.getBucketName() + "'. ");
+ } else {
+ info(
+ "Skipping repair for bucket '" + volume.getVolume() + "/" +
bucketInfo.getBucketName() + "' " +
+ "due to snapshot presence.");
+ return;
}
}
+ markReachableObjectsInBucket(volume, bucketInfo);
+ handleUnreachableAndUnreferencedObjects(volume, bucketInfo);
}
- return false;
- }
- private void processBucket(OmVolumeArgs volume, OmBucketInfo bucketInfo)
throws IOException {
- System.out.println("Processing bucket: " + volume.getVolume() + "/" +
bucketInfo.getBucketName());
- if (checkIfSnapshotExistsForBucket(volume.getVolume(),
bucketInfo.getBucketName())) {
- if (!repair) {
- System.out.println(
- "Snapshot detected in bucket '" + volume.getVolume() + "/" +
bucketInfo.getBucketName() + "'. ");
- } else {
- System.out.println(
- "Skipping repair for bucket '" + volume.getVolume() + "/" +
bucketInfo.getBucketName() + "' " +
- "due to snapshot presence.");
- return;
- }
+ private Report buildReportAndLog() {
+ Report report = new Report.Builder()
+ .setReachable(reachableStats)
+ .setUnreachable(unreachableStats)
+ .setUnreferenced(unreferencedStats)
+ .build();
+
+ info("\n" + report);
+ return report;
}
- markReachableObjectsInBucket(volume, bucketInfo);
- handleUnreachableAndUnreferencedObjects(volume, bucketInfo);
- }
- private Report buildReportAndLog() {
- Report report = new Report.Builder()
- .setReachable(reachableStats)
- .setUnreachable(unreachableStats)
- .setUnreferenced(unreferencedStats)
- .build();
+ private void markReachableObjectsInBucket(OmVolumeArgs volume,
OmBucketInfo bucket) throws IOException {
+ // Only put directories in the stack.
+ // Directory keys should have the form /volumeID/bucketID/parentID/name.
+ Stack<String> dirKeyStack = new Stack<>();
- System.out.println("\n" + report);
- return report;
- }
+ // Since the tool uses parent directories to check for reachability, add
+ // a reachable entry for the bucket as well.
+ addReachableEntry(volume, bucket, bucket);
+ // Initialize the stack with all immediate child directories of the
+ // bucket, and mark them all as reachable.
+ Collection<String> childDirs =
getChildDirectoriesAndMarkAsReachable(volume, bucket, bucket);
+ dirKeyStack.addAll(childDirs);
+
+ while (!dirKeyStack.isEmpty()) {
+ // Get one directory and process its immediate children.
+ String currentDirKey = dirKeyStack.pop();
+ OmDirectoryInfo currentDir = directoryTable.get(currentDirKey);
+ if (currentDir == null) {
+ info("Directory key" + currentDirKey + "to be processed was not
found in the directory table.");
+ continue;
+ }
- private void markReachableObjectsInBucket(OmVolumeArgs volume, OmBucketInfo
bucket) throws IOException {
- // Only put directories in the stack.
- // Directory keys should have the form /volumeID/bucketID/parentID/name.
- Stack<String> dirKeyStack = new Stack<>();
-
- // Since the tool uses parent directories to check for reachability, add
- // a reachable entry for the bucket as well.
- addReachableEntry(volume, bucket, bucket);
- // Initialize the stack with all immediate child directories of the
- // bucket, and mark them all as reachable.
- Collection<String> childDirs =
getChildDirectoriesAndMarkAsReachable(volume, bucket, bucket);
- dirKeyStack.addAll(childDirs);
-
- while (!dirKeyStack.isEmpty()) {
- // Get one directory and process its immediate children.
- String currentDirKey = dirKeyStack.pop();
- OmDirectoryInfo currentDir = directoryTable.get(currentDirKey);
- if (currentDir == null) {
- System.out.println("Directory key" + currentDirKey + "to be processed
was not found in the directory table.");
- continue;
+ // TODO revisit this for a more memory efficient implementation,
+ // possibly making better use of RocksDB iterators.
+ childDirs = getChildDirectoriesAndMarkAsReachable(volume, bucket,
currentDir);
+ dirKeyStack.addAll(childDirs);
}
+ }
- // TODO revisit this for a more memory efficient implementation,
- // possibly making better use of RocksDB iterators.
- childDirs = getChildDirectoriesAndMarkAsReachable(volume, bucket,
currentDir);
- dirKeyStack.addAll(childDirs);
+ private boolean isDirectoryInDeletedDirTable(String dirKey) throws
IOException {
+ return deletedDirectoryTable.isExist(dirKey);
}
- }
- private boolean isDirectoryInDeletedDirTable(String dirKey) throws
IOException {
- return deletedDirectoryTable.isExist(dirKey);
- }
+ private boolean isFileKeyInDeletedTable(String fileKey) throws IOException
{
+ return deletedTable.isExist(fileKey);
+ }
- private boolean isFileKeyInDeletedTable(String fileKey) throws IOException {
- return deletedTable.isExist(fileKey);
- }
+ private void handleUnreachableAndUnreferencedObjects(OmVolumeArgs volume,
OmBucketInfo bucket) throws IOException {
+ // Check for unreachable and unreferenced directories in the bucket.
+ String bucketPrefix = OM_KEY_PREFIX +
+ volume.getObjectID() +
+ OM_KEY_PREFIX +
+ bucket.getObjectID();
- private void handleUnreachableAndUnreferencedObjects(OmVolumeArgs volume,
OmBucketInfo bucket) throws IOException {
- // Check for unreachable and unreferenced directories in the bucket.
- String bucketPrefix = OM_KEY_PREFIX +
- volume.getObjectID() +
- OM_KEY_PREFIX +
- bucket.getObjectID();
-
- try (TableIterator<String, ? extends Table.KeyValue<String,
OmDirectoryInfo>> dirIterator =
- directoryTable.iterator()) {
- dirIterator.seek(bucketPrefix);
- while (dirIterator.hasNext()) {
- Table.KeyValue<String, OmDirectoryInfo> dirEntry = dirIterator.next();
- String dirKey = dirEntry.getKey();
-
- // Only search directories in this bucket.
- if (!dirKey.startsWith(bucketPrefix)) {
- break;
- }
+ try (TableIterator<String, ? extends Table.KeyValue<String,
OmDirectoryInfo>> dirIterator =
+ directoryTable.iterator()) {
+ dirIterator.seek(bucketPrefix);
+ while (dirIterator.hasNext()) {
+ Table.KeyValue<String, OmDirectoryInfo> dirEntry =
dirIterator.next();
+ String dirKey = dirEntry.getKey();
+
+ // Only search directories in this bucket.
+ if (!dirKey.startsWith(bucketPrefix)) {
+ break;
+ }
- if (!isReachable(dirKey)) {
- if (!isDirectoryInDeletedDirTable(dirKey)) {
- System.out.println("Found unreferenced directory: " + dirKey);
- unreferencedStats.addDir();
+ if (!isReachable(dirKey)) {
+ if (!isDirectoryInDeletedDirTable(dirKey)) {
+ info("Found unreferenced directory: " + dirKey);
+ unreferencedStats.addDir();
- if (!repair) {
- if (verbose) {
- System.out.println("Marking unreferenced directory " + dirKey
+ " for deletion.");
+ if (!repair) {
+ if (verbose) {
+ info("Marking unreferenced directory " + dirKey + " for
deletion.");
+ }
+ } else {
+ info("Deleting unreferenced directory " + dirKey);
+ OmDirectoryInfo dirInfo = dirEntry.getValue();
+ markDirectoryForDeletion(volume.getVolume(),
bucket.getBucketName(), dirKey, dirInfo);
}
} else {
- System.out.println("Deleting unreferenced directory " + dirKey);
- OmDirectoryInfo dirInfo = dirEntry.getValue();
- markDirectoryForDeletion(volume.getVolume(),
bucket.getBucketName(), dirKey, dirInfo);
+ unreachableStats.addDir();
}
- } else {
- unreachableStats.addDir();
}
}
}
- }
- // Check for unreachable and unreferenced files
- try (TableIterator<String, ? extends Table.KeyValue<String, OmKeyInfo>>
- fileIterator = fileTable.iterator()) {
- fileIterator.seek(bucketPrefix);
- while (fileIterator.hasNext()) {
- Table.KeyValue<String, OmKeyInfo> fileEntry = fileIterator.next();
- String fileKey = fileEntry.getKey();
- // Only search files in this bucket.
- if (!fileKey.startsWith(bucketPrefix)) {
- break;
- }
+ // Check for unreachable and unreferenced files
+ try (TableIterator<String, ? extends Table.KeyValue<String, OmKeyInfo>>
+ fileIterator = fileTable.iterator()) {
+ fileIterator.seek(bucketPrefix);
+ while (fileIterator.hasNext()) {
+ Table.KeyValue<String, OmKeyInfo> fileEntry = fileIterator.next();
+ String fileKey = fileEntry.getKey();
+ // Only search files in this bucket.
+ if (!fileKey.startsWith(bucketPrefix)) {
+ break;
+ }
- OmKeyInfo fileInfo = fileEntry.getValue();
- if (!isReachable(fileKey)) {
- if (!isFileKeyInDeletedTable(fileKey)) {
- System.out.println("Found unreferenced file: " + fileKey);
- unreferencedStats.addFile(fileInfo.getDataSize());
+ OmKeyInfo fileInfo = fileEntry.getValue();
+ if (!isReachable(fileKey)) {
+ if (!isFileKeyInDeletedTable(fileKey)) {
+ info("Found unreferenced file: " + fileKey);
+ unreferencedStats.addFile(fileInfo.getDataSize());
- if (!repair) {
- if (verbose) {
- System.out.println("Marking unreferenced file " + fileKey + "
for deletion." + fileKey);
+ if (!repair) {
+ if (verbose) {
+ info("Marking unreferenced file " + fileKey + " for
deletion." + fileKey);
+ }
+ } else {
+ info("Deleting unreferenced file " + fileKey);
+ markFileForDeletion(fileKey, fileInfo);
}
} else {
- System.out.println("Deleting unreferenced file " + fileKey);
- markFileForDeletion(fileKey, fileInfo);
+ unreachableStats.addFile(fileInfo.getDataSize());
}
} else {
- unreachableStats.addFile(fileInfo.getDataSize());
+ // NOTE: We are deserializing the proto of every reachable file
+ // just to log it's size. If we don't need this information we
could
+ // save time by skipping this step.
+ reachableStats.addFile(fileInfo.getDataSize());
}
- } else {
- // NOTE: We are deserializing the proto of every reachable file
- // just to log it's size. If we don't need this information we could
- // save time by skipping this step.
- reachableStats.addFile(fileInfo.getDataSize());
}
}
}
- }
- protected void markFileForDeletion(String fileKey, OmKeyInfo fileInfo)
throws IOException {
- try (BatchOperation batch = store.initBatchOperation()) {
- fileTable.deleteWithBatch(batch, fileKey);
-
- RepeatedOmKeyInfo originalRepeatedKeyInfo = deletedTable.get(fileKey);
- RepeatedOmKeyInfo updatedRepeatedOmKeyInfo = OmUtils.prepareKeyForDelete(
- fileInfo, fileInfo.getUpdateID(), true);
- // NOTE: The FSO code seems to write the open key entry with the whole
- // path, using the object's names instead of their ID. This would only
- // be possible when the file is deleted explicitly, and not part of a
- // directory delete. It is also not possible here if the file's parent
- // is gone. The name of the key does not matter so just use IDs.
- deletedTable.putWithBatch(batch, fileKey, updatedRepeatedOmKeyInfo);
- if (verbose) {
- System.out.println("Added entry " + fileKey + " to open key table: " +
updatedRepeatedOmKeyInfo);
+ protected void markFileForDeletion(String fileKey, OmKeyInfo fileInfo)
throws IOException {
+ try (BatchOperation batch = store.initBatchOperation()) {
+ fileTable.deleteWithBatch(batch, fileKey);
+
+ RepeatedOmKeyInfo originalRepeatedKeyInfo = deletedTable.get(fileKey);
+ RepeatedOmKeyInfo updatedRepeatedOmKeyInfo =
OmUtils.prepareKeyForDelete(
+ fileInfo, fileInfo.getUpdateID(), true);
+ // NOTE: The FSO code seems to write the open key entry with the whole
+ // path, using the object's names instead of their ID. This would only
+ // be possible when the file is deleted explicitly, and not part of a
+ // directory delete. It is also not possible here if the file's parent
+ // is gone. The name of the key does not matter so just use IDs.
+ deletedTable.putWithBatch(batch, fileKey, updatedRepeatedOmKeyInfo);
+ if (verbose) {
+ info("Added entry " + fileKey + " to open key table: " +
updatedRepeatedOmKeyInfo);
+ }
+ store.commitBatchOperation(batch);
}
- store.commitBatchOperation(batch);
}
- }
- protected void markDirectoryForDeletion(String volumeName, String bucketName,
- String dirKeyName, OmDirectoryInfo
dirInfo) throws IOException {
- try (BatchOperation batch = store.initBatchOperation()) {
- directoryTable.deleteWithBatch(batch, dirKeyName);
- // HDDS-7592: Make directory entries in deleted dir table unique.
- String deleteDirKeyName = dirKeyName + OM_KEY_PREFIX +
dirInfo.getObjectID();
+ protected void markDirectoryForDeletion(String volumeName, String
bucketName,
+ String dirKeyName, OmDirectoryInfo dirInfo) throws IOException {
+ try (BatchOperation batch = store.initBatchOperation()) {
+ directoryTable.deleteWithBatch(batch, dirKeyName);
+ // HDDS-7592: Make directory entries in deleted dir table unique.
+ String deleteDirKeyName = dirKeyName + OM_KEY_PREFIX +
dirInfo.getObjectID();
- // Convert the directory to OmKeyInfo for deletion.
- OmKeyInfo dirAsKeyInfo = OMFileRequest.getOmKeyInfo(volumeName,
bucketName, dirInfo, dirInfo.getName());
- deletedDirectoryTable.putWithBatch(batch, deleteDirKeyName,
dirAsKeyInfo);
+ // Convert the directory to OmKeyInfo for deletion.
+ OmKeyInfo dirAsKeyInfo = OMFileRequest.getOmKeyInfo(volumeName,
bucketName, dirInfo, dirInfo.getName());
+ deletedDirectoryTable.putWithBatch(batch, deleteDirKeyName,
dirAsKeyInfo);
- store.commitBatchOperation(batch);
+ store.commitBatchOperation(batch);
+ }
}
- }
- private Collection<String>
getChildDirectoriesAndMarkAsReachable(OmVolumeArgs volume, OmBucketInfo bucket,
-
WithObjectID currentDir) throws IOException {
-
- Collection<String> childDirs = new ArrayList<>();
-
- try (TableIterator<String, ? extends Table.KeyValue<String,
OmDirectoryInfo>>
- dirIterator = directoryTable.iterator()) {
- String dirPrefix = buildReachableKey(volume, bucket, currentDir);
- // Start searching the directory table at the current directory's
- // prefix to get its immediate children.
- dirIterator.seek(dirPrefix);
- while (dirIterator.hasNext()) {
- Table.KeyValue<String, OmDirectoryInfo> childDirEntry =
dirIterator.next();
- String childDirKey = childDirEntry.getKey();
- // Stop processing once we have seen all immediate children of this
- // directory.
- if (!childDirKey.startsWith(dirPrefix)) {
- break;
+ private Collection<String>
getChildDirectoriesAndMarkAsReachable(OmVolumeArgs volume, OmBucketInfo bucket,
+ WithObjectID currentDir) throws IOException {
+
+ Collection<String> childDirs = new ArrayList<>();
+
+ try (TableIterator<String, ? extends Table.KeyValue<String,
OmDirectoryInfo>>
+ dirIterator = directoryTable.iterator()) {
+ String dirPrefix = buildReachableKey(volume, bucket, currentDir);
+ // Start searching the directory table at the current directory's
+ // prefix to get its immediate children.
+ dirIterator.seek(dirPrefix);
+ while (dirIterator.hasNext()) {
+ Table.KeyValue<String, OmDirectoryInfo> childDirEntry =
dirIterator.next();
+ String childDirKey = childDirEntry.getKey();
+ // Stop processing once we have seen all immediate children of this
+ // directory.
+ if (!childDirKey.startsWith(dirPrefix)) {
+ break;
+ }
+ // This directory was reached by search.
+ addReachableEntry(volume, bucket, childDirEntry.getValue());
+ childDirs.add(childDirKey);
+ reachableStats.addDir();
}
- // This directory was reached by search.
- addReachableEntry(volume, bucket, childDirEntry.getValue());
- childDirs.add(childDirKey);
- reachableStats.addDir();
}
+
+ return childDirs;
+ }
+
+ /**
+ * Add the specified object to the reachable table, indicating it is part
+ * of the connected FSO tree.
+ */
+ private void addReachableEntry(OmVolumeArgs volume, OmBucketInfo bucket,
WithObjectID object) throws IOException {
+ String reachableKey = buildReachableKey(volume, bucket, object);
+ // No value is needed for this table.
+ reachableDB.getTable(REACHABLE_TABLE, String.class,
byte[].class).put(reachableKey, new byte[]{});
+ }
+
+ /**
+ * @param fileOrDirKey The key of a file or directory in RocksDB.
+ * @return true if the entry's parent is in the reachable table.
+ */
+ protected boolean isReachable(String fileOrDirKey) throws IOException {
+ String reachableParentKey = buildReachableParentKey(fileOrDirKey);
+
+ return reachableDB.getTable(REACHABLE_TABLE, String.class,
byte[].class).get(reachableParentKey) != null;
+ }
+
+ private void openReachableDB() throws IOException {
+ File reachableDBFile = new File(new File(omDBPath).getParentFile(),
"reachable.db");
+ info("Creating database of reachable directories at " + reachableDBFile);
+ // Delete the DB from the last run if it exists.
+ if (reachableDBFile.exists()) {
+ FileUtils.deleteDirectory(reachableDBFile);
+ }
+
+ ConfigurationSource conf = new OzoneConfiguration();
+ reachableDB = DBStoreBuilder.newBuilder(conf)
+ .setName("reachable.db")
+ .setPath(reachableDBFile.getParentFile().toPath())
+ .addTable(REACHABLE_TABLE)
+ .build();
}
- return childDirs;
+ private void closeReachableDB() throws IOException {
+ if (reachableDB != null) {
+ reachableDB.close();
+ }
+ File reachableDBFile = new File(new File(omDBPath).getParentFile(),
"reachable.db");
+ if (reachableDBFile.exists()) {
+ FileUtils.deleteDirectory(reachableDBFile);
+ }
+ }
}
- /**
- * Add the specified object to the reachable table, indicating it is part
- * of the connected FSO tree.
- */
- private void addReachableEntry(OmVolumeArgs volume, OmBucketInfo bucket,
WithObjectID object) throws IOException {
- String reachableKey = buildReachableKey(volume, bucket, object);
- // No value is needed for this table.
- reachableDB.getTable(REACHABLE_TABLE, String.class,
byte[].class).put(reachableKey, new byte[]{});
+ protected static DBStore getStoreFromPath(String dbPath) throws IOException {
+ File omDBFile = new File(dbPath);
+ if (!omDBFile.exists() || !omDBFile.isDirectory()) {
+ throw new IOException(String.format("Specified OM DB instance %s does " +
+ "not exist or is not a RocksDB directory.", dbPath));
+ }
+ // Load RocksDB and tables needed.
+ return OmMetadataManagerImpl.loadDB(new OzoneConfiguration(), new
File(dbPath).getParentFile(), -1);
}
/**
@@ -480,17 +551,6 @@ public class FSORepairTool {
object.getObjectID();
}
- /**
- *
- * @param fileOrDirKey The key of a file or directory in RocksDB.
- * @return true if the entry's parent is in the reachable table.
- */
- protected boolean isReachable(String fileOrDirKey) throws IOException {
- String reachableParentKey = buildReachableParentKey(fileOrDirKey);
-
- return reachableDB.getTable(REACHABLE_TABLE, String.class,
byte[].class).get(reachableParentKey) != null;
- }
-
/**
* Build an entry in the reachable table for the current object's parent
* object. The object could be a file or directory.
@@ -512,32 +572,6 @@ public class FSORepairTool {
parentID;
}
- private void openReachableDB() throws IOException {
- File reachableDBFile = new File(new File(omDBPath).getParentFile(),
"reachable.db");
- System.out.println("Creating database of reachable directories at " +
reachableDBFile);
- // Delete the DB from the last run if it exists.
- if (reachableDBFile.exists()) {
- FileUtils.deleteDirectory(reachableDBFile);
- }
-
- ConfigurationSource conf = new OzoneConfiguration();
- reachableDB = DBStoreBuilder.newBuilder(conf)
- .setName("reachable.db")
- .setPath(reachableDBFile.getParentFile().toPath())
- .addTable(REACHABLE_TABLE)
- .build();
- }
-
- private void closeReachableDB() throws IOException {
- if (reachableDB != null) {
- reachableDB.close();
- }
- File reachableDBFile = new File(new File(omDBPath).getParentFile(),
"reachable.db");
- if (reachableDBFile.exists()) {
- FileUtils.deleteDirectory(reachableDBFile);
- }
- }
-
/**
* Define a Report to be created.
*/
@@ -549,19 +583,19 @@ public class FSORepairTool {
/**
* Builds one report that is the aggregate of multiple others.
*/
- public Report(FSORepairTool.Report... reports) {
+ public Report(Report... reports) {
reachable = new ReportStatistics();
unreachable = new ReportStatistics();
unreferenced = new ReportStatistics();
- for (FSORepairTool.Report report : reports) {
+ for (Report report : reports) {
reachable.add(report.reachable);
unreachable.add(report.unreachable);
unreferenced.add(report.unreferenced);
}
}
- private Report(FSORepairTool.Report.Builder builder) {
+ private Report(Report.Builder builder) {
this.reachable = builder.reachable;
this.unreachable = builder.unreachable;
this.unreferenced = builder.unreferenced;
@@ -591,7 +625,7 @@ public class FSORepairTool {
if (other == null || getClass() != other.getClass()) {
return false;
}
- FSORepairTool.Report report = (FSORepairTool.Report) other;
+ Report report = (Report) other;
// Useful for testing.
System.out.println("Comparing reports\nExpect:\n" + this + "\nActual:\n"
+ report);
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/om/OMRepair.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/om/OMRepair.java
index 3b880f8754..9e20f6b9d1 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/om/OMRepair.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/repair/om/OMRepair.java
@@ -27,7 +27,7 @@ import picocli.CommandLine;
*/
@CommandLine.Command(name = "om",
subcommands = {
- FSORepairCLI.class,
+ FSORepairTool.class,
SnapshotRepair.class,
TransactionInfoRepair.class
},
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]