This is an automated email from the ASF dual-hosted git repository.
errose28 pushed a commit to branch HDDS-14496-zdu
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/HDDS-14496-zdu by this push:
new eba78b6c4a8 HDDS-15208. OM should learn to finalize from SCM after
finalization command (#10236)
eba78b6c4a8 is described below
commit eba78b6c4a88ca7e4c13b315121e5b710b0a16bc
Author: Stephen O'Donnell <[email protected]>
AuthorDate: Fri Jun 5 23:48:51 2026 +0100
HDDS-15208. OM should learn to finalize from SCM after finalization command
(#10236)
---
.../java/org/apache/hadoop/ozone/OzoneConsts.java | 3 +
.../common/src/main/resources/ozone-default.xml | 6 +
.../ozone/container/common/DatanodeStorage.java | 4 +-
.../hadoop/ozone/audit/AuditLogTestUtils.java | 26 +-
.../schemaupgrade/UpgradeContainerSchema.java | 35 +--
.../datanode/schemaupgrade/UpgradeUtils.java | 26 --
.../org/apache/hadoop/ozone/om/OMConfigKeys.java | 4 +
.../apache/hadoop/fs/ozone/TestHSyncUpgrade.java | 33 +--
.../hadoop/ozone/om/TestOMBucketLayoutUpgrade.java | 19 +-
.../hadoop/ozone/om/TestOMRatisSnapshots.java | 2 +-
.../hadoop/ozone/om/TestOMUpgradeFinalization.java | 31 ++-
.../om/multitenant/TestMultiTenantVolume.java | 36 +--
.../hadoop/ozone/om/snapshot/TestOmSnapshot.java | 26 +-
.../src/test/resources/auditlog.properties | 30 ++-
.../org/apache/hadoop/ozone/om/OzoneManager.java | 16 ++
.../request/upgrade/OMFinalizeUpgradeRequest.java | 6 +-
.../upgrade/OMFinalizeUpgradeResponse.java | 3 +
.../ozone/om/upgrade/OMUpgradeFinalizeService.java | 145 +++++++++++
.../upgrade/TestOMFinalizeUpgradeRequest.java | 84 +++++++
.../upgrade/TestOMFinalizeUpgradeResponse.java | 80 +++++++
.../ozone/om/response/upgrade/package-info.java | 21 ++
.../om/upgrade/TestOMUpgradeFinalizeService.java | 265 +++++++++++++++++++++
22 files changed, 763 insertions(+), 138 deletions(-)
diff --git
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
index 5d0b826c17e..1aa5961eb36 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
@@ -439,6 +439,9 @@ public final class OzoneConsts {
// Apparent Version written into Meta Table ONLY during finalization.
// The name "layout version" is kept for backwards compatibility.
public static final String APPARENT_VERSION_KEY = "#LAYOUTVERSION";
+ // Key written into the Meta table when finalization is needed and a
finalization command has been received
+ // to trigger the process
+ public static final String FINALIZATION_IN_PROGRESS_KEY =
"#FINALIZATION_IN_PROGRESS";
// Kerberos constants
public static final String KERBEROS_CONFIG_VALUE = "kerberos";
diff --git a/hadoop-hdds/common/src/main/resources/ozone-default.xml
b/hadoop-hdds/common/src/main/resources/ozone-default.xml
index aeda046dcd7..dc16b29b77a 100644
--- a/hadoop-hdds/common/src/main/resources/ozone-default.xml
+++ b/hadoop-hdds/common/src/main/resources/ozone-default.xml
@@ -5028,4 +5028,10 @@
<tag>OZONE, RATIS, OM</tag>
<description>The maximum number of events that can be pending in OM
Ratis.</description>
</property>
+ <property>
+ <name>ozone.om.upgrade.finalization.check.interval</name>
+ <value>1m</value>
+ <tag>OM</tag>
+ <description>If OM is unfinalized, how frequently it should poll SCM to
trigger finalization</description>
+ </property>
</configuration>
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/DatanodeStorage.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/DatanodeStorage.java
index 53b62afc3e1..fe267783437 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/DatanodeStorage.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/DatanodeStorage.java
@@ -17,12 +17,12 @@
package org.apache.hadoop.ozone.container.common;
-import static
org.apache.hadoop.hdds.upgrade.HDDSLayoutVersionManager.maxLayoutVersion;
import static org.apache.hadoop.ozone.OzoneConsts.DATANODE_LAYOUT_VERSION_DIR;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
+import org.apache.hadoop.hdds.HDDSVersion;
import org.apache.hadoop.hdds.conf.ConfigurationSource;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeType;
@@ -95,7 +95,7 @@ public void setClusterId(String clusterId) throws IOException
{
* layout version is found on disk.
*/
private static int getDefaultApparentVersion(ConfigurationSource conf) {
- int defaultApparentVersion = maxLayoutVersion();
+ int defaultApparentVersion = HDDSVersion.SOFTWARE_VERSION.serialize();
File dnIdFile = new File(HddsServerUtil.getDatanodeIdFilePath(conf));
if (dnIdFile.exists()) {
diff --git
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/ozone/audit/AuditLogTestUtils.java
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/ozone/audit/AuditLogTestUtils.java
index 0b05d4d01f0..3c438f07c8a 100644
---
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/ozone/audit/AuditLogTestUtils.java
+++
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/ozone/audit/AuditLogTestUtils.java
@@ -32,6 +32,7 @@
*/
public final class AuditLogTestUtils {
private static final String AUDITLOG_FILENAME = "audit.log";
+ private static final String SYSTEM_AUDITLOG_FILENAME = "system_audit.log";
private AuditLogTestUtils() {
}
@@ -55,8 +56,26 @@ public static void verifyAuditLog(AuditAction action,
1000, 10000);
}
+ /**
+ * Searches for the given action in the system audit log file.
+ */
+ public static void verifySystemAuditLog(AuditAction action,
+ AuditEventStatus eventStatus) throws InterruptedException,
TimeoutException {
+ waitFor(
+ () -> fileContains(SYSTEM_AUDITLOG_FILENAME, action.getAction(),
eventStatus.getStatus()),
+ 1000, 10000);
+ }
+
public static boolean auditLogContains(String... strings) {
- File file = new File(AUDITLOG_FILENAME);
+ return fileContains(AUDITLOG_FILENAME, strings);
+ }
+
+ public static boolean systemAuditLogContains(String... strings) {
+ return fileContains(SYSTEM_AUDITLOG_FILENAME, strings);
+ }
+
+ private static boolean fileContains(String filename, String... strings) {
+ File file = new File(filename);
try {
String contents = FileUtils.readFileToString(file, UTF_8);
for (String s : strings) {
@@ -72,9 +91,14 @@ public static boolean auditLogContains(String... strings) {
public static void truncateAuditLogFile() throws IOException {
Files.write(Paths.get(AUDITLOG_FILENAME), new byte[0]);
+ Files.write(Paths.get(SYSTEM_AUDITLOG_FILENAME), new byte[0]);
}
public static void deleteAuditLogFile() {
FileUtils.deleteQuietly(new File(AUDITLOG_FILENAME));
}
+
+ public static void deleteSystemAuditLogFile() {
+ FileUtils.deleteQuietly(new File(SYSTEM_AUDITLOG_FILENAME));
+ }
}
diff --git
a/hadoop-ozone/cli-repair/src/main/java/org/apache/hadoop/ozone/repair/datanode/schemaupgrade/UpgradeContainerSchema.java
b/hadoop-ozone/cli-repair/src/main/java/org/apache/hadoop/ozone/repair/datanode/schemaupgrade/UpgradeContainerSchema.java
index 7add67421fa..ffd9dfb8fa4 100644
---
a/hadoop-ozone/cli-repair/src/main/java/org/apache/hadoop/ozone/repair/datanode/schemaupgrade/UpgradeContainerSchema.java
+++
b/hadoop-ozone/cli-repair/src/main/java/org/apache/hadoop/ozone/repair/datanode/schemaupgrade/UpgradeContainerSchema.java
@@ -34,7 +34,7 @@
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang3.tuple.Pair;
+import org.apache.hadoop.hdds.HDDSVersion;
import org.apache.hadoop.hdds.StringUtils;
import org.apache.hadoop.hdds.cli.HddsVersionProvider;
import org.apache.hadoop.hdds.conf.ConfigurationSource;
@@ -50,6 +50,7 @@
import org.apache.hadoop.io.nativeio.NativeIO;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.common.Storage;
+import org.apache.hadoop.ozone.container.common.DatanodeStorage;
import org.apache.hadoop.ozone.container.common.helpers.ContainerUtils;
import org.apache.hadoop.ozone.container.common.impl.ContainerData;
import org.apache.hadoop.ozone.container.common.impl.ContainerDataYaml;
@@ -63,6 +64,7 @@
import
org.apache.hadoop.ozone.container.metadata.DatanodeSchemaThreeDBDefinition;
import org.apache.hadoop.ozone.container.metadata.DatanodeStore;
import org.apache.hadoop.ozone.container.metadata.DatanodeStoreSchemaThreeImpl;
+import org.apache.hadoop.ozone.container.upgrade.DatanodeVersionManager;
import org.apache.hadoop.ozone.repair.RepairTool;
import org.apache.hadoop.util.Time;
import picocli.CommandLine;
@@ -131,22 +133,21 @@ public void execute() throws Exception {
DatanodeDetails dnDetail =
UpgradeUtils.getDatanodeDetails(configuration);
- Pair<HDDSLayoutFeature, HDDSLayoutFeature> layoutFeature =
- UpgradeUtils.getLayoutFeature(dnDetail, configuration);
- final HDDSLayoutFeature softwareLayoutFeature = layoutFeature.getLeft();
- final HDDSLayoutFeature metadataLayoutFeature = layoutFeature.getRight();
- final int needLayoutVersion =
- HDDSLayoutFeature.DATANODE_SCHEMA_V3.layoutVersion();
-
- if (metadataLayoutFeature.layoutVersion() < needLayoutVersion ||
- softwareLayoutFeature.layoutVersion() < needLayoutVersion) {
- fatal(
- "Please upgrade your software version, no less than %s," +
- " current metadata layout version is %s," +
- " software layout version is %s",
- HDDSLayoutFeature.DATANODE_SCHEMA_V3.toString(),
- metadataLayoutFeature.toString(), softwareLayoutFeature.toString());
- return;
+ DatanodeStorage storage = new DatanodeStorage(configuration,
dnDetail.getUuidString());
+ try (DatanodeVersionManager versionManager = new
DatanodeVersionManager(storage, null)) {
+ // Ensure repair tool is not run in a newer version that supports schema
V3 while the datanode that will read the
+ // containers does not.
+ if
(!HDDSLayoutFeature.DATANODE_SCHEMA_V3.isSupportedBy(HDDSVersion.SOFTWARE_VERSION))
{
+ fatal("Please upgrade your software version to at least %s, current
software version is %s",
+ HDDSLayoutFeature.DATANODE_SCHEMA_V3,
HDDSVersion.SOFTWARE_VERSION);
+ return;
+ }
+
+
+ if (!versionManager.isAllowed(HDDSLayoutFeature.DATANODE_SCHEMA_V3)) {
+ fatal("Please finalize the cluster to enable support for Datanode
container schema V3");
+ return;
+ }
}
if (!Strings.isNullOrEmpty(volume)) {
diff --git
a/hadoop-ozone/cli-repair/src/main/java/org/apache/hadoop/ozone/repair/datanode/schemaupgrade/UpgradeUtils.java
b/hadoop-ozone/cli-repair/src/main/java/org/apache/hadoop/ozone/repair/datanode/schemaupgrade/UpgradeUtils.java
index 164f91b6a32..5c559c52f28 100644
---
a/hadoop-ozone/cli-repair/src/main/java/org/apache/hadoop/ozone/repair/datanode/schemaupgrade/UpgradeUtils.java
+++
b/hadoop-ozone/cli-repair/src/main/java/org/apache/hadoop/ozone/repair/datanode/schemaupgrade/UpgradeUtils.java
@@ -29,13 +29,9 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
-import org.apache.commons.lang3.tuple.Pair;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.DatanodeDetails;
-import org.apache.hadoop.hdds.upgrade.HDDSLayoutFeature;
-import org.apache.hadoop.hdds.upgrade.HDDSLayoutVersionManager;
import org.apache.hadoop.hdds.utils.HddsServerUtil;
-import org.apache.hadoop.ozone.container.common.DatanodeStorage;
import org.apache.hadoop.ozone.container.common.helpers.ContainerUtils;
import org.apache.hadoop.ozone.container.common.utils.StorageVolumeUtil;
import org.apache.hadoop.ozone.container.common.volume.HddsVolume;
@@ -93,28 +89,6 @@ public static boolean createFile(File file) throws
IOException {
return file.exists();
}
- public static Pair<HDDSLayoutFeature, HDDSLayoutFeature> getLayoutFeature(
- DatanodeDetails dnDetail, OzoneConfiguration conf) throws IOException {
- DatanodeStorage layoutStorage =
- new DatanodeStorage(conf, dnDetail.getUuidString());
- HDDSLayoutVersionManager layoutVersionManager =
- new HDDSLayoutVersionManager(layoutStorage.getApparentVersion(), null,
null);
-
- final int metadataLayoutVersion =
- layoutVersionManager.getMetadataLayoutVersion();
- final HDDSLayoutFeature metadataLayoutFeature =
- (HDDSLayoutFeature) layoutVersionManager.getFeature(
- metadataLayoutVersion);
-
- final int softwareLayoutVersion =
- layoutVersionManager.getSoftwareLayoutVersion();
- final HDDSLayoutFeature softwareLayoutFeature =
- (HDDSLayoutFeature) layoutVersionManager.getFeature(
- softwareLayoutVersion);
-
- return Pair.of(softwareLayoutFeature, metadataLayoutFeature);
- }
-
public static List<HddsVolume> getAllVolume(DatanodeDetails detail,
OzoneConfiguration configuration) throws IOException {
final MutableVolumeSet dataVolumeSet = getHddsVolumes(configuration,
StorageVolume.VolumeType.DATA_VOLUME,
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
index b9ccfedb40d..a9ec011ac07 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
@@ -698,6 +698,10 @@ public final class OMConfigKeys {
"ozone.om.ratis.events.max.limit";
public static final int OZONE_OM_RATIS_EVENTS_MAX_LIMIT_DEFAULT = 100;
+ public static final String OZONE_OM_UPGRADE_FINALIZATION_CHECK_INTERVAL =
+ "ozone.om.upgrade.finalization.check.interval";
+ public static final String
OZONE_OM_UPGRADE_FINALIZATION_CHECK_INTERVAL_DEFAULT = "1m";
+
/**
* Never constructed.
*/
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/fs/ozone/TestHSyncUpgrade.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/fs/ozone/TestHSyncUpgrade.java
index 22d8f60920f..ff5680a9fd3 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/fs/ozone/TestHSyncUpgrade.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/fs/ozone/TestHSyncUpgrade.java
@@ -29,16 +29,11 @@
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_OPEN_KEY_CLEANUP_SERVICE_INTERVAL;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_OPEN_KEY_EXPIRE_THRESHOLD;
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.NOT_SUPPORTED_OPERATION_PRIOR_FINALIZATION;
-import static org.apache.hadoop.ozone.upgrade.UpgradeFinalization.isDone;
-import static org.apache.hadoop.ozone.upgrade.UpgradeFinalization.isStarting;
-import static org.apache.ozone.test.LambdaTestUtils.await;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
-import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
import org.apache.hadoop.fs.FSDataOutputStream;
@@ -53,19 +48,21 @@
import org.apache.hadoop.ozone.ClientConfigForTesting;
import org.apache.hadoop.ozone.MiniOzoneCluster;
import org.apache.hadoop.ozone.OzoneConfigKeys;
+import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.TestDataUtil;
import org.apache.hadoop.ozone.client.OzoneBucket;
import org.apache.hadoop.ozone.client.OzoneClient;
import org.apache.hadoop.ozone.container.keyvalue.KeyValueHandler;
import org.apache.hadoop.ozone.container.keyvalue.impl.BlockManagerImpl;
import org.apache.hadoop.ozone.container.metadata.AbstractDatanodeStore;
+import org.apache.hadoop.ozone.om.OMConfigKeys;
import org.apache.hadoop.ozone.om.OMStorage;
+import org.apache.hadoop.ozone.om.OMUpgradeTestUtils;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
import org.apache.hadoop.ozone.om.service.OpenKeyCleanupService;
import org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature;
-import org.apache.hadoop.ozone.upgrade.UpgradeFinalization;
import org.apache.ozone.test.GenericTestUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -93,9 +90,6 @@ public class TestHSyncUpgrade {
private static final int SERVICE_INTERVAL = 100;
private static final int EXPIRE_THRESHOLD_MS = 140;
- private static final int POLL_INTERVAL_MILLIS = 500;
- private static final int POLL_MAX_WAIT_MILLIS = 120_000;
-
@BeforeEach
public void init() throws Exception {
final BucketLayout layout = BUCKET_LAYOUT;
@@ -118,6 +112,7 @@ public void init() throws Exception {
EXPIRE_THRESHOLD_MS, TimeUnit.MILLISECONDS);
conf.set(OzoneConfigKeys.OZONE_OM_LEASE_SOFT_LIMIT, "0s");
conf.setInt(OMStorage.TESTING_INIT_APPARENT_VERSION_KEY,
OMLayoutFeature.MULTITENANCY_SCHEMA.layoutVersion());
+ conf.set(OMConfigKeys.OZONE_OM_UPGRADE_FINALIZATION_CHECK_INTERVAL,
"10ms");
ClientConfigForTesting.newBuilder(StorageUnit.BYTES)
.setBlockSize(BLOCK_SIZE)
@@ -217,20 +212,12 @@ private void finalizeOMUpgrade() throws Exception {
// Trigger OM upgrade finalization. Ref: FinalizeUpgradeSubCommand#call
final OzoneManagerProtocol omClient = client.getObjectStore()
.getClientProxy().getOzoneManagerClient();
- final String upgradeClientID = "Test-Upgrade-Client-" + UUID.randomUUID();
- UpgradeFinalization.StatusAndMessages finalizationResponse =
- omClient.finalizeUpgrade(upgradeClientID);
-
- // The status should transition as soon as the client call above returns
- assertTrue(isStarting(finalizationResponse.status()));
- // Wait for the finalization to be marked as done.
- // 10s timeout should be plenty.
- await(POLL_MAX_WAIT_MILLIS, POLL_INTERVAL_MILLIS, () -> {
- final UpgradeFinalization.StatusAndMessages progress =
- omClient.queryUpgradeFinalizationProgress(
- upgradeClientID, false, false);
- return isDone(progress.status());
- });
+ // TODO - OZONE_FINAL_COMMAND - change to sending command when it is
ready. This will trigger OM finalization
+ cluster.getOzoneManager().getMetadataManager().getMetaTable()
+ .addCacheEntry(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY, "ignore", 1);
+ cluster.getOzoneManager().getMetadataManager().getMetaTable()
+ .put(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY, "ignore");
+ OMUpgradeTestUtils.waitForFinalization(omClient);
}
}
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMBucketLayoutUpgrade.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMBucketLayoutUpgrade.java
index 3f4120a4842..3ec9a492acc 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMBucketLayoutUpgrade.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMBucketLayoutUpgrade.java
@@ -33,8 +33,11 @@
import org.apache.hadoop.hdds.ComponentVersion;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.utils.IOUtils;
+import org.apache.hadoop.hdds.utils.db.CodecException;
+import org.apache.hadoop.hdds.utils.db.RocksDatabaseException;
import org.apache.hadoop.ozone.MiniOzoneCluster;
import org.apache.hadoop.ozone.MiniOzoneHAClusterImpl;
+import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.OzoneManagerVersion;
import org.apache.hadoop.ozone.client.OzoneClient;
import org.apache.hadoop.ozone.om.exceptions.OMException;
@@ -42,7 +45,6 @@
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
-import org.apache.hadoop.ozone.upgrade.UpgradeFinalization;
import org.apache.ozone.test.LambdaTestUtils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
@@ -89,6 +91,7 @@ class TestOMBucketLayoutUpgrade {
void setup() throws Exception {
OzoneConfiguration conf = new OzoneConfiguration();
conf.setInt(OMStorage.TESTING_INIT_APPARENT_VERSION_KEY,
fromVersion.serialize());
+ conf.set(OMConfigKeys.OZONE_OM_UPGRADE_FINALIZATION_CHECK_INTERVAL,
"10ms");
String omServiceId = UUID.randomUUID().toString();
MiniOzoneHAClusterImpl.Builder builder =
MiniOzoneCluster.newHABuilder(conf);
builder.setOMServiceId(omServiceId)
@@ -152,10 +155,16 @@ void allowsLegacyBucketBeforeUpgrade() throws Exception {
@Test
@Order(DURING_UPGRADE)
void finalizeUpgrade() throws Exception {
- UpgradeFinalization.StatusAndMessages response =
- omClient.finalizeUpgrade("finalize-test");
- System.out.println("Finalization Messages : " + response.msgs());
-
+ // TODO - OZONE_FINAL_COMMAND - change to sending command when it is
ready. This will trigger OM finalization
+ cluster.getOzoneManagersList().forEach(om -> {
+ try {
+
om.getMetadataManager().getMetaTable().addCacheEntry(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY,
"ignore", 1);
+ om.getMetadataManager().getMetaTable()
+ .put(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY, "ignore");
+ } catch (RocksDatabaseException | CodecException e) {
+ throw new RuntimeException(e);
+ }
+ });
waitForFinalization(omClient);
final String expectedVersion =
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMRatisSnapshots.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMRatisSnapshots.java
index de2bc98f10c..cdb40ed36d9 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMRatisSnapshots.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMRatisSnapshots.java
@@ -297,7 +297,7 @@ public void testInstallSnapshot(@TempDir Path tempDir)
throws Exception {
String toMatch = String.format(
"op=DB_CHECKPOINT_INSTALL
{\"leaderId\":\"%s\",\"term\":\"%d\",\"lastAppliedIndex\":\"%d\"}",
leaderOMNodeId, leaderOMSnapshotTermIndex, followerOMLastAppliedIndex);
- assertTrue(AuditLogTestUtils.auditLogContains(toMatch));
+ assertTrue(AuditLogTestUtils.systemAuditLogContains(toMatch));
// Read & Write after snapshot installed.
List<String> newKeys = writeKeys(1);
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMUpgradeFinalization.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMUpgradeFinalization.java
index 476ae16af05..03f1786415c 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMUpgradeFinalization.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMUpgradeFinalization.java
@@ -25,14 +25,18 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.utils.db.CodecException;
+import org.apache.hadoop.hdds.utils.db.RocksDatabaseException;
import org.apache.hadoop.ozone.MiniOzoneCluster;
import org.apache.hadoop.ozone.MiniOzoneHAClusterImpl;
+import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.OzoneManagerVersion;
import org.apache.hadoop.ozone.audit.AuditEventStatus;
import org.apache.hadoop.ozone.audit.AuditLogTestUtils;
@@ -40,10 +44,8 @@
import org.apache.hadoop.ozone.client.OzoneClient;
import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
import org.apache.hadoop.ozone.om.ratis.OzoneManagerStateMachine;
-import org.apache.hadoop.ozone.upgrade.UpgradeFinalization.StatusAndMessages;
import org.apache.ratis.util.LifeCycle;
import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -64,11 +66,14 @@ public void setup() throws Exception {
@AfterAll
public static void shutdown() {
AuditLogTestUtils.deleteAuditLogFile();
+ AuditLogTestUtils.deleteSystemAuditLogFile();
}
@Test
void testOMUpgradeFinalizationWithOneOMDown() throws Exception {
OzoneConfiguration conf = new OzoneConfiguration();
+ conf.setInt(OMStorage.TESTING_INIT_APPARENT_VERSION_KEY,
INITIAL_VERSION.layoutVersion());
+ conf.set(OMConfigKeys.OZONE_OM_UPGRADE_FINALIZATION_CHECK_INTERVAL,
"10ms");
try (MiniOzoneHAClusterImpl cluster = newCluster(conf)) {
cluster.waitForClusterToBeReady();
@@ -77,7 +82,7 @@ void testOMUpgradeFinalizationWithOneOMDown() throws
Exception {
for (OzoneManager om : runningOms) {
assertEquals(INITIAL_VERSION,
om.getVersionManager().getApparentVersion());
// The OMs have not been finalized yet, so no version has been
written to the DB.
-
Assertions.assertNull(om.getMetadataManager().getMetaTable().get(APPARENT_VERSION_KEY));
+
assertNull(om.getMetadataManager().getMetaTable().get(APPARENT_VERSION_KEY));
}
final int shutdownOMIndex = 2;
@@ -93,15 +98,25 @@ void testOMUpgradeFinalizationWithOneOMDown() throws
Exception {
long prepareIndex = omClient.prepareOzoneManager(120L, 5L);
assertClusterPrepared(prepareIndex, runningOms);
AuditLogTestUtils.verifyAuditLog(OMAction.UPGRADE_PREPARE,
AuditEventStatus.SUCCESS);
-
omClient.cancelOzoneManagerPrepare();
AuditLogTestUtils.verifyAuditLog(OMAction.UPGRADE_CANCEL,
AuditEventStatus.SUCCESS);
- StatusAndMessages response =
- omClient.finalizeUpgrade("finalize-test");
- System.out.println("Finalization Messages : " + response.msgs());
- AuditLogTestUtils.verifyAuditLog(OMAction.UPGRADE_FINALIZE,
AuditEventStatus.SUCCESS);
+ // TODO - OZONE_FINAL_COMMAND - change to sending command when it is
ready. This will trigger OM finalization
+ cluster.getOzoneManagersList().forEach(om -> {
+ try {
+ om.getMetadataManager().getMetaTable()
+ .addCacheEntry(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY,
"ignore", 1);
+ om.getMetadataManager().getMetaTable()
+ .put(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY, "ignore");
+ } catch (RocksDatabaseException | CodecException e) {
+ throw new RuntimeException(e);
+ }
+ });
waitForFinalization(omClient);
+ AuditLogTestUtils.verifySystemAuditLog(OMAction.UPGRADE_FINALIZE,
AuditEventStatus.SUCCESS);
+ // Ensure the finalization in progress key has been removed.
+ assertNull(cluster.getOzoneManager().getMetadataManager()
+ .getMetaTable().get(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY));
cluster.restartOzoneManager(downedOM, true);
OzoneManagerStateMachine omStateMachine = downedOM.getOmRatisServer()
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java
index f1102777327..4fdd713a77d 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java
@@ -18,13 +18,9 @@
package org.apache.hadoop.ozone.om.multitenant;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_MULTITENANCY_ENABLED;
-import static org.apache.hadoop.ozone.upgrade.UpgradeFinalization.isDone;
-import static org.apache.hadoop.ozone.upgrade.UpgradeFinalization.isStarting;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
import com.google.protobuf.ServiceException;
import java.io.IOException;
@@ -41,15 +37,15 @@
import org.apache.hadoop.ozone.client.OzoneClient;
import org.apache.hadoop.ozone.client.OzoneVolume;
import org.apache.hadoop.ozone.client.rpc.RpcClient;
+import org.apache.hadoop.ozone.om.OMConfigKeys;
import org.apache.hadoop.ozone.om.OMMultiTenantManagerImpl;
import org.apache.hadoop.ozone.om.OMStorage;
+import org.apache.hadoop.ozone.om.OMUpgradeTestUtils;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
import org.apache.hadoop.ozone.om.protocol.S3Auth;
import org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature;
-import org.apache.hadoop.ozone.upgrade.UpgradeFinalization;
-import org.apache.ozone.test.GenericTestUtils;
import org.apache.ozone.test.LambdaTestUtils.VoidCallable;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
@@ -77,6 +73,7 @@ public static void initClusterProvider() throws Exception {
OMMultiTenantManagerImpl.OZONE_OM_TENANT_DEV_SKIP_RANGER, true);
conf.setBoolean(OZONE_OM_MULTITENANCY_ENABLED, true);
conf.setInt(OMStorage.TESTING_INIT_APPARENT_VERSION_KEY,
OMLayoutFeature.INITIAL_VERSION.layoutVersion());
+ conf.set(OMConfigKeys.OZONE_OM_UPGRADE_FINALIZATION_CHECK_INTERVAL,
"10ms");
MiniOzoneCluster.Builder builder = MiniOzoneCluster.newBuilder(conf)
.withoutDatanodes();
cluster = builder.build();
@@ -146,27 +143,12 @@ private static void finalizeOMUpgrade()
// Trigger OM upgrade finalization. Ref: FinalizeUpgradeSubCommand#call
final OzoneManagerProtocol omClient = client.getObjectStore()
.getClientProxy().getOzoneManagerClient();
- final String upgradeClientID = "Test-Upgrade-Client-" + UUID.randomUUID();
- UpgradeFinalization.StatusAndMessages finalizationResponse =
- omClient.finalizeUpgrade(upgradeClientID);
-
- // The status should transition as soon as the client call above returns
- assertTrue(isStarting(finalizationResponse.status()));
-
- // Wait for the finalization to be marked as done.
- // 10s timeout should be plenty.
- GenericTestUtils.waitFor(() -> {
- try {
- final UpgradeFinalization.StatusAndMessages progress =
- omClient.queryUpgradeFinalizationProgress(
- upgradeClientID, false, false);
- return isDone(progress.status());
- } catch (IOException e) {
- fail("Unexpected exception while waiting for "
- + "the OM upgrade to finalize: " + e.getMessage());
- }
- return false;
- }, 500, 10000);
+ // TODO - OZONE_FINAL_COMMAND - change to sending command when it is
ready. This will trigger OM finalization
+ cluster.getOzoneManager().getMetadataManager().getMetaTable()
+ .addCacheEntry(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY, "ignore", 1);
+ cluster.getOzoneManager().getMetadataManager().getMetaTable()
+ .put(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY, "ignore");
+ OMUpgradeTestUtils.waitForFinalization(omClient);
}
@Test
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestOmSnapshot.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestOmSnapshot.java
index becf7fb9e5a..7a97d2c28c2 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestOmSnapshot.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestOmSnapshot.java
@@ -50,8 +50,6 @@
import static
org.apache.hadoop.ozone.snapshot.SnapshotDiffResponse.JobStatus.CANCELLED;
import static
org.apache.hadoop.ozone.snapshot.SnapshotDiffResponse.JobStatus.DONE;
import static
org.apache.hadoop.ozone.snapshot.SnapshotDiffResponse.JobStatus.IN_PROGRESS;
-import static org.apache.hadoop.ozone.upgrade.UpgradeFinalization.isDone;
-import static org.apache.hadoop.ozone.upgrade.UpgradeFinalization.isStarting;
import static
org.apache.ozone.rocksdiff.RocksDBCheckpointDiffer.COLUMN_FAMILIES_TO_TRACK_IN_DAG;
import static org.apache.ozone.test.LambdaTestUtils.await;
import static org.assertj.core.api.Assertions.assertThat;
@@ -134,6 +132,7 @@
import org.apache.hadoop.ozone.om.KeyManagerImpl;
import org.apache.hadoop.ozone.om.OMConfigKeys;
import org.apache.hadoop.ozone.om.OMStorage;
+import org.apache.hadoop.ozone.om.OMUpgradeTestUtils;
import org.apache.hadoop.ozone.om.OmSnapshot;
import org.apache.hadoop.ozone.om.OmSnapshotManager;
import org.apache.hadoop.ozone.om.OzoneManager;
@@ -153,7 +152,6 @@
import org.apache.hadoop.ozone.snapshot.CancelSnapshotDiffResponse;
import org.apache.hadoop.ozone.snapshot.SnapshotDiffReportOzone;
import org.apache.hadoop.ozone.snapshot.SnapshotDiffResponse;
-import org.apache.hadoop.ozone.upgrade.UpgradeFinalization;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.ozone.compaction.log.CompactionLogEntry;
@@ -234,6 +232,7 @@ private void init() throws Exception {
// Enable filesystem snapshot feature for the test regardless of the
default
conf.setBoolean(OMConfigKeys.OZONE_FILESYSTEM_SNAPSHOT_ENABLED_KEY, true);
conf.setInt(OMStorage.TESTING_INIT_APPARENT_VERSION_KEY,
OMLayoutFeature.BUCKET_LAYOUT_SUPPORT.layoutVersion());
+ conf.set(OMConfigKeys.OZONE_OM_UPGRADE_FINALIZATION_CHECK_INTERVAL,
"10ms");
conf.setTimeDuration(OZONE_SNAPSHOT_DELETING_SERVICE_INTERVAL, 1,
TimeUnit.SECONDS);
conf.setInt(OZONE_SNAPSHOT_SST_FILTERING_SERVICE_INTERVAL, -1);
conf.setTimeDuration(OZONE_OM_SNAPSHOT_CACHE_CLEANUP_SERVICE_RUN_INTERVAL,
100, TimeUnit.MILLISECONDS);
@@ -324,23 +323,14 @@ private static void
assertFinalizationException(OMException omException) {
* (status FINALIZATION_DONE).
*/
private void finalizeOMUpgrade() throws Exception {
- // Trigger OM upgrade finalization. Ref: FinalizeUpgradeSubCommand#call
final OzoneManagerProtocol omClient = client.getObjectStore()
.getClientProxy().getOzoneManagerClient();
- final String upgradeClientID = "Test-Upgrade-Client-" + UUID.randomUUID();
- UpgradeFinalization.StatusAndMessages finalizationResponse =
- omClient.finalizeUpgrade(upgradeClientID);
-
- // The status should transition as soon as the client call above returns
- assertTrue(isStarting(finalizationResponse.status()));
- // Wait for the finalization to be marked as done.
- // 10s timeout should be plenty.
- await(POLL_MAX_WAIT_MILLIS, POLL_INTERVAL_MILLIS, () -> {
- final UpgradeFinalization.StatusAndMessages progress =
- omClient.queryUpgradeFinalizationProgress(
- upgradeClientID, false, false);
- return isDone(progress.status());
- });
+ // TODO - OZONE_FINAL_COMMAND - change to sending command when it is
ready. This will trigger OM finalization
+ cluster.getOzoneManager().getMetadataManager().getMetaTable()
+ .addCacheEntry(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY, "ignore", 1);
+ cluster.getOzoneManager().getMetadataManager().getMetaTable()
+ .put(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY, "ignore");
+ OMUpgradeTestUtils.waitForFinalization(omClient);
}
@AfterAll
diff --git
a/hadoop-ozone/integration-test/src/test/resources/auditlog.properties
b/hadoop-ozone/integration-test/src/test/resources/auditlog.properties
index c5e9f8d2c7d..d7051b6cfda 100644
--- a/hadoop-ozone/integration-test/src/test/resources/auditlog.properties
+++ b/hadoop-ozone/integration-test/src/test/resources/auditlog.properties
@@ -52,7 +52,7 @@ filter.write.onMismatch = NEUTRAL
# TRACE (least specific, a lot of data)
# ALL (least specific, all data)
-appenders = console, audit
+appenders = console, audit, systemaudit
appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
@@ -64,15 +64,27 @@ appender.audit.fileName=audit.log
appender.audit.layout.type=PatternLayout
appender.audit.layout.pattern= %d{DEFAULT} | %-5level | %c{1} | %msg |
%throwable{3} %n
-loggers=audit,omSystemAudit
+loggers=audit,systemaudit
+
logger.audit.name=OMAudit
-logger.audit.level = INFO
-logger.audit.appenderRefs = audit
-logger.audit.appenderRef.file.ref = AUDITLOG
-logger.omSystemAudit.name=OMSystemAudit
-logger.omSystemAudit.level = INFO
-logger.omSystemAudit.appenderRefs = audit
-logger.omSystemAudit.appenderRef.file.ref = AUDITLOG
+logger.audit.level=INFO
+logger.audit.additivity=false
+logger.audit.appenderRefs=audit,console
+logger.audit.appenderRef.audit.ref=AUDITLOG
+logger.audit.appenderRef.console.ref=STDOUT
+
+appender.systemaudit.type = File
+appender.systemaudit.name = SYSTEMAUDITLOG
+appender.systemaudit.fileName=system_audit.log
+appender.systemaudit.layout.type=PatternLayout
+appender.systemaudit.layout.pattern= %d{DEFAULT} | %-5level | %c{1} | %msg |
%throwable{3} %n
+
+logger.systemaudit.name=OMSystemAudit
+logger.systemaudit.level=INFO
+logger.systemaudit.additivity=false
+logger.systemaudit.appenderRefs=systemaudit,console
+logger.systemaudit.appenderRef.systemaudit.ref=SYSTEMAUDITLOG
+logger.systemaudit.appenderRef.console.ref=STDOUT
rootLogger.level = INFO
rootLogger.appenderRefs = stdout
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
index f89dae3b3c0..b17eed0bbe3 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
@@ -300,6 +300,7 @@
import org.apache.hadoop.ozone.om.service.QuotaRepairTask;
import org.apache.hadoop.ozone.om.snapshot.defrag.SnapshotDefragService;
import org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature;
+import org.apache.hadoop.ozone.om.upgrade.OMUpgradeFinalizeService;
import org.apache.hadoop.ozone.om.upgrade.OMVersionManager;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerAdminProtocolProtos.OzoneManagerAdminService;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
@@ -463,6 +464,7 @@ public final class OzoneManager extends
ServiceRuntimeInfoImpl
private KeyProviderCryptoExtension kmsProvider;
private final OMVersionManager versionManager;
+ private OMUpgradeFinalizeService omUpgradeFinalizeService;
private final ReplicationConfigValidator replicationConfigValidator;
@@ -639,6 +641,12 @@ private OzoneManager(OzoneConfiguration conf,
StartupOption startupOption)
this.ozoneLockProvider = new OzoneLockProvider(getKeyPathLockEnabled(),
getEnableFileSystemPaths());
+ if (versionManager.needsFinalization()) {
+ long intervalMs =
conf.getTimeDuration(OMConfigKeys.OZONE_OM_UPGRADE_FINALIZATION_CHECK_INTERVAL,
+ OMConfigKeys.OZONE_OM_UPGRADE_FINALIZATION_CHECK_INTERVAL_DEFAULT,
TimeUnit.MILLISECONDS);
+ omUpgradeFinalizeService = new OMUpgradeFinalizeService(this,
versionManager, getScmClient(), intervalMs);
+ }
+
// For testing purpose only, not hit scm from om as Hadoop UGI can't login
// two principals in the same JVM.
ScmInfo scmInfo;
@@ -1900,6 +1908,10 @@ public void start() throws IOException {
bootstrap(omNodeDetails);
}
+ if (omUpgradeFinalizeService != null) {
+ omUpgradeFinalizeService.start();
+ }
+
omState = State.RUNNING;
auditMap.put("NewOmState", omState.name());
SYSTEMAUDIT.logWriteSuccess(buildAuditMessageForSuccess(OMSystemAction.STARTUP,
auditMap));
@@ -2441,6 +2453,10 @@ public boolean stop() {
bucketUtilizationMetrics.unRegister();
}
+ if (omUpgradeFinalizeService != null) {
+ omUpgradeFinalizeService.shutdown();
+ }
+
if (versionManager != null) {
versionManager.close();
}
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMFinalizeUpgradeRequest.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMFinalizeUpgradeRequest.java
index 2bff57e9a8d..0aab6e4cdc3 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMFinalizeUpgradeRequest.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMFinalizeUpgradeRequest.java
@@ -25,6 +25,7 @@
import
org.apache.hadoop.hdds.protocol.proto.HddsProtos.UpgradeFinalizationStatus;
import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
+import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.audit.AuditLogger;
import org.apache.hadoop.ozone.audit.OMAction;
import org.apache.hadoop.ozone.om.OMMetadataManager;
@@ -59,7 +60,7 @@ public OMFinalizeUpgradeRequest(OMRequest omRequest) {
@Override
public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager,
ExecutionContext context) {
LOG.trace("Request: {}", getOmRequest());
- AuditLogger auditLogger = ozoneManager.getAuditLogger();
+ AuditLogger auditLogger = ozoneManager.getSystemAuditLogger();
OzoneManagerProtocolProtos.UserInfo userInfo =
getOmRequest().getUserInfo();
OMResponse.Builder responseBuilder =
OmResponseUtil.getOMResponseBuilder(getOmRequest());
@@ -95,6 +96,9 @@ public OMClientResponse validateAndUpdateCache(OzoneManager
ozoneManager, Execut
omMetadataManager.getMetaTable().addCacheEntry(
new CacheKey<>(APPARENT_VERSION_KEY),
CacheValue.get(context.getIndex(), String.valueOf(apparentVersion)));
+ // Clear the finalization_in_progress key from the cache
+ omMetadataManager.getMetaTable().addCacheEntry(
+ new CacheKey<>(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY),
CacheValue.get(context.getIndex()));
FinalizeUpgradeResponse omResponse =
FinalizeUpgradeResponse.newBuilder()
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMFinalizeUpgradeResponse.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMFinalizeUpgradeResponse.java
index 1d25f9096f4..8dc981ba5ed 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMFinalizeUpgradeResponse.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMFinalizeUpgradeResponse.java
@@ -22,6 +22,7 @@
import java.io.IOException;
import org.apache.hadoop.hdds.utils.db.BatchOperation;
+import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.om.OMMetadataManager;
import org.apache.hadoop.ozone.om.response.CleanupTableInfo;
import org.apache.hadoop.ozone.om.response.OMClientResponse;
@@ -55,5 +56,7 @@ protected void addToDBBatch(OMMetadataManager
omMetadataManager,
APPARENT_VERSION_KEY,
String.valueOf(serializedApparentVersion));
}
+ // Finalization has completed successfully, so the IN_PROGRESS Key should
be removed from the database.
+ omMetadataManager.getMetaTable().deleteWithBatch(batchOperation,
OzoneConsts.FINALIZATION_IN_PROGRESS_KEY);
}
}
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMUpgradeFinalizeService.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMUpgradeFinalizeService.java
new file mode 100644
index 00000000000..93926ba6594
--- /dev/null
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMUpgradeFinalizeService.java
@@ -0,0 +1,145 @@
+/*
+ * 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.om.upgrade;
+
+import static org.apache.hadoop.ozone.OzoneConsts.FINALIZATION_IN_PROGRESS_KEY;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
+import org.apache.hadoop.hdds.utils.BackgroundService;
+import org.apache.hadoop.hdds.utils.BackgroundTask;
+import org.apache.hadoop.hdds.utils.BackgroundTaskQueue;
+import org.apache.hadoop.hdds.utils.BackgroundTaskResult;
+import org.apache.hadoop.ozone.om.OzoneManager;
+import org.apache.hadoop.ozone.om.ScmClient;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerRatisUtils;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
+import org.apache.ratis.protocol.ClientId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A background service that periodically checks whether SCM has completed
finalization of an upgrade and, if so,
+ * finalizes the OM upgrade.
+ */
+public class OMUpgradeFinalizeService extends BackgroundService {
+
+ private static final Logger LOG =
LoggerFactory.getLogger(OMUpgradeFinalizeService.class);
+
+ private static final int THREAD_POOL_SIZE = 1;
+ private static final TimeUnit INTERVAL_UNIT = TimeUnit.MILLISECONDS;
+ private static final long TIMEOUT = 60000;
+ private static final AtomicLong RUN_COUNT = new AtomicLong(0);
+
+ private final OzoneManager ozoneManager;
+ private final OMVersionManager versionManager;
+ private final ScmClient scmClient;
+ private final AtomicBoolean stopInitiated = new AtomicBoolean(false);
+ private final ClientId clientId = ClientId.randomId();
+
+ /**
+ * Creates an {@code OMUpgradeFinalizeService} with a custom check interval.
+ *
+ * @param ozoneManager the OzoneManager instance
+ * @param versionManager the {@link OMVersionManager} to query the
finalization status
+ * @param scmClient the scmClient instance used to query SCM
+ * @param intervalMs the duration to wait between checks
+ */
+ public OMUpgradeFinalizeService(OzoneManager ozoneManager, OMVersionManager
versionManager, ScmClient scmClient,
+ long intervalMs) {
+ super("OMUpgradeFinalizeService", intervalMs, INTERVAL_UNIT,
THREAD_POOL_SIZE, TIMEOUT,
+ ozoneManager.getThreadNamePrefix());
+ this.ozoneManager = ozoneManager;
+ this.versionManager = versionManager;
+ this.scmClient = scmClient;
+ }
+
+ @Override
+ public BackgroundTaskQueue getTasks() {
+ BackgroundTaskQueue queue = new BackgroundTaskQueue();
+ if (!versionManager.needsFinalization()) {
+ // Finalization is done (or was never needed), so this service can now
shutdown. To avoid deadlocking on the
+ // executor.awaitTermination by calling shutdown directly, spawn a
thread to perform the shutdown which will
+ // block until this task / thread completes in the executor.
+ if (stopInitiated.compareAndSet(false, true)) {
+ LOG.info("OMUpgradeFinalizeService: finalization is no longer needed,
shutting down.");
+ Thread stopper = new Thread(this::shutdown,
"OMUpgradeFinalizeService-stopper");
+ stopper.setDaemon(true);
+ stopper.start();
+ }
+ return queue; // empty — PeriodicalTask.run() will return without
scheduling work
+ }
+ if (ozoneManager.isLeaderReady()) {
+ queue.add(new UpgradeStatusCheckTask());
+ }
+ return queue;
+ }
+
+ /**
+ * Periodic task that checks upgrade finalization status and logs the result.
+ */
+ private class UpgradeStatusCheckTask implements BackgroundTask {
+
+ @Override
+ public BackgroundTaskResult call() {
+ final long run = RUN_COUNT.incrementAndGet();
+ if (!ozoneManager.isLeaderReady()) {
+ LOG.debug("OMUpgradeFinalizeService: skipping check — not the leader.
Run count {}", run);
+ return BackgroundTaskResult.EmptyTaskResult.newResult();
+ }
+ if (versionManager.needsFinalization()) {
+ try {
+ // To finalize OM, first finalization needs to have been started.
Then SCM needs to indicate that it has
+ // completed its finalization work. Only once both of those things
have happened can OM finalize.
+ String finalizationInProgress =
+
ozoneManager.getMetadataManager().getMetaTable().get(FINALIZATION_IN_PROGRESS_KEY);
+ if (finalizationInProgress == null) {
+ LOG.debug("OMUpgradeFinalizeService: skipping check — finalization
is not in progress. Run count {}", run);
+ return BackgroundTaskResult.EmptyTaskResult.newResult();
+ }
+
+ HddsProtos.UpgradeStatus upgradeStatus =
scmClient.getContainerClient().queryUpgradeStatus();
+ if (upgradeStatus.getShouldFinalize()) {
+ LOG.info("The SCM Upgrade has been finalized. OM will now
finalize. Run count {}", run);
+
+ OzoneManagerProtocolProtos.OMRequest omRequest =
OzoneManagerProtocolProtos.OMRequest.newBuilder()
+ .setCmdType(OzoneManagerProtocolProtos.Type.FinalizeUpgrade)
+ .setClientId(clientId.toString())
+ .build();
+ OzoneManagerProtocolProtos.OMResponse response =
OzoneManagerRatisUtils.submitRequest(
+ ozoneManager, omRequest, clientId, run);
+ if (!response.getSuccess()) {
+ LOG.error("Failed to send FinalizeUpgradeRequest to over Ratis.
{}. Run count {}",
+ response.getMessage(), run);
+ }
+ } else {
+ LOG.debug("The SCM Upgrade has not been finalized. Run count {}",
run);
+ }
+ } catch (Exception e) {
+ LOG.error("An exception occurred while trying to check the SCM
Upgrade status or finalize OM. Run count {}",
+ run, e);
+ }
+ } else {
+ LOG.debug("Finalization is not in progress. Run count {}", run);
+ }
+ return BackgroundTaskResult.EmptyTaskResult.newResult();
+ }
+ }
+}
diff --git
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/upgrade/TestOMFinalizeUpgradeRequest.java
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/upgrade/TestOMFinalizeUpgradeRequest.java
new file mode 100644
index 00000000000..863ead019ed
--- /dev/null
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/upgrade/TestOMFinalizeUpgradeRequest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.om.request.upgrade;
+
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.Collections;
+import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
+import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
+import org.apache.hadoop.ozone.OzoneConsts;
+import org.apache.hadoop.ozone.OzoneManagerVersion;
+import org.apache.hadoop.ozone.om.execution.flowcontrol.ExecutionContext;
+import org.apache.hadoop.ozone.om.request.key.TestOMKeyRequest;
+import org.apache.hadoop.ozone.om.upgrade.OMVersionManager;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
+import org.apache.hadoop.ozone.upgrade.UpgradeFinalization;
+import org.apache.ratis.protocol.ClientId;
+import org.apache.ratis.server.protocol.TermIndex;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for the OMFinalizeUpgradeRequest class.
+ */
+public class TestOMFinalizeUpgradeRequest extends TestOMKeyRequest {
+
+ @Test
+ public void testFinalizationInProgressKeyRemoved() throws IOException {
+ OMVersionManager omVersionManager = mock(OMVersionManager.class);
+
when(omVersionManager.getApparentVersion()).thenReturn(OzoneManagerVersion.DEFAULT_VERSION);
+ when(ozoneManager.getVersionManager()).thenReturn(omVersionManager);
+ when(ozoneManager.finalizeUpgrade(any())).thenReturn(new
UpgradeFinalization.StatusAndMessages(
+ UpgradeFinalization.Status.FINALIZATION_IN_PROGRESS,
Collections.singletonList("Finalization in progress")));
+
+
omMetadataManager.getMetaTable().put(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY,
"ignored");
+ omMetadataManager.getMetaTable().addCacheEntry(
+ new CacheKey<>(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY),
CacheValue.get(1, "ignored"));
+
+ String progressKey =
omMetadataManager.getMetaTable().get(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY);
+
+ assertNotNull(progressKey);
+ submitRequest();
+
+ progressKey =
omMetadataManager.getMetaTable().get(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY);
+ assertNull(progressKey);
+ }
+
+ private void submitRequest() throws IOException {
+ OzoneManagerProtocolProtos.OMRequest omRequest =
OzoneManagerProtocolProtos.OMRequest.newBuilder()
+ .setCmdType(OzoneManagerProtocolProtos.Type.FinalizeUpgrade)
+ .setClientId(ClientId.randomId().toString())
+ .build();
+
+ OMFinalizeUpgradeRequest request = new OMFinalizeUpgradeRequest(omRequest);
+ ExecutionContext context = ExecutionContext.of(1, TermIndex.INITIAL_VALUE);
+
+ OzoneManagerProtocolProtos.OMRequest modifiedOmRequest =
request.preExecute(ozoneManager);
+
+ // Will not be equal, as UserInfo will be set.
+ assertNotEquals(omRequest, modifiedOmRequest);
+ request.validateAndUpdateCache(ozoneManager, context);
+ }
+
+}
diff --git
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/upgrade/TestOMFinalizeUpgradeResponse.java
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/upgrade/TestOMFinalizeUpgradeResponse.java
new file mode 100644
index 00000000000..15ad3c848b5
--- /dev/null
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/upgrade/TestOMFinalizeUpgradeResponse.java
@@ -0,0 +1,80 @@
+/*
+ * 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.om.response.upgrade;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.utils.db.BatchOperation;
+import org.apache.hadoop.ozone.OzoneConsts;
+import org.apache.hadoop.ozone.om.OMConfigKeys;
+import org.apache.hadoop.ozone.om.OMMetadataManager;
+import org.apache.hadoop.ozone.om.OmMetadataManagerImpl;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+/**
+ * Tests for the OMFinalizeUpgradeResponse class.
+ */
+public class TestOMFinalizeUpgradeResponse {
+
+ @TempDir
+ private Path folder;
+ private OMMetadataManager omMetadataManager;
+ private BatchOperation batchOperation;
+
+ @BeforeEach
+ public void setup() throws Exception {
+ OzoneConfiguration ozoneConfiguration = new OzoneConfiguration();
+ ozoneConfiguration.set(OMConfigKeys.OZONE_OM_DB_DIRS,
+ folder.toAbsolutePath().toString());
+ omMetadataManager = new OmMetadataManagerImpl(ozoneConfiguration, null);
+ batchOperation = omMetadataManager.getStore().initBatchOperation();
+ }
+
+ @Test
+ public void testFinalizationInProgressKeyRemoved() throws IOException {
+ // Add the in progress key which would normally have been added by the
start finalization command
+
omMetadataManager.getMetaTable().put(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY,
"ignored");
+
+ String value =
omMetadataManager.getMetaTable().get(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY);
+ assertEquals("ignored", value);
+
+ OMFinalizeUpgradeResponse finalizeUpgradeResponse = new
OMFinalizeUpgradeResponse(createRequest(), 1);
+ finalizeUpgradeResponse.addToDBBatch(omMetadataManager, batchOperation);
+
+ omMetadataManager.getStore().commitBatchOperation(batchOperation);
+
+ // Ensure the key is removed as expected
+ value =
omMetadataManager.getMetaTable().get(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY);
+ assertNull(value);
+ }
+
+ private OzoneManagerProtocolProtos.OMResponse createRequest() {
+ return OzoneManagerProtocolProtos.OMResponse.newBuilder()
+ .setCmdType(OzoneManagerProtocolProtos.Type.FinalizeUpgrade)
+ .setStatus(OzoneManagerProtocolProtos.Status.OK)
+ .build();
+ }
+
+}
diff --git
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/upgrade/package-info.java
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/upgrade/package-info.java
new file mode 100644
index 00000000000..ef001e338eb
--- /dev/null
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/upgrade/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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 contains test classes for upgrade responses.
+ */
+package org.apache.hadoop.ozone.om.response.upgrade;
diff --git
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOMUpgradeFinalizeService.java
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOMUpgradeFinalizeService.java
new file mode 100644
index 00000000000..a3aa7be8b5f
--- /dev/null
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOMUpgradeFinalizeService.java
@@ -0,0 +1,265 @@
+/*
+ * 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.om.upgrade;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+import com.google.protobuf.ServiceException;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
+import org.apache.hadoop.hdds.scm.protocol.StorageContainerLocationProtocol;
+import org.apache.hadoop.hdds.utils.db.CodecException;
+import org.apache.hadoop.hdds.utils.db.RocksDatabaseException;
+import org.apache.hadoop.hdds.utils.db.TypedTable;
+import org.apache.hadoop.ozone.OzoneConsts;
+import org.apache.hadoop.ozone.OzoneManagerVersion;
+import org.apache.hadoop.ozone.om.OMMetadataManager;
+import org.apache.hadoop.ozone.om.OzoneManager;
+import org.apache.hadoop.ozone.om.ScmClient;
+import org.apache.hadoop.ozone.om.ratis.OzoneManagerRatisServer;
+import org.apache.ratis.protocol.ClientId;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for {@link OMUpgradeFinalizeService}.
+ * Uses {@link
org.apache.hadoop.hdds.utils.BackgroundService#runPeriodicalTaskNow()} to
execute
+ * tasks synchronously on the test thread, avoiding timing-dependent polling.
+ */
+public class TestOMUpgradeFinalizeService {
+
+ // A long interval so the scheduler never fires automatically during tests;
+ // we drive execution manually via runPeriodicalTaskNow().
+ private static final long INTERVAL_MS = 60_000;
+
+ private OzoneManager ozoneManager;
+ private OMVersionManager versionManager;
+ private TypedTable<String, String> metaTable;
+ private ScmClient scmClient;
+ private StorageContainerLocationProtocol containerClient;
+ private OzoneManagerRatisServer omRatisServer;
+ private OMUpgradeFinalizeService service;
+
+ @BeforeEach
+ void setUp() throws RocksDatabaseException, CodecException {
+ ozoneManager = mock(OzoneManager.class);
+ OMMetadataManager metadataManager = mock(OMMetadataManager.class);
+ metaTable = mock(TypedTable.class);
+ when(ozoneManager.getThreadNamePrefix()).thenReturn("");
+ when(ozoneManager.getOMNodeId()).thenReturn("clientId");
+ when(ozoneManager.getMetadataManager()).thenReturn(metadataManager);
+ when(metadataManager.getMetaTable()).thenReturn(metaTable);
+ // For most tests, set the finalization command as having been received
+
when(metaTable.get(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY)).thenReturn("ignored");
+
+ versionManager = mock(OMVersionManager.class);
+ // preExecute() calls
ozoneManager.getVersionManager().getApparentVersion().serialize()
+ when(ozoneManager.getVersionManager()).thenReturn(versionManager);
+
when(versionManager.getApparentVersion()).thenReturn(OzoneManagerVersion.DEFAULT_VERSION);
+
+ omRatisServer = mock(OzoneManagerRatisServer.class);
+ // OzoneManagerRatisUtils.submitRequest() calls
ozoneManager.getOmRatisServer().submitRequest(...)
+ when(ozoneManager.getOmRatisServer()).thenReturn(omRatisServer);
+
+ containerClient = mock(StorageContainerLocationProtocol.class);
+ scmClient = mock(ScmClient.class);
+ when(scmClient.getContainerClient()).thenReturn(containerClient);
+
+ service = new OMUpgradeFinalizeService(ozoneManager, versionManager,
scmClient, INTERVAL_MS);
+ }
+
+ /**
+ * When the OM is not the leader, getTasks() should return an empty queue
+ * and no interaction with the SCM client or Ratis server should occur.
+ */
+ @Test
+ void testNoTasksSubmittedWhenNotLeader() throws Exception {
+ when(ozoneManager.isLeaderReady()).thenReturn(false);
+ when(versionManager.needsFinalization()).thenReturn(true);
+
+ service.runPeriodicalTaskNow();
+
+ verifyNoInteractions(scmClient);
+ verifyNoInteractions(omRatisServer);
+ }
+
+ /**
+ * When finalization is not needed, getTasks() should return an empty queue
+ * and no SCM query or Ratis submission should occur.
+ */
+ @Test
+ void testNoTasksSubmittedWhenFinalizationNotNeeded() throws Exception {
+ when(ozoneManager.isLeaderReady()).thenReturn(true);
+ when(versionManager.needsFinalization()).thenReturn(false);
+
+ service.runPeriodicalTaskNow();
+
+ verifyNoInteractions(scmClient);
+ verifyNoInteractions(omRatisServer);
+ }
+
+ /**
+ * When the OM is the leader, finalization is needed, the finalization
command is given and SCM reports
+ * shouldFinalize=true, a FinalizeUpgrade request should be submitted via
Ratis.
+ */
+ @Test
+ void testFinalizationTriggeredWhenScmIsFinalizedAndFinalizationInProgress()
throws Exception {
+ when(ozoneManager.isLeaderReady()).thenReturn(true);
+ when(versionManager.needsFinalization()).thenReturn(true);
+
+ HddsProtos.UpgradeStatus scmStatus = HddsProtos.UpgradeStatus.newBuilder()
+ .setScmFinalized(true)
+ .setShouldFinalize(true)
+ .setNumDatanodesFinalized(3)
+ .setNumDatanodesTotal(3)
+ .build();
+ when(containerClient.queryUpgradeStatus()).thenReturn(scmStatus);
+ // Finalization command not given yet
+
when(metaTable.get(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY)).thenReturn(null);
+
+ service.runPeriodicalTaskNow();
+
+ verifyNoInteractions(containerClient);
+ verifyNoInteractions(omRatisServer);
+
+
when(metaTable.get(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY)).thenReturn("ignored");
+ service.runPeriodicalTaskNow();
+
+ verify(containerClient).queryUpgradeStatus();
+ // Implementation submits a FinalizeUpgrade request through Ratis
+ verify(omRatisServer).submitRequest(any(), any(ClientId.class), anyLong());
+ }
+
+ /**
+ * When SCM reports shouldFinalize=false (SCM is not yet finalized),
+ * no Ratis request should be submitted.
+ */
+ @Test
+ void testFinalizationSkippedWhenScmNotYetFinalized() throws Exception {
+ when(ozoneManager.isLeaderReady()).thenReturn(true);
+ when(versionManager.needsFinalization()).thenReturn(true);
+
+ HddsProtos.UpgradeStatus scmStatus = HddsProtos.UpgradeStatus.newBuilder()
+ .setScmFinalized(false)
+ .setShouldFinalize(false)
+ .setNumDatanodesFinalized(0)
+ .setNumDatanodesTotal(3)
+ .build();
+ when(containerClient.queryUpgradeStatus()).thenReturn(scmStatus);
+
+ service.runPeriodicalTaskNow();
+
+ verify(containerClient).queryUpgradeStatus();
+ verifyNoInteractions(omRatisServer);
+ }
+
+ /**
+ * When the SCM client throws an IOException, the service should absorb it
+ * and not submit any Ratis request.
+ */
+ @Test
+ void testExceptionFromScmClientIsHandledGracefully() throws Exception {
+ when(ozoneManager.isLeaderReady()).thenReturn(true);
+ when(versionManager.needsFinalization()).thenReturn(true);
+ when(containerClient.queryUpgradeStatus()).thenThrow(new IOException("SCM
unavailable"));
+
+ // The catch block in the task swallows the exception.
+ service.runPeriodicalTaskNow();
+
+ verify(containerClient).queryUpgradeStatus();
+ verifyNoInteractions(omRatisServer);
+ }
+
+ /**
+ * When {@code needsFinalization()} returns {@code false}, {@link
OMUpgradeFinalizeService#getTasks()}
+ * should spawn a stopper thread that calls {@link
OMUpgradeFinalizeService#shutdown()} exactly once,
+ * even when {@code getTasks()} is driven multiple times (guarded by the
internal
+ * {@code stopInitiated} AtomicBoolean). No SCM or Ratis interactions should
occur.
+ * <p>
+ * The service is subclassed to intercept {@code shutdown()} via a {@link
CountDownLatch},
+ * avoiding both actual executor teardown and any need for {@code
Thread.sleep}.
+ */
+ @Test
+ void testShutdownTriggeredExactlyOnceWhenFinalizationNoLongerNeeded() throws
Exception {
+ AtomicInteger shutdownCount = new AtomicInteger(0);
+ CountDownLatch firstShutdown = new CountDownLatch(1);
+
+ OMUpgradeFinalizeService testService = new OMUpgradeFinalizeService(
+ ozoneManager, versionManager, scmClient, INTERVAL_MS) {
+ @Override
+ public synchronized void shutdown() {
+ shutdownCount.incrementAndGet();
+ firstShutdown.countDown();
+ // Don't propagate to super — avoids racing on the test executor.
+ }
+ };
+
+ when(ozoneManager.isLeaderReady()).thenReturn(true);
+ when(versionManager.needsFinalization()).thenReturn(false);
+
+ // Drive getTasks() three times; the stopper thread should only fire once.
+ testService.runPeriodicalTaskNow();
+ testService.runPeriodicalTaskNow();
+ testService.runPeriodicalTaskNow();
+
+ // Wait for the single stopper thread to call shutdown().
+ assertTrue(firstShutdown.await(5, TimeUnit.SECONDS),
+ "shutdown() should have been called by the stopper thread within 5 s");
+ // incrementAndGet() happens before countDown(), so the count is already
final — no sleep needed.
+ assertEquals(1, shutdownCount.get(),
+ "shutdown() must be called exactly once despite multiple getTasks()
invocations");
+ verifyNoInteractions(scmClient);
+ verifyNoInteractions(omRatisServer);
+ }
+
+ /**
+ * When the Ratis submission throws, the service should absorb the error
+ * and not propagate the exception.
+ */
+ @Test
+ void testExceptionFromRatisSubmitIsHandledGracefully() throws Exception {
+ when(ozoneManager.isLeaderReady()).thenReturn(true);
+ when(versionManager.needsFinalization()).thenReturn(true);
+
+ HddsProtos.UpgradeStatus scmStatus = HddsProtos.UpgradeStatus.newBuilder()
+ .setScmFinalized(true)
+ .setShouldFinalize(true)
+ .setNumDatanodesFinalized(3)
+ .setNumDatanodesTotal(3)
+ .build();
+ when(containerClient.queryUpgradeStatus()).thenReturn(scmStatus);
+ when(omRatisServer.submitRequest(any(), any(ClientId.class), anyLong()))
+ .thenThrow(new ServiceException("Ratis unavailable"));
+
+ // The catch block in the task swallows the exception.
+ service.runPeriodicalTaskNow();
+
+ // submitRequest was attempted but threw — service should not propagate
the exception.
+ verify(omRatisServer).submitRequest(any(), any(ClientId.class), anyLong());
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]