From 741d84ba30655ddadb28f085734ec8bf66541136 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Fri, 6 Feb 2026 16:22:43 -0500 Subject: [PATCH 01/22] Initial removal of prepare for upgrade Generated-By: Cursor --- .../admin/om/CancelPrepareSubCommand.java | 3 +- .../ozone/admin/om/PrepareSubCommand.java | 7 +- .../java/org/apache/hadoop/ozone/OmUtils.java | 6 +- .../src/main/compose/ozone-om-prepare/test.sh | 62 -- .../smoketest/omha/om-cancel-prepare.robot | 44 -- .../src/main/smoketest/omha/om-prepare.robot | 51 -- .../src/main/smoketest/omha/om-prepared.robot | 37 -- .../src/main/smoketest/upgrade/prepare.robot | 29 - .../hadoop/ozone/om/OMUpgradeTestUtils.java | 32 -- .../ozone/om/TestOMUpgradeFinalization.java | 27 +- .../ozone/om/TestOzoneManagerPrepare.java | 529 ------------------ .../src/main/proto/OmClientProtocol.proto | 57 +- .../apache/hadoop/ozone/audit/OMAction.java | 2 - .../hadoop/ozone/om/OMStarterInterface.java | 3 - .../apache/hadoop/ozone/om/OzoneManager.java | 107 ---- .../ozone/om/OzoneManagerPrepareState.java | 310 ---------- .../hadoop/ozone/om/OzoneManagerStarter.java | 20 +- .../ozone/om/helpers/OMAuditLogger.java | 2 - .../om/ratis/OzoneManagerRatisServer.java | 25 +- .../om/ratis/OzoneManagerStateMachine.java | 50 -- .../ratis/utils/OzoneManagerRatisUtils.java | 6 - .../upgrade/OMCancelPrepareRequest.java | 102 ---- .../om/request/upgrade/OMPrepareRequest.java | 273 --------- .../upgrade/OMCancelPrepareResponse.java | 46 -- .../response/upgrade/OMPrepareResponse.java | 58 -- .../OzoneManagerRequestHandler.java | 25 +- .../ozone/om/TestOzoneManagerStarter.java | 23 - .../ratis/TestOzoneManagerStateMachine.java | 89 --- .../om/request/key/TestOMKeyRequest.java | 5 - .../upgrade/TestOMCancelPrepareRequest.java | 122 ---- .../upgrade/TestOzoneManagerPrepareState.java | 287 ---------- 31 files changed, 75 insertions(+), 2364 deletions(-) delete mode 100755 hadoop-ozone/dist/src/main/compose/ozone-om-prepare/test.sh delete mode 100644 hadoop-ozone/dist/src/main/smoketest/omha/om-cancel-prepare.robot delete mode 100644 hadoop-ozone/dist/src/main/smoketest/omha/om-prepare.robot delete mode 100644 hadoop-ozone/dist/src/main/smoketest/omha/om-prepared.robot delete mode 100644 hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot delete mode 100644 hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerPrepare.java delete mode 100644 hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerPrepareState.java delete mode 100644 hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMCancelPrepareRequest.java delete mode 100644 hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMPrepareRequest.java delete mode 100644 hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMCancelPrepareResponse.java delete mode 100644 hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMPrepareResponse.java delete mode 100644 hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/upgrade/TestOMCancelPrepareRequest.java delete mode 100644 hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOzoneManagerPrepareState.java diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/CancelPrepareSubCommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/CancelPrepareSubCommand.java index e208c8cc9273..d1a89c625353 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/CancelPrepareSubCommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/CancelPrepareSubCommand.java @@ -27,7 +27,8 @@ */ @CommandLine.Command( name = "cancelprepare", - description = "Cancel prepare state in the OMs.", + description = "Deprecated. Kept for backward compatibility.", + hidden = true, mixinStandardHelpOptions = true, versionProvider = HddsVersionProvider.class ) diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java index a0eabd4b7d19..bde177f13f3a 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java @@ -40,11 +40,8 @@ */ @CommandLine.Command( name = "prepare", - description = "Prepares Ozone Manager for upgrade/downgrade, by applying " + - "all pending transactions, taking a Ratis snapshot at the last " + - "transaction and purging all logs on each OM instance. The returned " + - "transaction #ID corresponds to the last transaction in the quorum in" + - " which the snapshot is taken.", + description = "Deprecated. Kept for backward compatibility with older server versions.", + hidden = true, mixinStandardHelpOptions = true, versionProvider = HddsVersionProvider.class ) diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java index 7e98b1279c03..c96fc15f1ea9 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java @@ -281,6 +281,8 @@ public static boolean isReadOnly(OMRequest omRequest) { case GetObjectTagging: case GetQuotaRepairStatus: case StartQuotaRepair: + case Prepare: + case CancelPrepare: return true; case CreateVolume: case SetVolumeProperty: @@ -315,8 +317,6 @@ public static boolean isReadOnly(OMRequest omRequest) { // TODO: Remove once migrated to proto3 and mark fields in proto // as deprecated case FinalizeUpgrade: - case Prepare: - case CancelPrepare: case DeleteOpenKeys: case SetS3Secret: case RevokeS3Secret: @@ -430,8 +430,6 @@ public static boolean shouldSendToFollower(OMRequest omRequest) { // TODO: Remove once migrated to proto3 and mark fields in proto // as deprecated case FinalizeUpgrade: - case Prepare: - case CancelPrepare: case DeleteOpenKeys: case SetS3Secret: case RevokeS3Secret: diff --git a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/test.sh b/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/test.sh deleted file mode 100755 index 1e4ef919b4a7..000000000000 --- a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/test.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash -# 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. - -set -u -o pipefail - -COMPOSE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -export COMPOSE_DIR - -: "${OZONE_REPLICATION_FACTOR:=3}" -: "${OZONE_VOLUME:="${COMPOSE_DIR}/data"}" - -export OZONE_VOLUME - -export OZONE_DIR=/opt/hadoop -export OM_SERVICE_ID=omservice - -# shellcheck source=/dev/null -source "${COMPOSE_DIR}/../testlib.sh" - -create_data_dirs dn{1..3} om{1..3} scm - -start_docker_env - -# Write data and prepare cluster. -execute_robot_test scm omha/om-prepare.robot - -# Cancel preparation. -execute_robot_test scm omha/om-cancel-prepare.robot - -# Prepare cluster again. -execute_robot_test scm omha/om-prepare.robot -execute_robot_test scm omha/om-prepared.robot - -# re-start cluster and check that it remains prepared. -KEEP_RUNNING=false stop_docker_env -export OZONE_KEEP_RESULTS=true -start_docker_env - -execute_robot_test scm omha/om-prepared.robot - -# re-start cluster with --upgrade flag to take it out of prepare. -KEEP_RUNNING=false stop_docker_env -export OM_HA_ARGS='--upgrade' -start_docker_env - -# Writes should now succeed. -execute_robot_test scm topology/loaddata.robot -execute_robot_test scm topology/readdata.robot diff --git a/hadoop-ozone/dist/src/main/smoketest/omha/om-cancel-prepare.robot b/hadoop-ozone/dist/src/main/smoketest/omha/om-cancel-prepare.robot deleted file mode 100644 index 92dc1a25a048..000000000000 --- a/hadoop-ozone/dist/src/main/smoketest/omha/om-cancel-prepare.robot +++ /dev/null @@ -1,44 +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. - -*** Settings *** -Documentation Smoke test for ozone manager cancel prepare -Library OperatingSystem -Library String -Library BuiltIn -Resource ../commonlib.robot -Test Timeout 5 minutes -Suite Setup Run Keywords Generate volume and bucket names -... AND Get Security Enabled From Config -Test Setup Run Keyword if '${SECURITY_ENABLED}' == 'true' Kinit test user testuser testuser.keytab - -** Keywords *** -Generate volume and bucket names - ${random} = Generate Random String 5 [NUMBERS] - Set Suite Variable ${volume_name} ${random}-volume-for-cancel - Set Suite Variable ${bucket_name} ${random}-bucket-for-cancel - -** Test Cases *** -Cancel Ozone Manager Prepare - ${result} = Execute ozone admin om cancelprepare -id=omservice - Wait Until Keyword Succeeds 3min 5sec Should contain ${result} Cancel prepare succeeded - -Test write operations - Execute ozone sh volume create /${volume_name} - Execute ozone sh bucket create /${volume_name}/${bucket_name} - ${result} = Execute ozone sh key put /${volume_name}/${bucket_name}/cancel-key /opt/hadoop/NOTICE.txt - ${result} = Execute ozone sh key info /${volume_name}/${bucket_name}/cancel-key - Should contain ${result} \"name\" : \"cancel-key\" - diff --git a/hadoop-ozone/dist/src/main/smoketest/omha/om-prepare.robot b/hadoop-ozone/dist/src/main/smoketest/omha/om-prepare.robot deleted file mode 100644 index b483fd0b925a..000000000000 --- a/hadoop-ozone/dist/src/main/smoketest/omha/om-prepare.robot +++ /dev/null @@ -1,51 +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. - -*** Settings *** -Documentation Smoke test to test preparing OMs in an OM HA cluster. -Library OperatingSystem -Library String -Library BuiltIn -Resource ../commonlib.robot -Resource ../ozone-lib/freon.robot -Test Timeout 5 minutes -Suite Setup Run Keywords Get Security Enabled From Config -... AND Create Specific OM data for prepare -Test Setup Run Keyword if '${SECURITY_ENABLED}' == 'true' Kinit test user testuser testuser.keytab - -*** Keywords *** -Create Specific OM data for prepare - # Freon data to make sure there are a reasonable number of transactions in the system. - Freon OCKG prefix=om-prepare n=100 - ${random} = Generate Random String 5 [NUMBERS] - Set Suite Variable ${volume_name} ${random}-volume-for-prepare - Set Suite Variable ${bucket_name} ${random}-bucket-for-prepare - Execute ozone sh volume create /${volume_name} - Execute ozone sh bucket create /${volume_name}/${bucket_name} - Execute ozone sh key put /${volume_name}/${bucket_name}/prepare-key /opt/hadoop/NOTICE.txt - -** Test Cases *** -Prepare Ozone Manager - ${result} = Execute ozone admin om prepare -id=omservice - Wait Until Keyword Succeeds 3min 10sec Should contain ${result} OM Preparation successful! - -Checks if the expected data is present in OM - ${result} = Execute ozone sh key info /${volume_name}/${bucket_name}/prepare-key - Should contain ${result} \"name\" : \"prepare-key\" - -Test write operation fails - ${result} = Execute and checkrc ozone sh key put /${volume_name}/${bucket_name}/prepare-key2 /opt/hadoop/NOTICE.txt 255 - Should contain ${result} OM is in prepare mode - Execute and checkrc ozone sh key info /${volume_name}/${bucket_name}/prepare-key2 255 diff --git a/hadoop-ozone/dist/src/main/smoketest/omha/om-prepared.robot b/hadoop-ozone/dist/src/main/smoketest/omha/om-prepared.robot deleted file mode 100644 index bf745f670728..000000000000 --- a/hadoop-ozone/dist/src/main/smoketest/omha/om-prepared.robot +++ /dev/null @@ -1,37 +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. - -*** Settings *** -Documentation Smoke test to test that OMs are prepared in an OM HA cluster. -Library OperatingSystem -Library String -Library BuiltIn -Resource ../commonlib.robot -Test Timeout 5 minutes -Suite Setup Get Security Enabled From Config -Test Setup Run Keyword if '${SECURITY_ENABLED}' == 'true' Kinit test user testuser testuser.keytab - -** Test Cases *** -Test create volume fails - ${random} = Generate Random String 5 [NUMBERS] - Set Suite Variable ${volume_name} ${random}-volume-for-prepare - ${result} = Execute and checkrc ozone sh volume create /${volume_name} 255 - Should contain ${result} OM is in prepare mode - ${result} = Execute and checkrc ozone sh volume info /${volume_name} 255 - Should contain ${result} VOLUME_NOT_FOUND - -Test list volumes succeeds - ${result} = Execute ozone sh volume list - diff --git a/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot b/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot deleted file mode 100644 index 021515ebd06d..000000000000 --- a/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot +++ /dev/null @@ -1,29 +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. - -*** Settings *** -Documentation Prepares OMs -Resource ../commonlib.robot -Resource lib.robot -Test Timeout 5 minutes -Suite Setup Get Security Enabled From Config -Test Setup Run Keyword if '${SECURITY_ENABLED}' == 'true' Kinit test user testuser testuser.keytab - -*** Test Cases *** -Prepare Ozone Manager - Pass Execution If '%{OZONE_UPGRADE_FROM}' == '1.1.0' OM prepare is skipped for version %{OZONE_UPGRADE_FROM} - ${service_id} = Get OM Service ID - Pass Execution If '${service_id}' == '' OM prepare skipped in non-HA - Wait Until Keyword Succeeds 3min 10sec Prepare OM diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/OMUpgradeTestUtils.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/OMUpgradeTestUtils.java index 8f1fc41544a7..cd2d704eb462 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/OMUpgradeTestUtils.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/OMUpgradeTestUtils.java @@ -17,17 +17,14 @@ package org.apache.hadoop.ozone.om; -import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus.PREPARE_COMPLETED; import static org.apache.hadoop.ozone.upgrade.UpgradeFinalization.Status.FINALIZATION_DONE; import static org.apache.ozone.test.GenericTestUtils.waitFor; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; -import java.util.List; import java.util.concurrent.TimeoutException; import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol; import org.apache.hadoop.ozone.upgrade.UpgradeFinalization; -import org.apache.ozone.test.LambdaTestUtils; /** * Utility class to help test OM upgrade scenarios. @@ -38,35 +35,6 @@ private OMUpgradeTestUtils() { // Utility class. } - public static void assertClusterPrepared( - long preparedIndex, List ozoneManagers) throws Exception { - for (OzoneManager om : ozoneManagers) { - LambdaTestUtils.await(120000, - 1000, () -> { - if (!om.isRunning()) { - return false; - } else { - boolean preparedAtIndex = false; - OzoneManagerPrepareState.State state = - om.getPrepareState().getState(); - - if (state.getStatus() == PREPARE_COMPLETED) { - if (state.getIndex() == preparedIndex) { - preparedAtIndex = true; - } else { - // State will not change if we are prepared at the wrong - // index. Break out of wait. - throw new Exception("OM " + om.getOMNodeId() + " prepared " + - "but prepare index " + state.getIndex() + " does not " + - "match expected prepare index " + preparedIndex); - } - } - return preparedAtIndex; - } - }); - } - } - public static void waitForFinalization(OzoneManagerProtocol omClient) throws TimeoutException, InterruptedException { waitFor(() -> { 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 be0f99099770..66c7f1a0105d 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 @@ -18,11 +18,9 @@ package org.apache.hadoop.ozone.om; import static org.apache.hadoop.ozone.OzoneConsts.LAYOUT_VERSION_KEY; -import static org.apache.hadoop.ozone.om.OMUpgradeTestUtils.assertClusterPrepared; import static org.apache.hadoop.ozone.om.OMUpgradeTestUtils.waitForFinalization; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.INITIAL_VERSION; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutVersionManager.maxLayoutVersion; -import static org.apache.ozone.test.GenericTestUtils.waitFor; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -30,7 +28,6 @@ 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.ozone.MiniOzoneCluster; import org.apache.hadoop.ozone.MiniOzoneHAClusterImpl; @@ -39,9 +36,7 @@ import org.apache.hadoop.ozone.audit.OMAction; 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.BeforeEach; import org.junit.jupiter.api.Test; @@ -81,14 +76,7 @@ void testOMUpgradeFinalizationWithOneOMDown() throws Exception { OzoneManagerProtocol omClient = client.getObjectStore().getClientProxy() .getOzoneManagerClient(); - // Have to do a "prepare" operation to get rid of the logs in the active - // OMs. - 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()); @@ -97,19 +85,6 @@ void testOMUpgradeFinalizationWithOneOMDown() throws Exception { waitForFinalization(omClient); cluster.restartOzoneManager(downedOM, true); - OzoneManagerStateMachine omStateMachine = downedOM.getOmRatisServer() - .getOmStateMachine(); - try { - waitFor(() -> omStateMachine.getLifeCycleState().isPausingOrPaused(), - 1000, 60000); - } catch (TimeoutException timeEx) { - assertEquals(LifeCycle.State.RUNNING, - omStateMachine.getLifeCycle().getCurrentState()); - } - - waitFor(() -> !omStateMachine.getLifeCycle().getCurrentState() - .isPausingOrPaused(), 1000, 60000); - assertEquals(maxLayoutVersion(), downedOM.getVersionManager().getMetadataLayoutVersion()); String lvString = downedOM.getMetadataManager().getMetaTable() diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerPrepare.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerPrepare.java deleted file mode 100644 index edc9b569b2a5..000000000000 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerPrepare.java +++ /dev/null @@ -1,529 +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.om; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.NOT_SUPPORTED_OPERATION_WHEN_PREPARED; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import org.apache.hadoop.ozone.MiniOzoneHAClusterImpl; -import org.apache.hadoop.ozone.TestDataUtil; -import org.apache.hadoop.ozone.client.ObjectStore; -import org.apache.hadoop.ozone.client.OzoneVolume; -import org.apache.hadoop.ozone.client.protocol.ClientProtocol; -import org.apache.hadoop.ozone.container.ContainerTestHelper; -import org.apache.hadoop.ozone.om.exceptions.OMException; -import org.apache.hadoop.ozone.om.helpers.OmKeyInfo; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus; -import org.apache.ozone.test.LambdaTestUtils; -import org.apache.ozone.test.tag.Flaky; -import org.apache.ozone.test.tag.Slow; -import org.apache.ozone.test.tag.Unhealthy; -import org.apache.ratis.server.RaftServer; -import org.apache.ratis.util.ExitUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Test OM prepare against actual mini cluster. - */ -@Flaky("HDDS-5990") -public class TestOzoneManagerPrepare extends TestOzoneManagerHA { - private static final String BUCKET = "bucket"; - private static final String VOLUME = "volume"; - private static final String KEY_PREFIX = "key"; - - // Maximum time to wait for conditions involving Ratis logs. - private static final int WAIT_TIMEOUT_MILLIS = 120000; - private static final long PREPARE_FLUSH_WAIT_TIMEOUT_SECONDS = 120L; - private static final long PREPARE_FLUSH_INTERVAL_SECONDS = 5L; - - private MiniOzoneHAClusterImpl cluster; - private ClientProtocol clientProtocol; - private ObjectStore store; - - private static final Logger LOG = - LoggerFactory.getLogger(TestOzoneManagerPrepare.class); - - private void initInstanceVariables() { - cluster = getCluster(); - store = getObjectStore(); - clientProtocol = store.getClientProxy(); - } - - /** - * Make sure OM is out of Prepare state before executing individual tests. - */ - @BeforeEach - void setup() throws Exception { - initInstanceVariables(); - - LOG.info("Waiting for OM leader election"); - waitForLeaderToBeReady(); - submitCancelPrepareRequest(); - assertClusterNotPrepared(); - } - - /** - * Reset cluster between tests. - */ - @AfterEach - void resetCluster() throws Exception { - if (cluster != null) { - cluster.restartOzoneManager(); - } - } - - /** - * Writes data to the cluster via the leader OM, and then prepares it. - * Checks that every OM is prepared successfully. - */ - @Test - public void testPrepareWithTransactions() throws Exception { - long prepareIndex = submitPrepareRequest(); - assertClusterPrepared(prepareIndex); - assertRatisLogsCleared(); - - submitCancelPrepareRequest(); - assertClusterNotPrepared(); - - String volumeName = VOLUME + UUID.randomUUID().toString(); - Set writtenKeys = writeKeysAndWaitForLogs(volumeName, 50, - cluster.getOzoneManagersList()); - prepareIndex = submitPrepareRequest(); - - // Make sure all OMs are prepared and all OMs still have their data. - assertClusterPrepared(prepareIndex); - assertRatisLogsCleared(); - assertKeysWritten(volumeName, writtenKeys); - - // Should be able to "prepare" the OM group again. - assertShouldBeAbleToPrepare(); - } - - /** - * Writes data to the cluster. - * Shuts down one OM. - * Writes more data to the cluster. - * Submits prepare as ratis request. - * Checks that two live OMs are prepared. - * Revives the third OM - * Checks that third OM received all transactions and is prepared. - * @throws Exception - */ - @Test - @Unhealthy("RATIS-1481") // until upgrade to Ratis 2.3.0 - public void testPrepareDownedOM() throws Exception { - // Index of the OM that will be shut down during this test. - final int shutdownOMIndex = 2; - List runningOms = cluster.getOzoneManagersList(); - - String volumeName1 = VOLUME + UUID.randomUUID().toString(); - // Create keys with all 3 OMs up. - Set writtenKeysBeforeOmShutDown = writeKeysAndWaitForLogs( - volumeName1, 10, runningOms); - - // Shut down one OM. - cluster.stopOzoneManager(shutdownOMIndex); - OzoneManager downedOM = cluster.getOzoneManager(shutdownOMIndex); - assertFalse(downedOM.isRunning()); - assertEquals(runningOms.remove(shutdownOMIndex), downedOM); - - // Write keys with the remaining OMs up. - String volumeName2 = VOLUME + UUID.randomUUID().toString(); - Set writtenKeysAfterOmShutDown = - writeKeysAndWaitForLogs(volumeName2, 10, runningOms); - - long prepareIndex = submitPrepareRequest(); - - // Check that the two live OMs are prepared. - assertClusterPrepared(prepareIndex, runningOms); - - // Restart the downed OM and wait for it to catch up. - // Since prepare was the last Ratis transaction, it should have all data - // it missed once it receives the prepare transaction. - cluster.restartOzoneManager(downedOM, true); - runningOms.add(shutdownOMIndex, downedOM); - ExitUtils.assertNotTerminated(); - - // Make sure all OMs are prepared and still have data. - assertClusterPrepared(prepareIndex, runningOms); - assertKeysWritten(volumeName1, writtenKeysBeforeOmShutDown, runningOms); - assertKeysWritten(volumeName2, writtenKeysAfterOmShutDown, runningOms); - - // Cancelling prepare state of the cluster to try out an operation. - submitCancelPrepareRequest(); - assertClusterNotPrepared(); - - // Should be able to write data to all 3 OMs. - String volumeName3 = VOLUME + UUID.randomUUID().toString(); - store.createVolume(volumeName3); - for (OzoneManager om : runningOms) { - LambdaTestUtils.await(WAIT_TIMEOUT_MILLIS, 1000, () -> { - OMMetadataManager metadataManager = om.getMetadataManager(); - String volumeKey = metadataManager.getVolumeKey(volumeName3); - return metadataManager.getVolumeTable().get(volumeKey) != null; - }); - } - } - - @Test - public void testPrepareWithRestart() throws Exception { - // Create fresh cluster for this test to prevent timeout from restarting - // modified cluster. - shutdown(); - init(); - initInstanceVariables(); - - String volumeName = VOLUME + UUID.randomUUID().toString(); - writeKeysAndWaitForLogs(volumeName, 10); - - long prepareIndex = submitPrepareRequest(); - assertClusterPrepared(prepareIndex); - - // Restart all ozone managers. - cluster.restartOzoneManager(); - - // No check for cleared logs, since Ratis meta transactions may slip in - // on restart. - assertClusterPrepared(prepareIndex); - } - - @Slow("Saving on CI time since this is a pessimistic test. We should not " + - "be able to do anything with 2 OMs down.") - @Test - public void testPrepareFailsWhenTwoOmsAreDown() throws Exception { - // Shut down 2 OMs. - for (int i : Arrays.asList(1, 2)) { - cluster.stopOzoneManager(i); - OzoneManager downedOM = cluster.getOzoneManager(i); - assertFalse(downedOM.isRunning()); - } - - assertThrows(IOException.class, - () -> clientProtocol.getOzoneManagerClient().prepareOzoneManager( - PREPARE_FLUSH_WAIT_TIMEOUT_SECONDS, - PREPARE_FLUSH_INTERVAL_SECONDS)); - } - - /** - * Issues requests on ten different threads, for which one is a prepare and - * the rest are create volume. We cannot be sure of the exact order that - * the requests will execute, so this test checks that the cluster ends in - * a prepared state, and that create volume requests either succeed, or fail - * indicating the cluster was prepared before they were encountered. - * - * @throws Exception - */ - @Test - public void testPrepareWithMultipleThreads() throws Exception { - final int numThreads = 10; - final int prepareTaskIndex = 5; - - ExecutorService executorService = Executors.newFixedThreadPool(numThreads); - // For the prepare task, the future will return a log index. - // For the create volume tasks, 0 (dummy value) will be returned. - List> tasks = new ArrayList<>(); - - for (int i = 0; i < numThreads; i++) { - Callable task; - if (i == prepareTaskIndex) { - task = this::submitPrepareRequest; - } else { - String volumeName = VOLUME + i; - task = () -> { - clientProtocol.createVolume(volumeName); - return 0L; - }; - } - tasks.add(executorService.submit(task)); - } - - // For each task, wait for it to complete and check its result. - for (int i = 0; i < numThreads; i++) { - Future future = tasks.get(i); - - if (i == prepareTaskIndex) { - assertClusterPrepared(future.get()); - assertRatisLogsCleared(); - } else { - try { - // If this throws an exception, it should be an OMException - // indicating failure because the cluster was already prepared. - // If no exception is thrown, the volume should be created. - future.get(); - String volumeName = VOLUME + i; - assertTrue(clientProtocol.listVolumes(volumeName, "", 1) - .stream() - .anyMatch((vol) -> vol.getName().equals(volumeName))); - } catch (ExecutionException ex) { - OMException cause = assertInstanceOf(OMException.class, ex.getCause()); - assertEquals(NOT_SUPPORTED_OPERATION_WHEN_PREPARED, cause.getResult()); - } - } - } - - // In the above loop, we have waited for all threads to terminate. - executorService.shutdown(); - } - - @Test - public void testCancelPrepare() throws Exception { - String volumeName = VOLUME + UUID.randomUUID().toString(); - Set writtenKeys = writeKeysAndWaitForLogs(volumeName, 10); - long prepareIndex = submitPrepareRequest(); - - // Make sure all OMs are prepared and all OMs still have their data. - assertClusterPrepared(prepareIndex); - assertRatisLogsCleared(); - assertKeysWritten(volumeName, writtenKeys); - - // Cancel prepare and check that data is still present. - submitCancelPrepareRequest(); - assertClusterNotPrepared(); - assertKeysWritten(volumeName, writtenKeys); - - // Cancelling prepare again should have no effect. - submitCancelPrepareRequest(); - assertClusterNotPrepared(); - - // Write more data after cancelling prepare. - String volumeNameNew = VOLUME + UUID.randomUUID().toString(); - writtenKeys = writeKeysAndWaitForLogs(volumeNameNew, 10); - - // Cancelling prepare again should have no effect and new data should be - // preserved. - submitCancelPrepareRequest(); - assertClusterNotPrepared(); - assertKeysWritten(volumeNameNew, writtenKeys); - } - - private boolean logFilesPresentInRatisPeer(OzoneManager om) { - final RaftServer.Division server = om.getOmRatisServer().getServerDivision(); - final String ratisDir = server.getRaftServer().getProperties().get("raft.server.storage.dir"); - final String groupIdDirName = server.getGroup().getGroupId().getUuid().toString(); - File logDir = Paths.get(ratisDir, groupIdDirName, "current") - .toFile(); - - File[] files = logDir.listFiles(); - if (files != null) { - for (File file : files) { - if (file.getName().startsWith("log")) { - return true; - } - } - } - return false; - } - - private Set writeKeysAndWaitForLogs(String volumeName, - int numKeys) throws Exception { - return writeKeysAndWaitForLogs(volumeName, numKeys, - cluster.getOzoneManagersList()); - } - - private Set writeKeysAndWaitForLogs(String volumeName, int numKeys, - List ozoneManagers) throws Exception { - - store.createVolume(volumeName); - OzoneVolume volume = store.getVolume(volumeName); - volume.createBucket(BUCKET); - - Set writtenKeys = new HashSet<>(); - for (int i = 1; i <= numKeys; i++) { - String keyName = KEY_PREFIX + i; - writeTestData(volumeName, BUCKET, keyName); - writtenKeys.add(keyName); - } - - // Make sure all OMs have logs from writing data, so we can check that - // they are purged after prepare. - for (OzoneManager om : ozoneManagers) { - LambdaTestUtils.await(WAIT_TIMEOUT_MILLIS, 1000, - () -> logFilesPresentInRatisPeer(om)); - } - - return writtenKeys; - } - - private void writeTestData(String volumeName, - String bucketName, String keyName) throws Exception { - - String keyString = UUID.randomUUID().toString(); - byte[] data = ContainerTestHelper.getFixedLengthString( - keyString, 100).getBytes(UTF_8); - TestDataUtil.createKey(store.getVolume(volumeName). - getBucket(bucketName), keyName, data); - } - - private void assertKeysWritten(String volumeName, - Set expectedKeys) throws Exception { - assertKeysWritten(volumeName, expectedKeys, cluster.getOzoneManagersList()); - } - - /** - * Checks that all provided OMs have {@code expectedKeys} in the volume - * {@code volumeName} and retries checking until the test timeout. - * All provided OMs are checked, not just a majority, so that we can - * test that downed OMs are able to make a full recovery after preparation, - * even though the cluster could appear healthy with just 2 OMs. - */ - private void assertKeysWritten(String volumeName, Set expectedKeys, - List ozoneManagers) throws Exception { - for (OzoneManager om: ozoneManagers) { - // Wait for a potentially slow follower to apply all key writes. - LambdaTestUtils.await(WAIT_TIMEOUT_MILLIS, 1000, () -> { - List keys = om.getMetadataManager().listKeys(volumeName, - BUCKET, null, KEY_PREFIX, 100).getKeys(); - - boolean allKeysFound = (expectedKeys.size() == keys.size()); - if (!allKeysFound) { - LOG.info("In {} waiting for number of keys {} to equal " + - "expected number of keys {}.", om.getOMNodeId(), - keys.size(), expectedKeys.size()); - } else { - for (OmKeyInfo keyInfo : keys) { - if (!expectedKeys.contains(keyInfo.getKeyName())) { - allKeysFound = false; - LOG.info("In {} expected keys did not contain key {}", - om.getOMNodeId(), keyInfo.getKeyName()); - break; - } - } - } - - return allKeysFound; - }); - } - } - - private long submitPrepareRequest() throws Exception { - return clientProtocol.getOzoneManagerClient() - .prepareOzoneManager(PREPARE_FLUSH_WAIT_TIMEOUT_SECONDS, - PREPARE_FLUSH_INTERVAL_SECONDS); - } - - private void submitCancelPrepareRequest() throws Exception { - clientProtocol.getOzoneManagerClient().cancelOzoneManagerPrepare(); - } - - private void assertClusterPrepared(long expectedPreparedIndex) - throws Exception { - assertClusterPrepared(expectedPreparedIndex, - cluster.getOzoneManagersList()); - } - - private void assertClusterPrepared(long expectedPreparedIndex, - List ozoneManagers) throws Exception { - - for (OzoneManager om : ozoneManagers) { - // Wait for each OM to be running and transaction info to match to know - // it is prepared. - LambdaTestUtils.await(WAIT_TIMEOUT_MILLIS, - 1000, () -> { - if (!om.isRunning()) { - LOG.info("{} is not yet started.", om.getOMNodeId()); - return false; - } else { - OzoneManagerPrepareState.State state = - om.getPrepareState().getState(); - - LOG.info("{} has prepare status: {} prepare index: {}.", - om.getOMNodeId(), state.getStatus(), state.getIndex()); - - return (state.getStatus() == PrepareStatus.PREPARE_COMPLETED) && - (state.getIndex() >= expectedPreparedIndex); - } - }); - } - - // Submitting a read request should pass. - clientProtocol.listVolumes(VOLUME, "", 100); - - // Submitting write request should fail. - OMException omException = assertThrows(OMException.class, - () -> clientProtocol.createVolume("vol")); - assertEquals(NOT_SUPPORTED_OPERATION_WHEN_PREPARED, - omException.getResult()); - } - - private void assertClusterNotPrepared() throws Exception { - assertClusterNotPrepared(cluster.getOzoneManagersList()); - } - - private void assertClusterNotPrepared(List ozoneManagers) - throws Exception { - for (OzoneManager om : ozoneManagers) { - LambdaTestUtils.await(WAIT_TIMEOUT_MILLIS, - 1000, () -> { - if (!om.isRunning()) { - return false; - } else { - return om.getPrepareState().getState().getStatus() == - PrepareStatus.NOT_PREPARED; - } - }); - } - - // Submitting a read request should pass. - clientProtocol.listVolumes(VOLUME, "", 100); - - // Submitting write request should also pass. - clientProtocol.createVolume("vol"); - clientProtocol.deleteVolume("vol"); - } - - private void assertRatisLogsCleared() throws Exception { - assertRatisLogsCleared(cluster.getOzoneManagersList()); - } - - private void assertRatisLogsCleared(List ozoneManagers) - throws Exception { - for (OzoneManager om: ozoneManagers) { - LambdaTestUtils.await(WAIT_TIMEOUT_MILLIS, 1000, - () -> !logFilesPresentInRatisPeer(om)); - } - } - - private void assertShouldBeAbleToPrepare() throws Exception { - long prepareIndex = submitPrepareRequest(); - assertClusterPrepared(prepareIndex); - assertRatisLogsCleared(); - } -} diff --git a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto index bdb3cc3cee35..badfa4ee57dd 100644 --- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto +++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto @@ -75,9 +75,9 @@ enum Type { DBUpdates = 53; FinalizeUpgrade = 54; FinalizeUpgradeProgress = 55; - Prepare = 56; - PrepareStatus = 57; - CancelPrepare = 58; + Prepare = 56; // Deprecated + PrepareStatus = 57; // Deprecated + CancelPrepare = 58; // Deprecated GetDelegationToken = 61; RenewDelegationToken = 62; @@ -214,9 +214,9 @@ message OMRequest { optional DBUpdatesRequest dbUpdatesRequest = 53; optional FinalizeUpgradeRequest finalizeUpgradeRequest = 54; optional FinalizeUpgradeProgressRequest finalizeUpgradeProgressRequest = 55; - optional PrepareRequest prepareRequest = 56; - optional PrepareStatusRequest prepareStatusRequest = 57; - optional CancelPrepareRequest cancelPrepareRequest = 58; + optional PrepareRequest prepareRequest = 56 [deprecated = true]; + optional PrepareStatusRequest prepareStatusRequest = 57 [deprecated = true]; + optional CancelPrepareRequest cancelPrepareRequest = 58 [deprecated = true]; optional hadoop.common.GetDelegationTokenRequestProto getDelegationTokenRequest = 61; optional hadoop.common.RenewDelegationTokenRequestProto renewDelegationTokenRequest= 62; @@ -355,9 +355,9 @@ message OMResponse { optional DBUpdatesResponse dbUpdatesResponse = 52; optional FinalizeUpgradeResponse finalizeUpgradeResponse = 54; optional FinalizeUpgradeProgressResponse finalizeUpgradeProgressResponse = 55; - optional PrepareResponse prepareResponse = 56; - optional PrepareStatusResponse prepareStatusResponse = 57; - optional CancelPrepareResponse cancelPrepareResponse = 58; + optional PrepareResponse prepareResponse = 56 [deprecated = true]; + optional PrepareStatusResponse prepareStatusResponse = 57 [deprecated = true]; + optional CancelPrepareResponse cancelPrepareResponse = 58 [deprecated = true]; optional GetDelegationTokenResponseProto getDelegationTokenResponse = 61; optional RenewDelegationTokenResponseProto renewDelegationTokenResponse = 62; @@ -529,9 +529,9 @@ enum Status { REMOVE_UPGRADE_TO_LAYOUT_VERSION_FAILED = 70; UPDATE_LAYOUT_VERSION_FAILED = 71; LAYOUT_FEATURE_FINALIZATION_FAILED = 72; - PREPARE_FAILED = 73; - NOT_SUPPORTED_OPERATION_WHEN_PREPARED = 74; - NOT_SUPPORTED_OPERATION_PRIOR_FINALIZATION = 75; + PREPARE_FAILED = 73; // Deprecated + NOT_SUPPORTED_OPERATION_WHEN_PREPARED = 74; // Deprecated + NOT_SUPPORTED_OPERATION_PRIOR_FINALIZATION = 75; // Deprecated TENANT_NOT_FOUND = 76; TENANT_ALREADY_EXISTS = 77; @@ -1616,23 +1616,38 @@ message FinalizeUpgradeProgressResponse { required hadoop.hdds.UpgradeFinalizationStatus status = 1; } +/** + * Deprecated. Prepare functionality has been removed. + */ message PrepareRequest { - required PrepareRequestArgs args = 1; + required PrepareRequestArgs args = 1 [deprecated = true]; } +/** + * Deprecated. Prepare functionality has been removed. + */ message PrepareRequestArgs { - optional uint64 txnApplyWaitTimeoutSeconds = 1 [default = 300]; - optional uint64 txnApplyCheckIntervalSeconds = 2 [default = 5]; + optional uint64 txnApplyWaitTimeoutSeconds = 1 [default = 300, deprecated = true]; + optional uint64 txnApplyCheckIntervalSeconds = 2 [default = 5, deprecated = true]; } +/** + * Deprecated. Prepare functionality has been removed. + */ message PrepareResponse { - required uint64 txnID = 1; + required uint64 txnID = 1 [deprecated = true]; } +/** + * Deprecated. Prepare functionality has been removed. + */ message PrepareStatusRequest { - required uint64 txnID = 1; + required uint64 txnID = 1 [deprecated = true]; } +/** + * Deprecated. Prepare functionality has been removed and server status will always be NOT_PREPARED. + */ message PrepareStatusResponse { enum PrepareStatus { NOT_PREPARED = 1; @@ -1640,13 +1655,19 @@ message PrepareStatusResponse { PREPARE_COMPLETED = 3; } required PrepareStatus status = 1; - optional uint64 currentTxnIndex = 2; + optional uint64 currentTxnIndex = 2 [deprecated = true]; } +/** + * Deprecated. Prepare functionality has been removed. + */ message CancelPrepareRequest { } +/** + * Deprecated. Prepare functionality has been removed. + */ message CancelPrepareResponse { } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java index f728a88b2b38..ff37077942e2 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java @@ -105,8 +105,6 @@ public enum OMAction implements AuditAction { ABORT_EXPIRED_MULTIPART_UPLOAD, - UPGRADE_PREPARE, - UPGRADE_CANCEL, UPGRADE_FINALIZE, LIST_OPEN_FILES, diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMStarterInterface.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMStarterInterface.java index 94be4139bc2a..0942fd33b058 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMStarterInterface.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMStarterInterface.java @@ -34,7 +34,4 @@ boolean init(OzoneConfiguration conf) throws IOException, void bootstrap(OzoneConfiguration conf, boolean force) throws IOException, AuthenticationException; - - void startAndCancelPrepare(OzoneConfiguration conf) throws IOException, - AuthenticationException; } 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 b48d8fbe40fc..c67ade2b35a2 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 @@ -47,10 +47,8 @@ import static org.apache.hadoop.ozone.OzoneConsts.OM_METRICS_FILE; import static org.apache.hadoop.ozone.OzoneConsts.OM_METRICS_TEMP_FILE; import static org.apache.hadoop.ozone.OzoneConsts.OZONE_RATIS_SNAPSHOT_DIR; -import static org.apache.hadoop.ozone.OzoneConsts.PREPARE_MARKER_KEY; import static org.apache.hadoop.ozone.OzoneConsts.RPC_PORT; import static org.apache.hadoop.ozone.OzoneConsts.SCM_CA_CERT_STORAGE_DIR; -import static org.apache.hadoop.ozone.OzoneConsts.TRANSACTION_INFO_KEY; import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_DEFAULT_BUCKET_LAYOUT; import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_DEFAULT_BUCKET_LAYOUT_DEFAULT; import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_DIR_DELETING_SERVICE_INTERVAL; @@ -100,7 +98,6 @@ import static org.apache.hadoop.ozone.om.s3.S3SecretStoreConfigurationKeys.S3_SECRET_STORAGE_TYPE; import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerInterServiceProtocolProtos.OzoneManagerInterService; import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneManagerService; -import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus; import static org.apache.hadoop.security.UserGroupInformation.getCurrentUser; import static org.apache.hadoop.util.ExitUtil.terminate; import static org.apache.hadoop.util.Time.monotonicNow; @@ -466,8 +463,6 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl private final boolean isStrictS3; private ExitManager exitManager; - private OzoneManagerPrepareState prepareState; - private boolean isBootstrapping = false; private boolean isForcedBootstrapping = false; @@ -1018,12 +1013,6 @@ public void close() { updateLayoutVersionInDB(versionManager, metadataManager); } } - - instantiatePrepareStateAfterSnapshot(); - } else { - // Prepare state depends on the transaction ID of metadataManager after a - // restart. - instantiatePrepareStateOnStartup(); } } @@ -4881,102 +4870,6 @@ public OMLayoutVersionManager getVersionManager() { return versionManager; } - public OzoneManagerPrepareState getPrepareState() { - return prepareState; - } - - /** - * Determines if the prepare gate should be enabled on this OM after OM - * is restarted. - * This must be done after metadataManager is instantiated - * and before the RPC server is started. - */ - private void instantiatePrepareStateOnStartup() - throws IOException { - TransactionInfo txnInfo = metadataManager.getTransactionInfoTable() - .get(TRANSACTION_INFO_KEY); - if (txnInfo == null) { - // No prepare request could be received if there are not transactions. - prepareState = new OzoneManagerPrepareState(configuration); - } else { - prepareState = new OzoneManagerPrepareState(configuration, - txnInfo.getTransactionIndex()); - TransactionInfo dbPrepareValue = - metadataManager.getTransactionInfoTable().get(PREPARE_MARKER_KEY); - - boolean hasMarkerFile = - (prepareState.getState().getStatus() == - PrepareStatus.PREPARE_COMPLETED); - boolean hasDBMarker = (dbPrepareValue != null); - - if (hasDBMarker) { - long dbPrepareIndex = dbPrepareValue.getTransactionIndex(); - - if (hasMarkerFile) { - long prepareFileIndex = prepareState.getState().getIndex(); - // If marker and DB prepare index do not match, use the DB value - // since this is synced through Ratis, to avoid divergence. - if (prepareFileIndex != dbPrepareIndex) { - LOG.warn("Prepare marker file index {} does not match DB prepare " + - "index {}. Writing DB index to prepare file and maintaining " + - "prepared state.", prepareFileIndex, dbPrepareIndex); - prepareState.finishPrepare(dbPrepareIndex); - } - // Else, marker and DB are present and match, so OM is prepared. - } else { - // Prepare cancelled with startup flag to remove marker file. - // Persist this to the DB. - // If the startup flag is used it should be used on all OMs to avoid - // divergence. - metadataManager.getTransactionInfoTable().delete(PREPARE_MARKER_KEY); - } - } else if (hasMarkerFile) { - // Marker file present but no DB entry present. - // This should never happen. If a prepare request fails partway - // through, OM should replay it so both the DB and marker file exist. - throw new OMException("Prepare marker file found on startup without " + - "a corresponding database entry. Corrupt prepare state.", - ResultCodes.PREPARE_FAILED); - } - // Else, no DB or marker file, OM is not prepared. - } - } - - /** - * Determines if the prepare gate should be enabled on this OM after OM - * receives a snapshot. - */ - private void instantiatePrepareStateAfterSnapshot() - throws IOException { - TransactionInfo txnInfo = metadataManager.getTransactionInfoTable() - .get(TRANSACTION_INFO_KEY); - if (txnInfo == null) { - // No prepare request could be received if there are not transactions. - prepareState = new OzoneManagerPrepareState(configuration); - } else { - prepareState = new OzoneManagerPrepareState(configuration, - txnInfo.getTransactionIndex()); - TransactionInfo dbPrepareValue = - metadataManager.getTransactionInfoTable().get(PREPARE_MARKER_KEY); - - boolean hasDBMarker = (dbPrepareValue != null); - - if (hasDBMarker) { - // Snapshot contained a prepare request to apply. - // Update the in memory prepare gate and marker file index. - // If we have already done this, the operation is idempotent. - long dbPrepareIndex = dbPrepareValue.getTransactionIndex(); - prepareState.restorePrepareFromIndex(dbPrepareIndex, - txnInfo.getTransactionIndex()); - } else { - // No DB marker. - // Deletes marker file if exists, otherwise does nothing if we were not - // already prepared. - prepareState.cancelPrepare(); - } - } - } - public int getMinMultipartUploadPartSize() { return minMultipartUploadPartSize; } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerPrepareState.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerPrepareState.java deleted file mode 100644 index 076c13766719..000000000000 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerPrepareState.java +++ /dev/null @@ -1,310 +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.om; - -import com.google.common.annotations.VisibleForTesting; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import org.apache.hadoop.hdds.conf.ConfigurationSource; -import org.apache.hadoop.hdds.server.ServerUtils; -import org.apache.hadoop.ozone.OzoneConsts; -import org.apache.hadoop.ozone.om.exceptions.OMException; -import org.apache.hadoop.ozone.om.ratis.OzoneManagerRatisServer; -import org.apache.hadoop.ozone.om.ratis.OzoneManagerStateMachine; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Controls the prepare state of the {@link OzoneManager} containing the - * instance. When prepared, an ozone manager should have no Ratis logs - * remaining, disallow all write requests except prepare and cancel prepare, - * and have a marker file present on disk that will cause it to remain prepared - * on restart. - */ -public final class OzoneManagerPrepareState { - public static final long NO_PREPARE_INDEX = -1; - - private static final Logger LOG = - LoggerFactory.getLogger(OzoneManagerPrepareState.class); - - private boolean prepareGateEnabled; - private long prepareIndex; - private PrepareStatus status; - private final ConfigurationSource conf; - - /** - * Sets prepare state to {@link PrepareStatus#NOT_PREPARED}, - * ignoring any marker file that may or may not exist. - */ - public OzoneManagerPrepareState(ConfigurationSource conf) { - this.conf = conf; - prepareIndex = NO_PREPARE_INDEX; - prepareGateEnabled = false; - status = PrepareStatus.NOT_PREPARED; - } - - /** - * Restores prepare state from the marker file if it exists, otherwise sets - * prepare state to {@link PrepareStatus#NOT_PREPARED}. - * - * @param conf Configuration used to determine marker file location. - * @param currentIndex The OM's current log index to verify the prepare - * index against. Prepare index should not be larger than the current - * index. - * @throws IOException On error restoring prepare state from marker file. - */ - public OzoneManagerPrepareState(ConfigurationSource conf, long currentIndex) - throws IOException { - this(conf); - - if (getPrepareMarkerFile().exists()) { - restorePrepareFromFile(currentIndex); - } - } - - /** - * Turns on the prepare gate flag, clears the prepare index, and moves the - * prepare status to {@link PrepareStatus#PREPARE_GATE_ENABLED}. - * - * Turning on the prepare gate flag will enable a gate in the - * {@link OzoneManagerStateMachine#preAppendTransaction} (called on leader - * OM only) and {@link OzoneManagerRatisServer#submitRequest} - * (called on all OMs) that block write requests from reaching the OM and - * fail them with error responses to the client. - */ - public synchronized void enablePrepareGate() { - prepareGateEnabled = true; - prepareIndex = NO_PREPARE_INDEX; - status = PrepareStatus.PREPARE_GATE_ENABLED; - } - - /** - * Removes the prepare marker file, clears the prepare index, turns off - * the prepare gate, and moves the prepare status to - * {@link PrepareStatus#NOT_PREPARED}. - * This can be called from any state to clear the current prepare state. - * - * @throws IOException If the prepare marker file exists but cannot be - * deleted. - */ - public synchronized void cancelPrepare() throws IOException { - prepareIndex = NO_PREPARE_INDEX; - prepareGateEnabled = false; - status = PrepareStatus.NOT_PREPARED; - deletePrepareMarkerFile(); - } - - /** - * Enables the prepare gate, writes the prepare marker file, sets the in - * memory prepare index, and - * moves the prepare status to {@link PrepareStatus#PREPARE_COMPLETED}. - * This can be called from any state to move the OM into prepare mode. - * - * @param index The log index to prepare the OM on. - * @throws IOException If the marker file cannot be written. - */ - public synchronized void finishPrepare(long index) throws IOException { - restorePrepareFromIndex(index, index, true); - } - - /** - * Finishes preparation the same way as - * {@link OzoneManagerPrepareState#finishPrepare(long)}, but only if {@code - * currentIndex} is at least as large as {@code minIndex}. This is useful - * if the current log index needs to be checked against a prepare index - * saved to disk for validity. - */ - public synchronized void restorePrepareFromIndex(long restoredPrepareIndex, - long currentIndex) throws IOException { - restorePrepareFromIndex(restoredPrepareIndex, currentIndex, true); - } - - private void restorePrepareFromIndex(long restoredPrepareIndex, - long currentIndex, boolean writeFile) throws IOException { - if (restoredPrepareIndex <= currentIndex) { - // Enabling the prepare gate is idempotent, and may have already been - // performed if we are the leader. If we are a follower, we must ensure - // this is run now in case we become the leader. - enablePrepareGate(); - - if (writeFile) { - writePrepareMarkerFile(restoredPrepareIndex); - } - prepareIndex = currentIndex; - status = PrepareStatus.PREPARE_COMPLETED; - } else { - throwPrepareException("Failed to restore OM prepare " + - "state, because the existing prepare index %d is larger than" + - "the current index %d.", restoredPrepareIndex, currentIndex); - } - } - - /** - * Uses the on disk marker file to determine the OM's prepare state. - * If the marker file exists and contains an index matching {@code - * currentIndex}, the necessary steps will be taken to finish - * preparation and the state will be moved to - * {@link PrepareStatus#PREPARE_COMPLETED}. - * Else, the status will be moved to - * {@link PrepareStatus#NOT_PREPARED} and any preparation steps will - * be cancelled. - * - * @throws IOException If the marker file cannot be read, and it cannot be - * deleted as part of moving to the - * {@link PrepareStatus#NOT_PREPARED} state. - */ - public synchronized void restorePrepareFromFile(long currentIndex) - throws IOException { - long prepareMarkerIndex = NO_PREPARE_INDEX; - - File prepareMarkerFile = getPrepareMarkerFile(); - if (prepareMarkerFile.exists()) { - byte[] data = new byte[(int) prepareMarkerFile.length()]; - try (InputStream stream = Files.newInputStream(prepareMarkerFile.toPath())) { - stream.read(data); - } catch (IOException e) { - throwPrepareException(e, "Failed to read prepare marker " + - "file %s while restoring OM.", prepareMarkerFile.getAbsolutePath()); - } - - try { - prepareMarkerIndex = Long.parseLong( - new String(data, StandardCharsets.UTF_8)); - } catch (NumberFormatException e) { - throwPrepareException("Failed to parse log index from " + - "prepare marker file %s while restoring OM.", - prepareMarkerFile.getAbsolutePath()); - } - } else { - // No marker file found. - throwPrepareException("Unable to find prepare marker file to restore" + - " from. Expected %s: ", prepareMarkerFile.getAbsolutePath()); - } - - restorePrepareFromIndex(prepareMarkerIndex, currentIndex, false); - } - - private void throwPrepareException(Throwable cause, String format, - Object... args) throws OMException { - throw new OMException(String.format(format, args), cause, - OMException.ResultCodes.PREPARE_FAILED); - } - - private void throwPrepareException(String format, - Object... args) throws OMException { - throw new OMException(String.format(format, args), - OMException.ResultCodes.PREPARE_FAILED); - } - - /** - * If the prepare gate is enabled, always returns true. - * If the prepare gate is disabled, returns true only if {@code - * requestType} is {@code Prepare} or {@code CancelPrepare}. Returns false - * otherwise. - */ - public synchronized boolean requestAllowed(Type requestType) { - boolean requestAllowed = true; - - if (prepareGateEnabled) { - requestAllowed = - (requestType == Type.Prepare) || (requestType == Type.CancelPrepare); - } - - return requestAllowed; - } - - /** - * @return the current log index and status of preparation. - * Both fields are returned together to provide a consistent view of the - * state, which would not be guaranteed if they had to be retrieved through - * separate getters. - */ - public synchronized State getState() { - return new State(prepareIndex, status); - } - - /** - * Creates a prepare marker file inside the OM metadata directory which - * contains the log index {@code index}. - * If a marker file already exists, it will be overwritten. - */ - private void writePrepareMarkerFile(long index) throws IOException { - File markerFile = getPrepareMarkerFile(); - File parentDir = markerFile.getParentFile(); - Files.createDirectories(parentDir.toPath()); - - try (OutputStream stream = Files.newOutputStream(markerFile.toPath())) { - stream.write(Long.toString(index).getBytes(StandardCharsets.UTF_8)); - } - - LOG.info("Prepare marker file written with log index {} to file {}", index, - markerFile.getAbsolutePath()); - } - - private void deletePrepareMarkerFile() - throws IOException { - File markerFile = getPrepareMarkerFile(); - if (markerFile.exists()) { - Files.delete(markerFile.toPath()); - LOG.info("Deleted prepare marker file: {}", markerFile.getAbsolutePath()); - } else { - LOG.debug("Request to delete prepare marker file that does not exist: {}", - markerFile.getAbsolutePath()); - } - } - - /** - * Returns a {@link File} object representing the prepare marker file, - * which may or may not actually exist on disk. - * This method should be used for testing only. - */ - @VisibleForTesting - public File getPrepareMarkerFile() { - File markerFileDir = new File(ServerUtils.getOzoneMetaDirPath(conf), - OMStorage.STORAGE_DIR_CURRENT); - return new File(markerFileDir, OzoneConsts.PREPARE_MARKER); - } - - /** - * The current state of preparation is defined by the status and the - * prepare index that are currently set. - */ - public static class State { - private final long index; - private final PrepareStatus status; - - public State(long index, PrepareStatus status) { - this.index = index; - this.status = status; - } - - public long getIndex() { - return index; - } - - public PrepareStatus getStatus() { - return status; - } - } -} diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java index 8f7b01991a86..4697f74f03cd 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java @@ -111,16 +111,18 @@ public void initOm() @CommandLine.Command(name = "--upgrade", aliases = "--downgrade", customSynopsis = "ozone om [global options] --upgrade", - description = "Cancels prepare state in this OM on startup", + description = "Deprecated. Starts OM normally. Kept for backward compatibility.", + hidden = true, mixinStandardHelpOptions = true, versionProvider = HddsVersionProvider.class) public void startOmUpgrade() throws Exception { try { commonInit(); - receiver.startAndCancelPrepare(conf); + // These flags are deprecated and no longer have special behavior. + // Just start OM normally for backward compatibility. + receiver.start(conf); } catch (Exception ex) { - LOG.error("Cancelling prepare to start OM in upgrade mode failed " + - "with exception", ex); + LOG.error("Starting OM failed with exception", ex); throw ex; } } @@ -224,15 +226,5 @@ public void bootstrap(OzoneConfiguration conf, boolean force) om.join(); } } - - @Override - public void startAndCancelPrepare(OzoneConfiguration conf) - throws IOException, AuthenticationException { - try (OzoneManager om = OzoneManager.createOm(conf)) { - om.getPrepareState().cancelPrepare(); - om.start(); - om.join(); - } - } } } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java index e6185f3d65a0..8075c9bbd853 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java @@ -89,8 +89,6 @@ private static void init() { CMD_AUDIT_ACTION_MAP.put(Type.AbortExpiredMultiPartUploads, OMAction.ABORT_EXPIRED_MULTIPART_UPLOAD); CMD_AUDIT_ACTION_MAP.put(Type.SetVolumeProperty, OMAction.SET_OWNER); CMD_AUDIT_ACTION_MAP.put(Type.SetBucketProperty, OMAction.UPDATE_BUCKET); - CMD_AUDIT_ACTION_MAP.put(Type.Prepare, OMAction.UPGRADE_PREPARE); - CMD_AUDIT_ACTION_MAP.put(Type.CancelPrepare, OMAction.UPGRADE_CANCEL); CMD_AUDIT_ACTION_MAP.put(Type.FinalizeUpgrade, OMAction.UPGRADE_FINALIZE); CMD_AUDIT_ACTION_MAP.put(Type.GetObjectTagging, OMAction.GET_OBJECT_TAGGING); CMD_AUDIT_ACTION_MAP.put(Type.PutObjectTagging, OMAction.PUT_OBJECT_TAGGING); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java index bccf9e0900a1..bcee2e8cfff9 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java @@ -68,7 +68,6 @@ import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status; import org.apache.ratis.conf.Parameters; import org.apache.ratis.conf.RaftProperties; import org.apache.ratis.grpc.GrpcConfigKeys; @@ -245,27 +244,9 @@ public static OzoneManagerRatisServer newOMRatisServer( * @throws ServiceException */ public OMResponse submitRequest(OMRequest omRequest, boolean isWrite) throws ServiceException { - // In prepare mode, only prepare and cancel requests are allowed to go - // through. - if (ozoneManager.getPrepareState().requestAllowed(omRequest.getCmdType())) { - RaftClientRequest raftClientRequest = createRaftRequest(omRequest, isWrite); - RaftClientReply raftClientReply = submitRequestToRatis(raftClientRequest); - return createOmResponse(omRequest, raftClientReply); - } else { - LOG.info("Rejecting write request on OM {} because it is in prepare " + - "mode: {}", ozoneManager.getOMNodeId(), - omRequest.getCmdType().name()); - - String message = "Cannot apply write request " + - omRequest.getCmdType().name() + " when OM is in prepare mode."; - OMResponse.Builder omResponse = OMResponse.newBuilder() - .setMessage(message) - .setStatus(Status.NOT_SUPPORTED_OPERATION_WHEN_PREPARED) - .setCmdType(omRequest.getCmdType()) - .setTraceID(omRequest.getTraceID()) - .setSuccess(false); - return omResponse.build(); - } + RaftClientRequest raftClientRequest = createRaftRequest(omRequest, isWrite); + RaftClientReply raftClientReply = submitRequestToRatis(raftClientRequest); + return createOmResponse(omRequest, raftClientReply); } private OMResponse createOmResponse(OMRequest omRequest, diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java index a3ad217ceef7..56402b6f2040 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java @@ -41,7 +41,6 @@ import org.apache.hadoop.ozone.audit.OMSystemAction; import org.apache.hadoop.ozone.om.OMConfigKeys; import org.apache.hadoop.ozone.om.OzoneManager; -import org.apache.hadoop.ozone.om.OzoneManagerPrepareState; import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.om.execution.flowcontrol.ExecutionContext; import org.apache.hadoop.ozone.om.helpers.OMRatisHelper; @@ -49,12 +48,10 @@ import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerRatisUtils; import org.apache.hadoop.ozone.om.response.DummyOMClientResponse; import org.apache.hadoop.ozone.om.response.OMClientResponse; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse; import org.apache.hadoop.ozone.protocolPB.OzoneManagerRequestHandler; import org.apache.hadoop.ozone.protocolPB.RequestHandler; -import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.util.Time; import org.apache.hadoop.util.concurrent.HadoopExecutors; import org.apache.ratis.proto.RaftProtos; @@ -65,7 +62,6 @@ import org.apache.ratis.protocol.RaftGroupMemberId; import org.apache.ratis.protocol.RaftPeer; import org.apache.ratis.protocol.RaftPeerId; -import org.apache.ratis.protocol.exceptions.StateMachineException; import org.apache.ratis.server.RaftServer; import org.apache.ratis.server.protocol.TermIndex; import org.apache.ratis.server.raftlog.RaftLog; @@ -335,52 +331,6 @@ public TransactionContext startTransaction( .build(); } - @Override - public TransactionContext preAppendTransaction(TransactionContext trx) - throws IOException { - final OMRequest request = (OMRequest) trx.getStateMachineContext(); - OzoneManagerProtocolProtos.Type cmdType = request.getCmdType(); - - OzoneManagerPrepareState prepareState = ozoneManager.getPrepareState(); - - if (LOG.isDebugEnabled()) { - LOG.debug("{}: preAppendTransaction {}", getId(), TermIndex.valueOf(trx.getLogEntry())); - } - - if (cmdType == OzoneManagerProtocolProtos.Type.Prepare) { - // Must authenticate prepare requests here, since we must determine - // whether or not to apply the prepare gate before proceeding with the - // prepare request. - UserGroupInformation userGroupInformation = - UserGroupInformation.createRemoteUser( - request.getUserInfo().getUserName()); - if (ozoneManager.getAclsEnabled() - && !ozoneManager.isAdmin(userGroupInformation)) { - String message = "Access denied for user " + userGroupInformation - + ". Superuser privilege is required to prepare upgrade/downgrade."; - OMException cause = - new OMException(message, OMException.ResultCodes.ACCESS_DENIED); - // Leader should not step down because of this failure. - throw new StateMachineException(message, cause, false); - } else { - prepareState.enablePrepareGate(); - } - } - - // In prepare mode, only prepare and cancel requests are allowed to go - // through. - if (prepareState.requestAllowed(cmdType)) { - return trx; - } else { - String message = "Cannot apply write request " + - request.getCmdType().name() + " when OM is in prepare mode."; - OMException cause = new OMException(message, - OMException.ResultCodes.NOT_SUPPORTED_OPERATION_WHEN_PREPARED); - // Indicate that the leader should not step down because of this failure. - throw new StateMachineException(message, cause, false); - } - } - /* * Apply a committed log entry to the state machine. */ diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java index 1778de3520d9..c6cc6631573d 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java @@ -84,9 +84,7 @@ import org.apache.hadoop.ozone.om.request.snapshot.OMSnapshotPurgeRequest; import org.apache.hadoop.ozone.om.request.snapshot.OMSnapshotRenameRequest; import org.apache.hadoop.ozone.om.request.snapshot.OMSnapshotSetPropertyRequest; -import org.apache.hadoop.ozone.om.request.upgrade.OMCancelPrepareRequest; import org.apache.hadoop.ozone.om.request.upgrade.OMFinalizeUpgradeRequest; -import org.apache.hadoop.ozone.om.request.upgrade.OMPrepareRequest; import org.apache.hadoop.ozone.om.request.util.OMEchoRPCWriteRequest; import org.apache.hadoop.ozone.om.request.volume.OMQuotaRepairRequest; import org.apache.hadoop.ozone.om.request.volume.OMVolumeCreateRequest; @@ -185,10 +183,6 @@ public static OMClientRequest createClientRequest(OMRequest omRequest, return new S3GetSecretRequest(omRequest); case FinalizeUpgrade: return new OMFinalizeUpgradeRequest(omRequest); - case Prepare: - return new OMPrepareRequest(omRequest); - case CancelPrepare: - return new OMCancelPrepareRequest(omRequest); case SetS3Secret: return new OMSetSecretRequest(omRequest); case RevokeS3Secret: diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMCancelPrepareRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMCancelPrepareRequest.java deleted file mode 100644 index efbdf6bcd189..000000000000 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMCancelPrepareRequest.java +++ /dev/null @@ -1,102 +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.om.request.upgrade; - -import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type; - -import java.io.IOException; -import java.util.HashMap; -import org.apache.hadoop.ozone.audit.AuditLogger; -import org.apache.hadoop.ozone.audit.OMAction; -import org.apache.hadoop.ozone.om.OzoneManager; -import org.apache.hadoop.ozone.om.exceptions.OMException; -import org.apache.hadoop.ozone.om.execution.flowcontrol.ExecutionContext; -import org.apache.hadoop.ozone.om.request.OMClientRequest; -import org.apache.hadoop.ozone.om.request.util.OmResponseUtil; -import org.apache.hadoop.ozone.om.response.OMClientResponse; -import org.apache.hadoop.ozone.om.response.upgrade.OMCancelPrepareResponse; -import org.apache.hadoop.ozone.om.response.upgrade.OMPrepareResponse; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CancelPrepareResponse; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse; -import org.apache.hadoop.security.UserGroupInformation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * OM request class to cancel preparation. - */ -public class OMCancelPrepareRequest extends OMClientRequest { - private static final Logger LOG = - LoggerFactory.getLogger(OMCancelPrepareRequest.class); - - public OMCancelPrepareRequest(OMRequest omRequest) { - super(omRequest); - } - - @Override - public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, ExecutionContext context) { - - LOG.info("OM {} Received cancel prepare request with log {}", ozoneManager.getOMNodeId(), context.getTermIndex()); - - OMRequest omRequest = getOmRequest(); - AuditLogger auditLogger = ozoneManager.getAuditLogger(); - OzoneManagerProtocolProtos.UserInfo userInfo = omRequest.getUserInfo(); - OMResponse.Builder responseBuilder = - OmResponseUtil.getOMResponseBuilder(omRequest); - responseBuilder.setCmdType(Type.CancelPrepare); - OMClientResponse response = null; - Exception exception = null; - - try { - UserGroupInformation ugi = createUGIForApi(); - if (ozoneManager.getAclsEnabled() && !ozoneManager.isAdmin(ugi)) { - throw new OMException("Access denied for user " - + ugi + ". " + - "Superuser privilege is required to cancel ozone manager " + - "preparation.", - OMException.ResultCodes.ACCESS_DENIED); - } - - // Create response. - CancelPrepareResponse omResponse = CancelPrepareResponse.newBuilder() - .build(); - responseBuilder.setCancelPrepareResponse(omResponse); - response = new OMCancelPrepareResponse(responseBuilder.build()); - - // Deletes on disk marker file, does not update DB and therefore does - // not update cache. - ozoneManager.getPrepareState().cancelPrepare(); - - LOG.info("OM {} prepare state cancelled at log {}. Returning response {}", - ozoneManager.getOMNodeId(), context.getTermIndex(), omResponse); - } catch (IOException e) { - exception = e; - LOG.error("Cancel Prepare Request apply failed in {}. ", - ozoneManager.getOMNodeId(), e); - response = new OMPrepareResponse( - createErrorOMResponse(responseBuilder, e)); - } - - markForAudit(auditLogger, buildAuditMessage(OMAction.UPGRADE_CANCEL, - new HashMap<>(), exception, userInfo)); - return response; - } - -} diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMPrepareRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMPrepareRequest.java deleted file mode 100644 index f349e74e5417..000000000000 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMPrepareRequest.java +++ /dev/null @@ -1,273 +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.om.request.upgrade; - -import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type; - -import java.io.IOException; -import java.time.Duration; -import java.time.temporal.ChronoUnit; -import java.util.HashMap; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import org.apache.hadoop.ozone.audit.AuditLogger; -import org.apache.hadoop.ozone.audit.OMAction; -import org.apache.hadoop.ozone.om.OzoneManager; -import org.apache.hadoop.ozone.om.exceptions.OMException; -import org.apache.hadoop.ozone.om.execution.flowcontrol.ExecutionContext; -import org.apache.hadoop.ozone.om.ratis.OzoneManagerDoubleBuffer; -import org.apache.hadoop.ozone.om.ratis.OzoneManagerRatisServer; -import org.apache.hadoop.ozone.om.ratis.OzoneManagerStateMachine; -import org.apache.hadoop.ozone.om.request.OMClientRequest; -import org.apache.hadoop.ozone.om.request.util.OmResponseUtil; -import org.apache.hadoop.ozone.om.response.OMClientResponse; -import org.apache.hadoop.ozone.om.response.upgrade.OMPrepareResponse; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareResponse; -import org.apache.hadoop.util.Time; -import org.apache.ratis.server.RaftServer; -import org.apache.ratis.server.raftlog.RaftLog; -import org.apache.ratis.statemachine.StateMachine; -import org.apache.ratis.util.Preconditions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * OM Request used to flush all transactions to disk, take a DB snapshot, and - * purge the logs, leaving Ratis in a clean state without unapplied log - * entries. This prepares the OM for upgrades/downgrades so that no request - * in the log is applied to the database in the old version of the code in one - * OM, and the new version of the code on another OM. - */ -public class OMPrepareRequest extends OMClientRequest { - private static final Logger LOG = - LoggerFactory.getLogger(OMPrepareRequest.class); - - public OMPrepareRequest(OMRequest omRequest) { - super(omRequest); - } - - @Override - public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, ExecutionContext context) { - final long transactionLogIndex = context.getIndex(); - - LOG.info("OM {} Received prepare request with log {}", ozoneManager.getOMNodeId(), context.getTermIndex()); - - OMRequest omRequest = getOmRequest(); - AuditLogger auditLogger = ozoneManager.getAuditLogger(); - OzoneManagerProtocolProtos.UserInfo userInfo = omRequest.getUserInfo(); - OzoneManagerProtocolProtos.PrepareRequestArgs args = - omRequest.getPrepareRequest().getArgs(); - OMResponse.Builder responseBuilder = - OmResponseUtil.getOMResponseBuilder(omRequest); - responseBuilder.setCmdType(Type.Prepare); - OMClientResponse response = null; - Exception exception = null; - - // Allow double buffer this many seconds to flush all transactions before - // returning an error to the caller. - Duration flushTimeout = - Duration.of(args.getTxnApplyWaitTimeoutSeconds(), ChronoUnit.SECONDS); - // Time between checks to see if double buffer finished flushing. - Duration flushCheckInterval = - Duration.of(args.getTxnApplyCheckIntervalSeconds(), ChronoUnit.SECONDS); - - try { - // Create response. - PrepareResponse omResponse = PrepareResponse.newBuilder() - .setTxnID(transactionLogIndex) - .build(); - responseBuilder.setPrepareResponse(omResponse); - response = new OMPrepareResponse(responseBuilder.build(), - transactionLogIndex); - - // Add response to double buffer before clearing logs. - // This guarantees the log index of this request will be the same as - // the snapshot index in the prepared state. - OzoneManagerDoubleBuffer doubleBuffer = - ozoneManager.getOmRatisServer().getOmStateMachine().getOzoneManagerDoubleBuffer(); - doubleBuffer.add(response, context.getTermIndex()); - - OzoneManagerRatisServer omRatisServer = ozoneManager.getOmRatisServer(); - final RaftServer.Division division = omRatisServer.getServerDivision(); - final OzoneManagerStateMachine stateMachine = (OzoneManagerStateMachine) division.getStateMachine(); - - // Wait for outstanding double buffer entries - // - to be flushed to db, and - // - to be notified by Ratis. - // The log index returned, will be used as the prepare index, is the last Ratis commit index - // which can be higher than the transactionLogIndex of this request. - final long prepareIndex = waitForLogIndex(transactionLogIndex, ozoneManager, stateMachine, - flushTimeout, flushCheckInterval); - Preconditions.assertTrue(prepareIndex >= transactionLogIndex); - takeSnapshotAndPurgeLogs(prepareIndex, division); - - // Save prepare index to a marker file, so if the OM restarts, - // it will remain in prepare mode as long as the file exists and its - // log indices are >= the one in the file. - ozoneManager.getPrepareState().finishPrepare(transactionLogIndex); - - LOG.info("OM {} prepared at log index {}. Returning response {} with " + - "log index {}", ozoneManager.getOMNodeId(), transactionLogIndex, - omResponse, omResponse.getTxnID()); - } catch (OMException e) { - exception = e; - LOG.error("Prepare Request Apply failed in {}. ", - ozoneManager.getOMNodeId(), e); - response = new OMPrepareResponse( - createErrorOMResponse(responseBuilder, e)); - } catch (InterruptedException | IOException e) { - // Set error code so that prepare failure does not cause the OM to - // terminate. - exception = e; - LOG.error("Prepare Request Apply failed in {}. ", - ozoneManager.getOMNodeId(), e); - response = new OMPrepareResponse( - createErrorOMResponse(responseBuilder, new OMException(e, - OMException.ResultCodes.PREPARE_FAILED))); - - // Disable prepare gate and attempt to delete prepare marker file. - // Whether marker file delete fails or succeeds, we will return the - // above error response to the caller. - try { - ozoneManager.getPrepareState().cancelPrepare(); - } catch (IOException ex) { - LOG.error("Failed to delete prepare marker file.", ex); - } - if (e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - } - - markForAudit(auditLogger, buildAuditMessage(OMAction.UPGRADE_PREPARE, - new HashMap<>(), exception, userInfo)); - return response; - } - - /** - * Waits for the specified index to be applied to {@link OzoneManagerStateMachine}. - * Note that - * - the applied index is updated after the transaction is flushed to db. - * - after a transaction (i) is committed, ratis will append another ratis-metadata transaction (i+1). - * - * @return the last Ratis applied index - */ - private static long waitForLogIndex(long minOMDBFlushIndex, - OzoneManager om, OzoneManagerStateMachine stateMachine, - Duration flushTimeout, Duration flushCheckInterval) - throws InterruptedException, IOException { - - long endTime = Time.monotonicNow() + flushTimeout.toMillis(); - - boolean omDBFlushed = false; - boolean ratisStateMachineApplied = false; - - // Wait for the given Ratis commit index to be applied to Ratis' - // state machine. This index will not appear in the OM DB until a - // snapshot is taken. - // If we purge logs without waiting for this index, it may not make it to - // the RocksDB snapshot, and then the log entry is lost on this OM. - - // Wait OM state machine to apply the given index. - long lastOMDBFlushIndex = RaftLog.INVALID_LOG_INDEX; - long lastRatisAppliedIndex = RaftLog.INVALID_LOG_INDEX; - - LOG.info("{} waiting for index {} to flush to OM DB and flush" + - " to Ratis state machine.", om.getOMNodeId(), minOMDBFlushIndex); - while (!(omDBFlushed && ratisStateMachineApplied) && - Time.monotonicNow() < endTime) { - // Check OM DB. - lastOMDBFlushIndex = om.getRatisSnapshotIndex(); - omDBFlushed = (lastOMDBFlushIndex >= minOMDBFlushIndex); - LOG.debug("{} Current DB transaction index {}.", om.getOMNodeId(), - lastOMDBFlushIndex); - - // Check ratis state machine. - lastRatisAppliedIndex = stateMachine.getLastAppliedTermIndex().getIndex(); - ratisStateMachineApplied = lastRatisAppliedIndex >= minOMDBFlushIndex; - LOG.debug("{} Current Ratis state machine transaction index {}.", - om.getOMNodeId(), lastRatisAppliedIndex); - - if (!(omDBFlushed && ratisStateMachineApplied)) { - Thread.sleep(flushCheckInterval.toMillis()); - } - } - - // If the timeout waiting for all transactions to reach the state machine - // is exceeded, the exception is propagated, resulting in an error response - // to the client. They can retry the prepare request. - if (!omDBFlushed) { - throw new IOException(String.format("After waiting for %d seconds, " + - "OM database flushed index %d which is less than the minimum " + - "required index %d.", - flushTimeout.getSeconds(), lastOMDBFlushIndex, minOMDBFlushIndex)); - } else if (!ratisStateMachineApplied) { - throw new IOException(String.format("After waiting for %d seconds, " + - "Ratis state machine applied index %d which is less than" + - " the minimum required index %d.", - flushTimeout.getSeconds(), lastRatisAppliedIndex, - minOMDBFlushIndex)); - } - return lastRatisAppliedIndex; - } - - /** - * Take a snapshot of the state machine at the last index, and purge at - * least all log with indices less than or equal to the prepare index. - * If there is another prepare request or cancel prepare request, - * this one will end up purging that request since it was allowed through - * the pre-append prepare gate. - * This means that an OM cannot support 2 prepare requests in the - * transaction pipeline (un-applied) at the same time. - */ - public static void takeSnapshotAndPurgeLogs(long prepareIndex, - RaftServer.Division division) throws IOException { - StateMachine stateMachine = division.getStateMachine(); - long snapshotIndex = stateMachine.takeSnapshot(); - LOG.info("takeSnapshot at {} for prepareIndex {}", snapshotIndex, prepareIndex); - - if (snapshotIndex < prepareIndex) { - throw new IOException(String.format("OM DB snapshot index %d is less " + - "than prepare index %d. Some required logs may not have" + - "been persisted to the state machine.", snapshotIndex, - prepareIndex)); - } - - CompletableFuture purgeFuture = - division.getRaftLog().onSnapshotInstalled(snapshotIndex); - - try { - long actualPurgeIndex = purgeFuture.get(); - - if (actualPurgeIndex != snapshotIndex) { - LOG.warn("Actual purge index {} does not " + - "match specified purge index {}. ", actualPurgeIndex, - snapshotIndex); - } - } catch (ExecutionException e) { - // Ozone manager error handler does not respect exception chaining and - // only displays the message of the top level exception. - throw new IOException("Unable to purge logs: " + e.getMessage()); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IOException("Unable to purge logs: " + e.getMessage()); - } - } -} diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMCancelPrepareResponse.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMCancelPrepareResponse.java deleted file mode 100644 index 8e150d696bd6..000000000000 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMCancelPrepareResponse.java +++ /dev/null @@ -1,46 +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.om.response.upgrade; - -import static org.apache.hadoop.ozone.om.codec.OMDBDefinition.TRANSACTION_INFO_TABLE; - -import java.io.IOException; -import org.apache.hadoop.hdds.utils.db.BatchOperation; -import org.apache.hadoop.ozone.om.OMMetadataManager; -import org.apache.hadoop.ozone.om.response.CleanupTableInfo; -import org.apache.hadoop.ozone.om.response.OMClientResponse; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; - -/** - * Response for cancel prepare. - */ -@CleanupTableInfo(cleanupTables = {TRANSACTION_INFO_TABLE}) -public class OMCancelPrepareResponse extends OMClientResponse { - public OMCancelPrepareResponse( - OzoneManagerProtocolProtos.OMResponse omResponse) { - super(omResponse); - } - - @Override - protected void addToDBBatch(OMMetadataManager omMetadataManager, - BatchOperation batchOperation) throws IOException { - // The cancel prepare request will delete the prepare marker file and - // update in memory state, so this response does not need to perform DB - // update. - } -} diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMPrepareResponse.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMPrepareResponse.java deleted file mode 100644 index d9e8901554b0..000000000000 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMPrepareResponse.java +++ /dev/null @@ -1,58 +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.om.response.upgrade; - -import static org.apache.hadoop.ozone.OzoneConsts.PREPARE_MARKER_KEY; -import static org.apache.hadoop.ozone.om.codec.OMDBDefinition.TRANSACTION_INFO_TABLE; - -import java.io.IOException; -import org.apache.hadoop.hdds.utils.TransactionInfo; -import org.apache.hadoop.hdds.utils.db.BatchOperation; -import org.apache.hadoop.ozone.om.OMMetadataManager; -import org.apache.hadoop.ozone.om.response.CleanupTableInfo; -import org.apache.hadoop.ozone.om.response.OMClientResponse; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; - -/** - * Response for prepare request. - */ -@CleanupTableInfo(cleanupTables = {TRANSACTION_INFO_TABLE}) -public class OMPrepareResponse extends OMClientResponse { - - private long prepareIndex = -1; - - public OMPrepareResponse(OzoneManagerProtocolProtos.OMResponse omResponse, - long prepareIndex) { - super(omResponse); - this.prepareIndex = prepareIndex; - } - - public OMPrepareResponse(OzoneManagerProtocolProtos.OMResponse omResponse) { - super(omResponse); - } - - @Override - protected void addToDBBatch(OMMetadataManager omMetadataManager, - BatchOperation batchOperation) throws IOException { - if (prepareIndex != -1) { - omMetadataManager.getTransactionInfoTable().putWithBatch(batchOperation, - PREPARE_MARKER_KEY, - TransactionInfo.valueOf(TransactionInfo.DEFAULT_VALUE.getTerm(), prepareIndex)); - } - } -} diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java index bea7785bfbc2..3fb4eae52197 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java @@ -62,7 +62,6 @@ import org.apache.hadoop.hdds.utils.FaultInjector; import org.apache.hadoop.ozone.OzoneAcl; import org.apache.hadoop.ozone.om.OzoneManager; -import org.apache.hadoop.ozone.om.OzoneManagerPrepareState; import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.om.execution.flowcontrol.ExecutionContext; import org.apache.hadoop.ozone.om.helpers.BasicOmKeyInfo; @@ -100,6 +99,7 @@ import org.apache.hadoop.ozone.om.response.OMClientResponse; import org.apache.hadoop.ozone.om.upgrade.DisallowedUntilLayoutVersion; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; +import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CancelPrepareResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CancelSnapshotDiffRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CancelSnapshotDiffResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CheckVolumeAccessRequest; @@ -141,6 +141,7 @@ import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneFileStatusProto; +import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrintCompactionLogDagRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrintCompactionLogDagResponse; @@ -301,6 +302,21 @@ public OMResponse handleReadRequest(OMRequest request) { PrepareStatusResponse prepareStatusResponse = getPrepareStatus(); responseBuilder.setPrepareStatusResponse(prepareStatusResponse); break; + case Prepare: + // Prepare is no longer functional but return success for backward compatibility + PrepareResponse prepareResponse = PrepareResponse.newBuilder() + .setTxnID(0) // Dummy transaction ID + .build(); + responseBuilder.setPrepareResponse(prepareResponse); + responseBuilder.setMessage("Prepare is no longer required in this version"); + break; + case CancelPrepare: + // CancelPrepare is no longer functional but return success for backward compatibility + CancelPrepareResponse cancelPrepareResponse = CancelPrepareResponse.newBuilder() + .build(); + responseBuilder.setCancelPrepareResponse(cancelPrepareResponse); + responseBuilder.setMessage("Prepare is no longer required in this version"); + break; case GetS3VolumeContext: GetS3VolumeContextResponse s3VolumeContextResponse = getS3VolumeContext(); @@ -1372,11 +1388,10 @@ private FinalizeUpgradeProgressResponse reportUpgradeProgress( } private PrepareStatusResponse getPrepareStatus() { - OzoneManagerPrepareState.State prepareState = - impl.getPrepareState().getState(); + // Prepare is no longer used, always return NOT_PREPARED return PrepareStatusResponse.newBuilder() - .setStatus(prepareState.getStatus()) - .setCurrentTxnIndex(prepareState.getIndex()).build(); + .setStatus(PrepareStatusResponse.PrepareStatus.NOT_PREPARED) + .setCurrentTxnIndex(0).build(); } private GetS3VolumeContextResponse getS3VolumeContext() diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerStarter.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerStarter.java index 5ad57731728f..381e0de11b13 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerStarter.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerStarter.java @@ -108,18 +108,6 @@ public void testInitThatReturnsFalseThrowsException() { assertEquals(EXECUTION_ERROR_EXIT_CODE, executeCommand("--init")); } - @Test - public void testCallsStartAndCancelPrepareWithUpgradeFlag() { - assertEquals(OK, executeCommand("--upgrade")); - assertTrue(mock.startAndCancelPrepareCalled); - } - - @Test - public void testUnsuccessfulUpgradeThrowsException() { - mock.throwOnStartAndCancelPrepare = true; - assertEquals(EXECUTION_ERROR_EXIT_CODE, executeCommand("--upgrade")); - } - @Test public void testUsagePrintedOnInvalidInput() throws UnsupportedEncodingException { @@ -140,8 +128,6 @@ static class MockOMStarter implements OMStarterInterface { private boolean initStatus = true; private boolean throwOnStart = false; private boolean throwOnInit = false; - private boolean startAndCancelPrepareCalled = false; - private boolean throwOnStartAndCancelPrepare = false; @Override public void start(OzoneConfiguration conf) throws IOException, @@ -167,14 +153,5 @@ public void bootstrap(OzoneConfiguration conf, boolean force) throws IOException, AuthenticationException { //TODO: Add test for bootstrap } - - @Override - public void startAndCancelPrepare(OzoneConfiguration conf) - throws IOException, AuthenticationException { - startAndCancelPrepareCalled = true; - if (throwOnStartAndCancelPrepare) { - throw new IOException("Simulated Exception"); - } - } } } diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerStateMachine.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerStateMachine.java index 797f6aa2c3e3..782cb2fb9a2c 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerStateMachine.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerStateMachine.java @@ -18,10 +18,6 @@ package org.apache.hadoop.ozone.om.ratis; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -37,22 +33,14 @@ import org.apache.hadoop.ozone.om.OmConfig; import org.apache.hadoop.ozone.om.OmMetadataManagerImpl; import org.apache.hadoop.ozone.om.OzoneManager; -import org.apache.hadoop.ozone.om.OzoneManagerPrepareState; import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.om.helpers.OMRatisHelper; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateKeyRequest; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.KeyArgs; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareRequest; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareRequestArgs; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.UserInfo; -import org.apache.hadoop.security.UserGroupInformation; import org.apache.ratis.proto.RaftProtos; import org.apache.ratis.protocol.Message; -import org.apache.ratis.protocol.exceptions.StateMachineException; import org.apache.ratis.server.protocol.TermIndex; import org.apache.ratis.server.raftlog.LogProtoUtils; import org.apache.ratis.statemachine.TransactionContext; @@ -72,7 +60,6 @@ public class TestOzoneManagerStateMachine { private Path tempDir; private OzoneManagerStateMachine ozoneManagerStateMachine; - private OzoneManagerPrepareState prepareState; private AuditLogger auditLogger; @BeforeEach @@ -80,9 +67,6 @@ public void setup() throws Exception { OzoneManagerRatisServer ozoneManagerRatisServer = mock(OzoneManagerRatisServer.class); OzoneManager ozoneManager = mock(OzoneManager.class); - // Allow testing of prepare pre-append gate. - when(ozoneManager.isAdmin(any(UserGroupInformation.class))) - .thenReturn(true); OzoneConfiguration conf = new OzoneConfiguration(); conf.set(OMConfigKeys.OZONE_OM_DB_DIRS, @@ -95,8 +79,6 @@ public void setup() throws Exception { auditLogger = mock(AuditLogger.class); when(ozoneManager.getAuditLogger()).thenReturn(auditLogger); - prepareState = new OzoneManagerPrepareState(conf); - when(ozoneManager.getPrepareState()).thenReturn(prepareState); when(ozoneManagerRatisServer.getOzoneManager()).thenReturn(ozoneManager); when(ozoneManager.getTransactionInfo()).thenReturn(mock(TransactionInfo.class)); @@ -164,77 +146,6 @@ public void testNotifyTermIndexPendingBufferUpdateIndex() { assertTermIndex(0, 3, ozoneManagerStateMachine.getLastNotifiedTermIndex()); } - @Test - public void testPreAppendTransaction() throws Exception { - // Submit write request. - KeyArgs args = KeyArgs - .newBuilder() - .setVolumeName("volume") - .setBucketName("bucket") - .setKeyName("key") - .build(); - OMRequest createKeyRequest = OMRequest.newBuilder() - .setCreateKeyRequest(CreateKeyRequest.newBuilder().setKeyArgs(args)) - .setCmdType(Type.CreateKey) - .setClientId("123") - .setUserInfo(UserInfo - .newBuilder() - .setUserName("user") - .setHostName("localhost") - .setRemoteAddress("127.0.0.1")) - .build(); - // Without prepare enabled, the txn should be returned unaltered. - TransactionContext submittedTrx = mockTransactionContext(createKeyRequest); - TransactionContext returnedTrx = - ozoneManagerStateMachine.preAppendTransaction(submittedTrx); - assertSame(submittedTrx, returnedTrx); - - assertEquals(PrepareStatus.NOT_PREPARED, prepareState.getState().getStatus()); - - // Submit prepare request. - OMRequest prepareRequest = OMRequest.newBuilder() - .setPrepareRequest( - PrepareRequest.newBuilder() - .setArgs(PrepareRequestArgs.getDefaultInstance())) - .setCmdType(Type.Prepare) - .setClientId("123") - .setUserInfo(UserInfo - .newBuilder() - .setUserName("user") - .setHostName("localhost") - .setRemoteAddress("127.0.0.1")) - .build(); - - submittedTrx = mockTransactionContext(prepareRequest); - returnedTrx = ozoneManagerStateMachine.preAppendTransaction(submittedTrx); - assertSame(submittedTrx, returnedTrx); - - // Prepare should be started. - assertEquals(PrepareStatus.PREPARE_GATE_ENABLED, - prepareState.getState().getStatus()); - - // Submitting a write request should now fail. - StateMachineException smEx = - assertThrows(StateMachineException.class, - () -> ozoneManagerStateMachine.preAppendTransaction(mockTransactionContext(createKeyRequest)), - "Expected StateMachineException to be thrown when submitting write request while prepared."); - assertFalse(smEx.leaderShouldStepDown()); - - Throwable cause = smEx.getCause(); - OMException omException = assertInstanceOf(OMException.class, cause); - assertEquals(omException.getResult(), OMException.ResultCodes.NOT_SUPPORTED_OPERATION_WHEN_PREPARED); - - // Should be able to prepare again without issue. - submittedTrx = mockTransactionContext(prepareRequest); - returnedTrx = ozoneManagerStateMachine.preAppendTransaction(submittedTrx); - assertSame(submittedTrx, returnedTrx); - - assertEquals(PrepareStatus.PREPARE_GATE_ENABLED, prepareState.getState().getStatus()); - - // Cancel prepare is handled in the cancel request apply txn step, not - // the pre-append state machine step, so it is tested in other classes. - } - @Test public void testApplyTransactionExceptionAuditLog() throws Exception { ExitUtils.disableSystemExit(); diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyRequest.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyRequest.java index b84294370c58..7d2b92168b00 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyRequest.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyRequest.java @@ -71,7 +71,6 @@ import org.apache.hadoop.ozone.om.OmMetadataReader; import org.apache.hadoop.ozone.om.OmSnapshotManager; import org.apache.hadoop.ozone.om.OzoneManager; -import org.apache.hadoop.ozone.om.OzoneManagerPrepareState; import org.apache.hadoop.ozone.om.ResolvedBucket; import org.apache.hadoop.ozone.om.ScmClient; import org.apache.hadoop.ozone.om.helpers.BucketLayout; @@ -110,7 +109,6 @@ public class TestOMKeyRequest { protected OMMetrics omMetrics; protected OMMetadataManager omMetadataManager; protected AuditLogger auditLogger; - protected OzoneManagerPrepareState prepareState; protected ScmClient scmClient; protected OzoneBlockTokenSecretManager ozoneBlockTokenSecretManager; @@ -204,9 +202,6 @@ public void setup() throws Exception { when(omMetadataReader.isNativeAuthorizerEnabled()).thenReturn(true); when(rcOmMetadataReader.get()).thenReturn(omMetadataReader); - prepareState = new OzoneManagerPrepareState(ozoneConfiguration); - when(ozoneManager.getPrepareState()).thenReturn(prepareState); - Pipeline pipeline = Pipeline.newBuilder() .setState(Pipeline.PipelineState.OPEN) .setId(PipelineID.randomId()) diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/upgrade/TestOMCancelPrepareRequest.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/upgrade/TestOMCancelPrepareRequest.java deleted file mode 100644 index e36573bc3262..000000000000 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/upgrade/TestOMCancelPrepareRequest.java +++ /dev/null @@ -1,122 +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.om.request.upgrade; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.UUID; -import org.apache.hadoop.ozone.om.OzoneManagerPrepareState; -import org.apache.hadoop.ozone.om.request.key.OMOpenKeysDeleteRequest; -import org.apache.hadoop.ozone.om.request.key.TestOMKeyRequest; -import org.apache.hadoop.ozone.om.response.OMClientResponse; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type; -import org.junit.jupiter.api.Test; - -/** - * Unit testing of cancel prepare request. Cancel prepare response does not - * perform an action, so it has no unit testing. - */ -public class TestOMCancelPrepareRequest extends TestOMKeyRequest { - private static final long LOG_INDEX = 1; - - @Test - public void testCancelPrepare() throws Exception { - assertNotPrepared(); - ozoneManager.getPrepareState().finishPrepare(LOG_INDEX); - assertPrepared(LOG_INDEX); - submitCancelPrepareRequest(); - assertNotPrepared(); - - // Another cancel prepare should be able to be submitted without error. - submitCancelPrepareRequest(); - assertNotPrepared(); - } - - private void assertPrepared(long logIndex) { - OzoneManagerPrepareState prepareState = ozoneManager.getPrepareState(); - OzoneManagerPrepareState.State state = prepareState.getState(); - - assertEquals(state.getStatus(), PrepareStatus.PREPARE_COMPLETED); - assertEquals(state.getIndex(), logIndex); - assertTrue(prepareState.getPrepareMarkerFile().exists()); - assertFalse(prepareState.requestAllowed(Type.CreateVolume)); - } - - private void assertNotPrepared() { - OzoneManagerPrepareState prepareState = ozoneManager.getPrepareState(); - OzoneManagerPrepareState.State state = prepareState.getState(); - - assertEquals(state.getStatus(), PrepareStatus.NOT_PREPARED); - assertEquals(state.getIndex(), - OzoneManagerPrepareState.NO_PREPARE_INDEX); - assertFalse(prepareState.getPrepareMarkerFile().exists()); - assertTrue(prepareState.requestAllowed(Type.CreateVolume)); - } - - private void submitCancelPrepareRequest() throws Exception { - OMRequest omRequest = - doPreExecute(createCancelPrepareRequest()); - - OMCancelPrepareRequest cancelPrepareRequest = - new OMCancelPrepareRequest(omRequest); - - OMClientResponse omClientResponse = - cancelPrepareRequest.validateAndUpdateCache(ozoneManager, LOG_INDEX); - - assertEquals(OzoneManagerProtocolProtos.Status.OK, - omClientResponse.getOMResponse().getStatus()); - } - - private OMRequest doPreExecute(OMRequest originalOmRequest) throws Exception { - OMOpenKeysDeleteRequest omOpenKeysDeleteRequest = - new OMOpenKeysDeleteRequest(originalOmRequest, getBucketLayout()); - - OMRequest modifiedOmRequest = - omOpenKeysDeleteRequest.preExecute(ozoneManager); - - // Will not be equal, as UserInfo will be set. - assertNotEquals(originalOmRequest, modifiedOmRequest); - - return modifiedOmRequest; - } - - private OMRequest createCancelPrepareRequest() { - OzoneManagerProtocolProtos.CancelPrepareRequest cancelPrepareRequest = - OzoneManagerProtocolProtos.CancelPrepareRequest.newBuilder().build(); - - OzoneManagerProtocolProtos.UserInfo userInfo = - OzoneManagerProtocolProtos.UserInfo.newBuilder() - .setUserName("user") - .setHostName("host") - .setRemoteAddress("0.0.0.0") - .build(); - - return OMRequest.newBuilder() - .setCancelPrepareRequest(cancelPrepareRequest) - .setCmdType(OzoneManagerProtocolProtos.Type.CancelPrepare) - .setClientId(UUID.randomUUID().toString()) - .setUserInfo(userInfo) - .build(); - } -} diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOzoneManagerPrepareState.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOzoneManagerPrepareState.java deleted file mode 100644 index 800549ad9182..000000000000 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOzoneManagerPrepareState.java +++ /dev/null @@ -1,287 +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.om.upgrade; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Random; -import org.apache.hadoop.hdds.HddsConfigKeys; -import org.apache.hadoop.hdds.conf.OzoneConfiguration; -import org.apache.hadoop.ozone.om.OzoneManagerPrepareState; -import org.apache.hadoop.ozone.om.exceptions.OMException; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type; -import org.apache.ozone.test.LambdaTestUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -/** - * Class to test Ozone Manager prepare state maintenance. - */ -public class TestOzoneManagerPrepareState { - @TempDir - private Path folder; - - private static final int TEST_INDEX = 5; - private OzoneManagerPrepareState prepareState; - private static final Random RANDOM = new Random(); - - @BeforeEach - public void setup() throws Exception { - OzoneConfiguration conf = new OzoneConfiguration(); - conf.set(HddsConfigKeys.OZONE_METADATA_DIRS, - folder.toAbsolutePath().toString()); - - prepareState = new OzoneManagerPrepareState(conf); - } - - @AfterEach - public void tearDown() throws Exception { - // Clean up marker file from previous runs. - prepareState.cancelPrepare(); - } - - @Test - public void testStartPrepare() { - assertPrepareNotStarted(); - prepareState.enablePrepareGate(); - assertPrepareInProgress(); - } - - @Test - public void testFinishPrepare() throws Exception { - // Test finish as a follower OM, where it would be called without setting - // the prepare gate first. - prepareState.finishPrepare(TEST_INDEX); - assertPrepareCompleted(TEST_INDEX); - prepareState.cancelPrepare(); - - // Test finish as a leader OM, where the prepare gate would be put up - // before prepare is finished. - prepareState.enablePrepareGate(); - prepareState.finishPrepare(TEST_INDEX); - assertPrepareCompleted(TEST_INDEX); - - // A following prepare operation should not raise errors. - prepareState.finishPrepare(TEST_INDEX); - assertPrepareCompleted(TEST_INDEX); - } - - @Test - public void testCancelPrepare() throws Exception { - // Test cancel after start. - prepareState.enablePrepareGate(); - prepareState.cancelPrepare(); - assertPrepareNotStarted(); - - // Test cancel after finish. - prepareState.enablePrepareGate(); - prepareState.finishPrepare(TEST_INDEX); - prepareState.cancelPrepare(); - assertPrepareNotStarted(); - - // Test cancel after cancel. - prepareState.cancelPrepare(); - prepareState.cancelPrepare(); - assertPrepareNotStarted(); - - } - - @Test - public void testRequestAllowed() { - assertPrepareGateDown(); - prepareState.enablePrepareGate(); - assertPrepareGateUp(); - } - - @Test - public void testRestoreCorrectIndex() throws Exception { - writePrepareMarkerFile(TEST_INDEX); - prepareState.restorePrepareFromFile(TEST_INDEX); - assertPrepareCompleted(TEST_INDEX); - } - - @Test - public void testRestoreIncorrectIndex() throws Exception { - writePrepareMarkerFile(TEST_INDEX); - // Ratis is allowed to apply transactions after prepare, like commit and - // conf entries, but OM write requests are not allowed. - // Therefore prepare restoration should only fail if the marker file index - // is less than the OM's txn index. - assertPrepareFailedException(() -> - prepareState.restorePrepareFromFile(TEST_INDEX - 1)); - } - - @Test - public void testRestoreGarbageMarkerFile() throws Exception { - byte[] randomBytes = new byte[10]; - RANDOM.nextBytes(randomBytes); - writePrepareMarkerFile(randomBytes); - - assertPrepareFailedException(() -> - prepareState.restorePrepareFromFile(TEST_INDEX)); - } - - @Test - public void testRestoreEmptyMarkerFile() throws Exception { - writePrepareMarkerFile(new byte[]{}); - - assertPrepareFailedException(() -> - prepareState.restorePrepareFromFile(TEST_INDEX)); - } - - @Test - public void testRestoreNoMarkerFile() throws Exception { - assertPrepareFailedException(() -> - prepareState.restorePrepareFromFile(TEST_INDEX)); - assertPrepareNotStarted(); - } - - @Test - public void testRestoreWithGateOnly() throws Exception { - prepareState.enablePrepareGate(); - - // If prepare is started but never finished, (gate is up but no marker - // file written), then restoring the prepare state from the file should - // fail, but leave the in memory state the same. - assertPrepareFailedException(() -> - prepareState.restorePrepareFromFile(TEST_INDEX)); - assertPrepareInProgress(); - } - - @Test - public void testMultipleRestores() throws Exception { - assertPrepareFailedException(() -> - prepareState.restorePrepareFromFile(TEST_INDEX)); - assertPrepareNotStarted(); - - assertPrepareFailedException(() -> - prepareState.restorePrepareFromFile(TEST_INDEX)); - assertPrepareNotStarted(); - - prepareState.enablePrepareGate(); - prepareState.finishPrepare(TEST_INDEX); - - prepareState.restorePrepareFromFile(TEST_INDEX); - assertPrepareCompleted(TEST_INDEX); - - prepareState.restorePrepareFromFile(TEST_INDEX); - assertPrepareCompleted(TEST_INDEX); - } - - private void writePrepareMarkerFile(long index) throws IOException { - writePrepareMarkerFile(Long.toString(index).getBytes( - Charset.defaultCharset())); - } - - private void writePrepareMarkerFile(byte[] bytes) throws IOException { - File markerFile = prepareState.getPrepareMarkerFile(); - boolean mkdirs = markerFile.getParentFile().mkdirs(); - if (!mkdirs) { - throw new IOException("Unable to create marker file directory."); - } - try (OutputStream stream = - Files.newOutputStream(markerFile.toPath())) { - stream.write(bytes); - } - } - - private long readPrepareMarkerFile() throws Exception { - long index = 0; - File prepareMarkerFile = prepareState.getPrepareMarkerFile(); - byte[] data = new byte[(int) prepareMarkerFile.length()]; - - try (InputStream stream = Files.newInputStream(prepareMarkerFile.toPath())) { - stream.read(data); - index = Long.parseLong(new String(data, Charset.defaultCharset())); - } - - return index; - } - - private void assertPrepareNotStarted() { - OzoneManagerPrepareState.State state = prepareState.getState(); - assertEquals(PrepareStatus.NOT_PREPARED, state.getStatus()); - assertEquals(OzoneManagerPrepareState.NO_PREPARE_INDEX, - state.getIndex()); - assertFalse(prepareState.getPrepareMarkerFile().exists()); - - assertPrepareGateDown(); - } - - private void assertPrepareInProgress() { - OzoneManagerPrepareState.State state = prepareState.getState(); - assertEquals(PrepareStatus.PREPARE_GATE_ENABLED, - state.getStatus()); - assertEquals(OzoneManagerPrepareState.NO_PREPARE_INDEX, - state.getIndex()); - assertFalse(prepareState.getPrepareMarkerFile().exists()); - - assertPrepareGateUp(); - } - - private void assertPrepareCompleted(long index) throws Exception { - OzoneManagerPrepareState.State state = prepareState.getState(); - assertEquals(PrepareStatus.PREPARE_COMPLETED, - state.getStatus()); - assertEquals(index, state.getIndex()); - assertEquals(index, readPrepareMarkerFile()); - - assertPrepareGateUp(); - } - - private void assertPrepareGateUp() { - // Once preparation has begun, only prepare and cancel prepare should be - // allowed. - for (Type cmdType: Type.values()) { - if (cmdType == Type.Prepare || cmdType == Type.CancelPrepare) { - assertTrue(prepareState.requestAllowed(cmdType)); - } else { - assertFalse(prepareState.requestAllowed(cmdType)); - } - } - } - - private void assertPrepareGateDown() { - for (Type cmdType: Type.values()) { - assertTrue(prepareState.requestAllowed(cmdType)); - } - } - - private void assertPrepareFailedException(LambdaTestUtils.VoidCallable call) - throws Exception { - try { - call.call(); - } catch (OMException ex) { - if (ex.getResult() != OMException.ResultCodes.PREPARE_FAILED) { - throw ex; - } - } - } -} From f61268e2470af6d4725461dba1fcdabc7ae7d291 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Fri, 6 Feb 2026 17:49:21 -0500 Subject: [PATCH 02/22] Add initial draft of tests Generated-By: Cursor --- .../hadoop/ozone/shell/TestOzoneShellHA.java | 48 +++++++++++++ .../TestOzoneManagerRequestHandler.java | 68 +++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java index 743214d72f4d..ccbe09962ca5 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java @@ -2478,6 +2478,54 @@ public void testKeyDeleteLegacyWithEnableFileSystemPath() throws IOException { execute(ozoneShell, args); } + /** + * Test that prepare commands are deprecated but still work and don't block writes. + */ + @Test + public void testPrepareCommandDoesNotBlockWrites() throws IOException { + String volumeName = "vol-prepare-test"; + String bucketName = "bucket-prepare-test"; + + // Execute prepare command via CLI + String[] args = new String[] {"om", "prepare", "--service-id", omServiceId}; + execute(ozoneAdminShell, args); + out.reset(); + + // Verify write operations still work (prepare should not block them) + // Create volume + args = new String[] {"volume", "create", "o3://" + omServiceId + OZONE_URI_DELIMITER + volumeName}; + execute(ozoneShell, args); + out.reset(); + + // Create bucket + args = new String[] {"bucket", "create", "o3://" + omServiceId + OZONE_URI_DELIMITER + + volumeName + OZONE_URI_DELIMITER + bucketName}; + execute(ozoneShell, args); + out.reset(); + + // Create key + String keyName = OZONE_URI_DELIMITER + volumeName + OZONE_URI_DELIMITER + + bucketName + OZONE_URI_DELIMITER + "testkey"; + args = new String[] {"key", "put", "o3://" + omServiceId + keyName, testFile.getPath()}; + execute(ozoneShell, args); + out.reset(); + + // Verify the key was created successfully + OzoneVolume volume = client.getObjectStore().getVolume(volumeName); + OzoneBucket bucket = volume.getBucket(bucketName); + assertNotNull(bucket.getKey("testkey"), "Key should be created successfully after prepare"); + + // Execute cancelprepare command via CLI + args = new String[] {"om", "cancelprepare", "--service-id", omServiceId}; + execute(ozoneAdminShell, args); + out.reset(); + + // Cleanup + args = new String[] {"volume", "delete", volumeName, "-r", "--yes"}; + execute(ozoneShell, args); + out.reset(); + } + private static String getKeyProviderURI(MiniKMS kms) { if (kms == null) { return ""; diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java index a35551e246cc..cb3b7622279e 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java @@ -242,4 +242,72 @@ public void testListKeysLightEncryptionFromOmKeyInfo() throws IOException { Assertions.assertTrue(basicKeyInfoList.get(0).getIsEncrypted(), "encrypted-key should have isEncrypted=true"); Assertions.assertFalse(basicKeyInfoList.get(1).getIsEncrypted(), "normal-key should have isEncrypted=false"); } + + /** + * Test to verify prepare-related requests return success as no-ops. + */ + @Test + public void testPrepareRequestsAreNoOps() { + OzoneManagerRequestHandler requestHandler = getRequestHandler(10); + + // Prepare command should be a no-op. + OzoneManagerProtocolProtos.OMRequest prepareRequest = + OzoneManagerProtocolProtos.OMRequest.newBuilder() + .setCmdType(OzoneManagerProtocolProtos.Type.Prepare) + .setClientId("test-client") + .setPrepareRequest(OzoneManagerProtocolProtos.PrepareRequest.newBuilder() + .setArgs(OzoneManagerProtocolProtos.PrepareRequestArgs.newBuilder().build()) + .build()) + .build(); + + OzoneManagerProtocolProtos.OMResponse prepareResponse = + requestHandler.handleReadRequest(prepareRequest); + + Assertions.assertTrue(prepareResponse.getSuccess(), "Prepare should return success"); + Assertions.assertTrue(prepareResponse.hasPrepareResponse(), "Prepare response should be present"); + Assertions.assertEquals(0, prepareResponse.getPrepareResponse().getTxnID(), + "Prepare should return empty txnID of 0"); + Assertions.assertEquals("Prepare is no longer required in this version", + prepareResponse.getMessage(), "Prepare should return deprecation message"); + + // PrepareStatus should always return NOT_PREPARED. + OzoneManagerProtocolProtos.OMRequest prepareStatusRequest = + OzoneManagerProtocolProtos.OMRequest.newBuilder() + .setCmdType(OzoneManagerProtocolProtos.Type.PrepareStatus) + .setClientId("test-client") + .setPrepareStatusRequest(OzoneManagerProtocolProtos.PrepareStatusRequest.newBuilder() + .setTxnID(0) + .build()) + .build(); + + OzoneManagerProtocolProtos.OMResponse prepareStatusResponse = + requestHandler.handleReadRequest(prepareStatusRequest); + + Assertions.assertTrue(prepareStatusResponse.getSuccess(), "PrepareStatus should return success"); + Assertions.assertTrue(prepareStatusResponse.hasPrepareStatusResponse(), + "PrepareStatus response should be present"); + Assertions.assertEquals( + OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus.NOT_PREPARED, + prepareStatusResponse.getPrepareStatusResponse().getStatus(), + "PrepareStatus should always return NOT_PREPARED"); + Assertions.assertEquals(0, prepareStatusResponse.getPrepareStatusResponse().getCurrentTxnIndex(), + "PrepareStatus should return currentTxnIndex of 0"); + + // CancelPrepare command should be a no-op. + OzoneManagerProtocolProtos.OMRequest cancelPrepareRequest = + OzoneManagerProtocolProtos.OMRequest.newBuilder() + .setCmdType(OzoneManagerProtocolProtos.Type.CancelPrepare) + .setClientId("test-client") + .setCancelPrepareRequest(OzoneManagerProtocolProtos.CancelPrepareRequest.newBuilder().build()) + .build(); + + OzoneManagerProtocolProtos.OMResponse cancelPrepareResponse = + requestHandler.handleReadRequest(cancelPrepareRequest); + + Assertions.assertTrue(cancelPrepareResponse.getSuccess(), "CancelPrepare should return success"); + Assertions.assertTrue(cancelPrepareResponse.hasCancelPrepareResponse(), + "CancelPrepare response should be present"); + Assertions.assertEquals("Prepare is no longer required in this version", + cancelPrepareResponse.getMessage(), "CancelPrepare should return deprecation message"); + } } From 23b652103fdeb62a5b031fb8a39cae948d75fd89 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Mon, 9 Feb 2026 14:26:47 -0500 Subject: [PATCH 03/22] Make prepare_completed the default state --- .../hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java | 4 ++-- .../ozone/protocolPB/TestOzoneManagerRequestHandler.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java index 3fb4eae52197..9aa6425d5d88 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java @@ -1388,9 +1388,9 @@ private FinalizeUpgradeProgressResponse reportUpgradeProgress( } private PrepareStatusResponse getPrepareStatus() { - // Prepare is no longer used, always return NOT_PREPARED + // Prepare is no longer used, always return PREPARE_COMPLETED for backward compatibility return PrepareStatusResponse.newBuilder() - .setStatus(PrepareStatusResponse.PrepareStatus.NOT_PREPARED) + .setStatus(PrepareStatusResponse.PrepareStatus.PREPARE_COMPLETED) .setCurrentTxnIndex(0).build(); } diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java index cb3b7622279e..21dc43e8a6cd 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java @@ -270,7 +270,7 @@ public void testPrepareRequestsAreNoOps() { Assertions.assertEquals("Prepare is no longer required in this version", prepareResponse.getMessage(), "Prepare should return deprecation message"); - // PrepareStatus should always return NOT_PREPARED. + // PrepareStatus should always return PREPARE_COMPLETED. OzoneManagerProtocolProtos.OMRequest prepareStatusRequest = OzoneManagerProtocolProtos.OMRequest.newBuilder() .setCmdType(OzoneManagerProtocolProtos.Type.PrepareStatus) @@ -287,9 +287,9 @@ public void testPrepareRequestsAreNoOps() { Assertions.assertTrue(prepareStatusResponse.hasPrepareStatusResponse(), "PrepareStatus response should be present"); Assertions.assertEquals( - OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus.NOT_PREPARED, + OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus.PREPARE_COMPLETED, prepareStatusResponse.getPrepareStatusResponse().getStatus(), - "PrepareStatus should always return NOT_PREPARED"); + "PrepareStatus should always return PREPARE_COMPLETED for backward compatibility"); Assertions.assertEquals(0, prepareStatusResponse.getPrepareStatusResponse().getCurrentTxnIndex(), "PrepareStatus should return currentTxnIndex of 0"); From 530e096a624d5f264bf821cf21c64a28d6654d7c Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Tue, 17 Feb 2026 17:37:39 -0500 Subject: [PATCH 04/22] Update CLI and request comments/descriptions --- .../apache/hadoop/ozone/admin/om/CancelPrepareSubCommand.java | 3 ++- .../org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java | 3 ++- .../common/src/main/java/org/apache/hadoop/ozone/OmUtils.java | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/CancelPrepareSubCommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/CancelPrepareSubCommand.java index d1a89c625353..7e859c604e6f 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/CancelPrepareSubCommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/CancelPrepareSubCommand.java @@ -27,7 +27,8 @@ */ @CommandLine.Command( name = "cancelprepare", - description = "Deprecated. Kept for backward compatibility.", + description = "Deprecated. This command is no longer required, but is kept for backward compatibility with older" + + " Ozone Managers.", hidden = true, mixinStandardHelpOptions = true, versionProvider = HddsVersionProvider.class diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java index bde177f13f3a..ca588026f6b6 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java @@ -40,7 +40,8 @@ */ @CommandLine.Command( name = "prepare", - description = "Deprecated. Kept for backward compatibility with older server versions.", + description = "Deprecated. This command is no longer required, but is kept for backward compatibility with older" + + " Ozone Managers.", hidden = true, mixinStandardHelpOptions = true, versionProvider = HddsVersionProvider.class diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java index c96fc15f1ea9..d2f0e071236f 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java @@ -281,6 +281,7 @@ public static boolean isReadOnly(OMRequest omRequest) { case GetObjectTagging: case GetQuotaRepairStatus: case StartQuotaRepair: + // Prepare and CancelPrepare are now no-ops, but still produce responses for compatability. case Prepare: case CancelPrepare: return true; From b9f97ea071061856ce614430bf62ca53942dee88 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Tue, 17 Feb 2026 17:38:10 -0500 Subject: [PATCH 05/22] Fix acceptance test files --- .../src/main/compose/ozone-om-prepare/.env | 25 ---- .../ozone-om-prepare/docker-compose.yaml | 126 ------------------ .../compose/ozone-om-prepare/docker-config | 37 ----- .../src/main/smoketest/upgrade/prepare.robot | 30 +++++ 4 files changed, 30 insertions(+), 188 deletions(-) delete mode 100644 hadoop-ozone/dist/src/main/compose/ozone-om-prepare/.env delete mode 100644 hadoop-ozone/dist/src/main/compose/ozone-om-prepare/docker-compose.yaml delete mode 100644 hadoop-ozone/dist/src/main/compose/ozone-om-prepare/docker-config create mode 100644 hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot diff --git a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/.env b/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/.env deleted file mode 100644 index dd37cf578302..000000000000 --- a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/.env +++ /dev/null @@ -1,25 +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. - -HDDS_VERSION=${hdds.version} -OZONE_RUNNER_VERSION=${docker.ozone-runner.version} -OZONE_RUNNER_IMAGE=apache/ozone-runner -OZONE_DIR=/opt/hadoop -OZONE_VOLUME=. -# Indicates no arguments to the OM. -# This variable must be set to some non-empty value, or docker compose will -# expand it to an empty string and pass that to the OM as an argument. -OM_HA_ARGS=-- diff --git a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/docker-compose.yaml b/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/docker-compose.yaml deleted file mode 100644 index 228f78c968be..000000000000 --- a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/docker-compose.yaml +++ /dev/null @@ -1,126 +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. - -# reusable fragments (see https://docs.docker.com/compose/compose-file/#extension-fields) -x-common-config: - &common-config - env_file: - - docker-config - image: ${OZONE_RUNNER_IMAGE}:${OZONE_RUNNER_VERSION} - -x-replication: - &replication - OZONE-SITE.XML_ozone.server.default.replication: ${OZONE_REPLICATION_FACTOR:-1} - -x-datanode: - &datanode - command: ["ozone","datanode"] - <<: *common-config - environment: - <<: *replication - ports: - - 19864 - - 9882 - -x-om: - &om - command: ["ozone","om","${OM_HA_ARGS}"] - <<: *common-config - environment: - ENSURE_OM_INITIALIZED: /data/metadata/om/current/VERSION - <<: *replication - ports: - - 9862 - - 9872 - -services: - dn1: - <<: *datanode - hostname: dn1 - networks: - net: - ipv4_address: 10.9.0.11 - volumes: - - ${OZONE_VOLUME}/dn1:/data - - ../..:${OZONE_DIR} - dn2: - <<: *datanode - hostname: dn2 - networks: - net: - ipv4_address: 10.9.0.12 - volumes: - - ${OZONE_VOLUME}/dn2:/data - - ../..:${OZONE_DIR} - dn3: - <<: *datanode - hostname: dn3 - networks: - net: - ipv4_address: 10.9.0.13 - volumes: - - ${OZONE_VOLUME}/dn3:/data - - ../..:${OZONE_DIR} - - om1: - <<: *om - networks: - net: - ipv4_address: 10.9.0.14 - volumes: - - ${OZONE_VOLUME}/om1:/data - - ../..:${OZONE_DIR} - - om2: - <<: *om - networks: - net: - ipv4_address: 10.9.0.15 - volumes: - - ${OZONE_VOLUME}/om2:/data - - ../..:${OZONE_DIR} - - om3: - <<: *om - networks: - net: - ipv4_address: 10.9.0.16 - volumes: - - ${OZONE_VOLUME}/om3:/data - - ../..:${OZONE_DIR} - - scm: - command: ["ozone","scm"] - <<: *common-config - networks: - net: - ipv4_address: 10.9.0.17 - environment: - ENSURE_SCM_INITIALIZED: /data/metadata/scm/current/VERSION - OZONE-SITE.XML_hdds.scm.safemode.min.datanode: ${OZONE_SAFEMODE_MIN_DATANODES:-1} - <<: *replication - ports: - - 9876:9876 - volumes: - - ${OZONE_VOLUME}/scm:/data - - ../..:${OZONE_DIR} - -networks: - net: - driver: bridge - ipam: - config: - - subnet: 10.9.0.0/16 diff --git a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/docker-config b/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/docker-config deleted file mode 100644 index b197e9a7f7e4..000000000000 --- a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/docker-config +++ /dev/null @@ -1,37 +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. - -CORE-SITE.XML_fs.defaultFS=ofs://omservice/ - -OZONE-SITE.XML_ozone.om.service.ids=omservice -OZONE-SITE.XML_ozone.om.nodes.omservice=om1,om2,om3 -OZONE-SITE.XML_ozone.om.address.omservice.om1=om1 -OZONE-SITE.XML_ozone.om.address.omservice.om2=om2 -OZONE-SITE.XML_ozone.om.address.omservice.om3=om3 - -OZONE-SITE.XML_ozone.scm.names=scm -OZONE-SITE.XML_ozone.scm.datanode.id.dir=/data/metadata -OZONE-SITE.XML_ozone.scm.block.client.address=scm -OZONE-SITE.XML_ozone.scm.container.size=1GB -OZONE-SITE.XML_ozone.metadata.dirs=/data/metadata -OZONE-SITE.XML_ozone.scm.client.address=scm -OZONE-SITE.XML_ozone.client.failover.max.attempts=6 -OZONE-SITE.XML_hdds.datanode.dir=/data/hdds -OZONE-SITE.XML_hdds.datanode.volume.min.free.space=100MB -OZONE-SITE.XML_hdds.datanode.volume.min.free.space.percent=0 -OZONE-SITE.XML_ozone.http.basedir=/tmp/ozone_http - -no_proxy=om1,om2,om3,scm,s3g,recon,kdc,localhost,127.0.0.1 diff --git a/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot b/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot new file mode 100644 index 000000000000..6004bd9a07cd --- /dev/null +++ b/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot @@ -0,0 +1,30 @@ +# 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. + +*** Settings *** +Documentation Prepares OMs +Resource ../commonlib.robot +Resource lib.robot +Test Timeout 5 minutes +Suite Setup Get Security Enabled From Config +Test Setup Run Keyword if '${SECURITY_ENABLED}' == 'true' Kinit test user testuser testuser.keytab + +*** Test Cases *** +Prepare Ozone Manager + Pass Execution If '%{OZONE_UPGRADE_FROM}' == '1.1.0' OM prepare is skipped for version %{OZONE_UPGRADE_FROM} + ${service_id} = Get OM Service ID + Pass Execution If '${service_id}' == '' OM prepare skipped in non-HA + Wait Until Keyword Succeeds 3min 10sec Prepare OM + From 260a76f7f0c649ade720c0bb1e29c1d76c20b0d8 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Wed, 18 Feb 2026 13:23:44 -0500 Subject: [PATCH 06/22] Update comments and messages --- .../apache/hadoop/ozone/shell/TestOzoneShellHA.java | 8 ++++---- .../src/main/proto/OmClientProtocol.proto | 10 +++++----- .../apache/hadoop/ozone/om/OzoneManagerStarter.java | 3 ++- .../ozone/protocolPB/OzoneManagerRequestHandler.java | 8 +++++--- .../protocolPB/TestOzoneManagerRequestHandler.java | 5 +++-- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java index ccbe09962ca5..67b612053a3d 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java @@ -2479,10 +2479,10 @@ public void testKeyDeleteLegacyWithEnableFileSystemPath() throws IOException { } /** - * Test that prepare commands are deprecated but still work and don't block writes. + * Test that even though prepare commands are deprecated, they still succeed without blocking writes. */ @Test - public void testPrepareCommandDoesNotBlockWrites() throws IOException { + public void testPrepareCommandNoOp() throws IOException { String volumeName = "vol-prepare-test"; String bucketName = "bucket-prepare-test"; @@ -2498,8 +2498,8 @@ public void testPrepareCommandDoesNotBlockWrites() throws IOException { out.reset(); // Create bucket - args = new String[] {"bucket", "create", "o3://" + omServiceId + OZONE_URI_DELIMITER + - volumeName + OZONE_URI_DELIMITER + bucketName}; + args = new String[] {"bucket", "create", "o3://" + omServiceId + OZONE_URI_DELIMITER + volumeName + + OZONE_URI_DELIMITER + bucketName}; execute(ozoneShell, args); out.reset(); diff --git a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto index badfa4ee57dd..7efddf7d11a0 100644 --- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto +++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto @@ -1624,7 +1624,7 @@ message PrepareRequest { } /** - * Deprecated. Prepare functionality has been removed. + * Deprecated. Prepare functionality has been removed and is now a no-op. */ message PrepareRequestArgs { optional uint64 txnApplyWaitTimeoutSeconds = 1 [default = 300, deprecated = true]; @@ -1632,14 +1632,14 @@ message PrepareRequestArgs { } /** - * Deprecated. Prepare functionality has been removed. + * Deprecated. Prepare functionality has been removed and is now a no-op. */ message PrepareResponse { required uint64 txnID = 1 [deprecated = true]; } /** - * Deprecated. Prepare functionality has been removed. + * Deprecated. Prepare functionality has been removed and is now a no-op. */ message PrepareStatusRequest { required uint64 txnID = 1 [deprecated = true]; @@ -1659,14 +1659,14 @@ message PrepareStatusResponse { } /** - * Deprecated. Prepare functionality has been removed. + * Deprecated. Prepare functionality has been removed and is now a no-op. */ message CancelPrepareRequest { } /** - * Deprecated. Prepare functionality has been removed. + * Deprecated. Prepare functionality has been removed and is now a no-op. */ message CancelPrepareResponse { diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java index 4697f74f03cd..f033856f6708 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java @@ -105,8 +105,9 @@ public void initOm() } /** - * This function implements a sub-command to allow the OM to be + * This function previously implemented a sub-command to allow the OM to be * Removed from prepare mode after an upgrade or downgrade. + * Since the Ozone Manager no longer requires prepare for upgrade, these flags are a no-op. */ @CommandLine.Command(name = "--upgrade", aliases = "--downgrade", diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java index 9aa6425d5d88..8df03dff3c1f 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java @@ -303,7 +303,8 @@ public OMResponse handleReadRequest(OMRequest request) { responseBuilder.setPrepareStatusResponse(prepareStatusResponse); break; case Prepare: - // Prepare is no longer functional but return success for backward compatibility + // Prepare functionality is no longer supported. An empty successful transaction is returned for compatibility + // with older clients and upgrade processes. PrepareResponse prepareResponse = PrepareResponse.newBuilder() .setTxnID(0) // Dummy transaction ID .build(); @@ -311,11 +312,12 @@ public OMResponse handleReadRequest(OMRequest request) { responseBuilder.setMessage("Prepare is no longer required in this version"); break; case CancelPrepare: - // CancelPrepare is no longer functional but return success for backward compatibility + // Cancel Prepare functionality is no longer supported. An empty successful transaction is returned for + // compatibility with older clients and upgrade processes. CancelPrepareResponse cancelPrepareResponse = CancelPrepareResponse.newBuilder() .build(); responseBuilder.setCancelPrepareResponse(cancelPrepareResponse); - responseBuilder.setMessage("Prepare is no longer required in this version"); + responseBuilder.setMessage("Cancel Prepare is no longer required in this version"); break; case GetS3VolumeContext: GetS3VolumeContextResponse s3VolumeContextResponse = diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java index 21dc43e8a6cd..2908dea974ca 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java @@ -244,7 +244,8 @@ public void testListKeysLightEncryptionFromOmKeyInfo() throws IOException { } /** - * Test to verify prepare-related requests return success as no-ops. + * Test to verify prepare-related requests return success as no-ops, since the Ozone Manager no longer supports this + * operation. */ @Test public void testPrepareRequestsAreNoOps() { @@ -307,7 +308,7 @@ public void testPrepareRequestsAreNoOps() { Assertions.assertTrue(cancelPrepareResponse.getSuccess(), "CancelPrepare should return success"); Assertions.assertTrue(cancelPrepareResponse.hasCancelPrepareResponse(), "CancelPrepare response should be present"); - Assertions.assertEquals("Prepare is no longer required in this version", + Assertions.assertEquals("Cancel Prepare is no longer required in this version", cancelPrepareResponse.getMessage(), "CancelPrepare should return deprecation message"); } } From bfee07b32e7cbbe539994e279dfadc8220afec0f Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Thu, 19 Feb 2026 11:54:00 -0500 Subject: [PATCH 07/22] Fix prepare request send to follower classification --- .../common/src/main/java/org/apache/hadoop/ozone/OmUtils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java index d2f0e071236f..69643d4d720a 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java @@ -431,6 +431,8 @@ public static boolean shouldSendToFollower(OMRequest omRequest) { // TODO: Remove once migrated to proto3 and mark fields in proto // as deprecated case FinalizeUpgrade: + case Prepare: + case CancelPrepare: case DeleteOpenKeys: case SetS3Secret: case RevokeS3Secret: From c5a9fd9bb8e77caa348c7fde90e676982a065bfe Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Thu, 19 Feb 2026 13:47:42 -0500 Subject: [PATCH 08/22] Fix existing client side prepare bug exposed by new test --- .../ozone/admin/om/PrepareSubCommand.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java index ca588026f6b6..7749d6105351 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java @@ -17,7 +17,6 @@ package org.apache.hadoop.ozone.admin.om; -import static org.apache.hadoop.ozone.OmUtils.getOmHostsFromConfig; import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus.PREPARE_COMPLETED; import java.io.IOException; @@ -25,10 +24,12 @@ import java.time.temporal.ChronoUnit; import java.util.HashMap; import java.util.Map; -import java.util.Set; import java.util.concurrent.Callable; import java.util.stream.Collectors; import org.apache.hadoop.hdds.cli.HddsVersionProvider; +import org.apache.hadoop.hdds.conf.OzoneConfiguration; +import org.apache.hadoop.ozone.OmUtils; +import org.apache.hadoop.ozone.om.helpers.OMNodeDetails; import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus; @@ -104,9 +105,11 @@ private void execute(OzoneManagerProtocol client) throws Exception { "with Transaction Id : [" + prepareTxnId + "]."); Map omPreparedStatusMap = new HashMap<>(); - Set omHosts = getOmHostsFromConfig( - parent.getParent().getOzoneConf(), omServiceOption.getServiceID()); - omHosts.forEach(h -> omPreparedStatusMap.put(h, false)); + OzoneConfiguration conf = parent.getParent().getOzoneConf(); + OmUtils.getAllOMHAAddresses(conf, omServiceOption.getServiceID(), false) + .stream() + .map(OMNodeDetails::getRpcAddressString) + .forEach(addr -> omPreparedStatusMap.put(addr, false)); Duration pTimeout = Duration.of(prepareTimeOut, ChronoUnit.SECONDS); Duration pInterval = Duration.of(prepareCheckInterval, ChronoUnit.SECONDS); @@ -121,10 +124,9 @@ private void execute(OzoneManagerProtocol client) throws Exception { for (Map.Entry e : omPreparedStatusMap.entrySet()) { if (!e.getValue()) { String omHost = e.getKey(); - try (OzoneManagerProtocol singleOmClient = - parent.createOmClient(omServiceOption.getServiceID(), omHost, false)) { - PrepareStatusResponse response = - singleOmClient.getOzoneManagerPrepareStatus(prepareTxnId); + try (OzoneManagerProtocol singleOmClient = parent.createOmClient(omServiceOption.getServiceID(), omHost, + false)) { + PrepareStatusResponse response = singleOmClient.getOzoneManagerPrepareStatus(prepareTxnId); PrepareStatus status = response.getStatus(); System.out.println("OM : [" + omHost + "], Prepare " + "Status : [" + status.name() + "], Current Transaction Id : [" + From 5a9ef5678abccdaa91c4de5a6af066e3d0e3080a Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Fri, 20 Feb 2026 12:52:00 -0500 Subject: [PATCH 09/22] Restore state machine polling in upgrade finalization test --- .../ozone/om/TestOMUpgradeFinalization.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) 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 66c7f1a0105d..c4feaf0722b0 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 @@ -21,6 +21,7 @@ import static org.apache.hadoop.ozone.om.OMUpgradeTestUtils.waitForFinalization; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.INITIAL_VERSION; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutVersionManager.maxLayoutVersion; +import static org.apache.ozone.test.GenericTestUtils.waitFor; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -28,6 +29,7 @@ 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.ozone.MiniOzoneCluster; import org.apache.hadoop.ozone.MiniOzoneHAClusterImpl; @@ -36,7 +38,9 @@ import org.apache.hadoop.ozone.audit.OMAction; 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.BeforeEach; import org.junit.jupiter.api.Test; @@ -61,7 +65,7 @@ public static void shutdown() { } @Test - void testOMUpgradeFinalizationWithOneOMDown() throws Exception { + void testOMUpgradeFinalizationFromSnapshot() throws Exception { OzoneConfiguration conf = new OzoneConfiguration(); try (MiniOzoneHAClusterImpl cluster = newCluster(conf)) { cluster.waitForClusterToBeReady(); @@ -83,8 +87,25 @@ void testOMUpgradeFinalizationWithOneOMDown() throws Exception { AuditLogTestUtils.verifyAuditLog(OMAction.UPGRADE_FINALIZE, AuditEventStatus.SUCCESS); waitForFinalization(omClient); + + // TODO need to purge follower logs to test snapshot install + + cluster.restartOzoneManager(downedOM, true); + // Wait for the downed OM to finish installing a snapshot. + OzoneManagerStateMachine omStateMachine = downedOM.getOmRatisServer() + .getOmStateMachine(); + try { + waitFor(() -> omStateMachine.getLifeCycleState().isPausingOrPaused(), + 1000, 60000); + } catch (TimeoutException timeEx) { + assertEquals(LifeCycle.State.RUNNING, + omStateMachine.getLifeCycle().getCurrentState()); + } + waitFor(() -> !omStateMachine.getLifeCycle().getCurrentState() + .isPausingOrPaused(), 1000, 60000); + assertEquals(maxLayoutVersion(), downedOM.getVersionManager().getMetadataLayoutVersion()); String lvString = downedOM.getMetadataManager().getMetaTable() From 631f8420ebc581e7c21c090377d86c00530967f5 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Fri, 20 Feb 2026 13:54:46 -0500 Subject: [PATCH 10/22] Mark open key tests as unhealthy --- .../hadoop/ozone/shell/TestOzoneShellHA.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java index 67b612053a3d..340f9d160e52 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java @@ -100,6 +100,7 @@ import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.util.ToolRunner; import org.apache.ozone.test.GenericTestUtils; +import org.apache.ozone.test.tag.Unhealthy; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -564,6 +565,7 @@ public void testOzoneAdminCmdList() throws UnsupportedEncodingException { execute(ozoneAdminShell, args); } + @Unhealthy("HDDS-14686") @Test public void testAdminCmdListOpenFiles() throws IOException, InterruptedException, TimeoutException { @@ -684,6 +686,7 @@ public void testAdminCmdListOpenFiles() } + @Unhealthy("HDDS-14686") @Test public void testAdminCmdListOpenFilesWithDeletedKeys() throws Exception { @@ -2485,41 +2488,41 @@ public void testKeyDeleteLegacyWithEnableFileSystemPath() throws IOException { public void testPrepareCommandNoOp() throws IOException { String volumeName = "vol-prepare-test"; String bucketName = "bucket-prepare-test"; - + // Execute prepare command via CLI String[] args = new String[] {"om", "prepare", "--service-id", omServiceId}; execute(ozoneAdminShell, args); out.reset(); - + // Verify write operations still work (prepare should not block them) // Create volume args = new String[] {"volume", "create", "o3://" + omServiceId + OZONE_URI_DELIMITER + volumeName}; execute(ozoneShell, args); out.reset(); - + // Create bucket args = new String[] {"bucket", "create", "o3://" + omServiceId + OZONE_URI_DELIMITER + volumeName + OZONE_URI_DELIMITER + bucketName}; execute(ozoneShell, args); out.reset(); - + // Create key - String keyName = OZONE_URI_DELIMITER + volumeName + OZONE_URI_DELIMITER + + String keyName = OZONE_URI_DELIMITER + volumeName + OZONE_URI_DELIMITER + bucketName + OZONE_URI_DELIMITER + "testkey"; args = new String[] {"key", "put", "o3://" + omServiceId + keyName, testFile.getPath()}; execute(ozoneShell, args); out.reset(); - + // Verify the key was created successfully OzoneVolume volume = client.getObjectStore().getVolume(volumeName); OzoneBucket bucket = volume.getBucket(bucketName); assertNotNull(bucket.getKey("testkey"), "Key should be created successfully after prepare"); - + // Execute cancelprepare command via CLI args = new String[] {"om", "cancelprepare", "--service-id", omServiceId}; execute(ozoneAdminShell, args); out.reset(); - + // Cleanup args = new String[] {"volume", "delete", volumeName, "-r", "--yes"}; execute(ozoneShell, args); From 02749efbb84c8f07ba026ad9095ada501d192de1 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Fri, 20 Feb 2026 16:48:10 -0500 Subject: [PATCH 11/22] Install snapshot test passes intermittently --- .../hadoop/ozone/audit/AuditLogTestUtils.java | 2 +- .../ozone/om/TestOMUpgradeFinalization.java | 118 +++++++++++++----- .../src/test/resources/auditlog.properties | 7 +- 3 files changed, 96 insertions(+), 31 deletions(-) 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 0b05d4d01f0e..1bd009096ef0 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 @@ -52,7 +52,7 @@ public static void verifyAuditLog(AuditAction action, throws InterruptedException, TimeoutException { waitFor( () -> auditLogContains(action.getAction(), eventStatus.getStatus()), - 1000, 10000); + 1000, 100000); } public static boolean auditLogContains(String... strings) { 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 c4feaf0722b0..b8b606859e4f 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 @@ -21,26 +21,34 @@ import static org.apache.hadoop.ozone.om.OMUpgradeTestUtils.waitForFinalization; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.INITIAL_VERSION; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutVersionManager.maxLayoutVersion; -import static org.apache.ozone.test.GenericTestUtils.waitFor; +import static org.assertj.core.api.Assertions.assertThat; 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 java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.UUID; -import java.util.concurrent.TimeoutException; import org.apache.hadoop.hdds.conf.OzoneConfiguration; +import org.apache.hadoop.hdds.conf.StorageUnit; +import org.apache.hadoop.hdds.utils.FaultInjector; +import org.apache.hadoop.hdds.utils.TransactionInfo; import org.apache.hadoop.ozone.MiniOzoneCluster; import org.apache.hadoop.ozone.MiniOzoneHAClusterImpl; import org.apache.hadoop.ozone.audit.AuditEventStatus; import org.apache.hadoop.ozone.audit.AuditLogTestUtils; import org.apache.hadoop.ozone.audit.OMAction; +import org.apache.hadoop.ozone.audit.OMSystemAction; import org.apache.hadoop.ozone.client.OzoneClient; +import org.apache.hadoop.ozone.om.OMConfigKeys; +import org.apache.hadoop.ozone.om.helpers.SnapshotInfo; import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol; -import org.apache.hadoop.ozone.om.ratis.OzoneManagerStateMachine; +import org.apache.hadoop.ozone.om.ratis.OzoneManagerRatisServerConfig; import org.apache.hadoop.ozone.upgrade.UpgradeFinalization.StatusAndMessages; -import org.apache.ratis.util.LifeCycle; +import org.apache.ozone.test.GenericTestUtils; +import org.apache.ratis.server.protocol.TermIndex; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -50,6 +58,9 @@ * TODO: can be merged into class with other OM tests with per-method cluster */ class TestOMUpgradeFinalization { + private static final int LOG_PURGE_GAP = 1; + private static final long SNAPSHOT_THRESHOLD = 1; + static { AuditLogTestUtils.enableAuditLog(); } @@ -64,6 +75,55 @@ public static void shutdown() { AuditLogTestUtils.deleteAuditLogFile(); } + @Test + void testFinalization2() throws Exception { + OzoneConfiguration conf = new OzoneConfiguration(); + try (MiniOzoneHAClusterImpl cluster = newCluster(conf)) { + cluster.waitForClusterToBeReady(); + // Get the leader OM + final String leaderOMNodeId = OmTestUtil.getCurrentOmProxyNodeId(cluster.newClient().getObjectStore()); + + OzoneManager leaderOM = cluster.getOzoneManager(leaderOMNodeId); + + // Find the inactive OM + String followerNodeId = leaderOM.getPeerNodes().get(0).getNodeId(); + if (cluster.isOMActive(followerNodeId)) { + followerNodeId = leaderOM.getPeerNodes().get(1).getNodeId(); + } +// OzoneManager followerOM = cluster.getOzoneManager(followerNodeId); + + try (OzoneClient client = cluster.newClient()) { + for (int i = 0; i < 100; i++) { + client.getObjectStore().createVolume("vol" + i); + } + } + +// // Get the latest db checkpoint from the leader OM. +// TransactionInfo transactionInfo = +// TransactionInfo.readTransactionInfo(leaderOM.getMetadataManager()); +// TermIndex leaderOMTermIndex = +// TermIndex.valueOf(transactionInfo.getTerm(), +// transactionInfo.getTransactionIndex()); +// long leaderOMSnapshotIndex = leaderOMTermIndex.getIndex(); +// long leaderOMSnapshotTermIndex = leaderOMTermIndex.getTerm(); + + // Start the inactive OM. Checkpoint installation will happen spontaneously. + cluster.startInactiveOM(followerNodeId); + + // The recently started OM should be lagging behind the leader OM. + // Wait & for follower to update transactions to leader snapshot index. + // Timeout error if follower does not load update within 10s +// GenericTestUtils.waitFor(() -> { +// long index = followerOM.getOmRatisServer().getLastAppliedTermIndex().getIndex(); +// return index >= leaderOMSnapshotIndex - 1; +// }, 100, 30_000); + + // Verify the inactive OM installed a snapshot from the leader. + AuditLogTestUtils.verifyAuditLog(OMSystemAction.DB_CHECKPOINT_INSTALL, + AuditEventStatus.SUCCESS); + } + } + @Test void testOMUpgradeFinalizationFromSnapshot() throws Exception { OzoneConfiguration conf = new OzoneConfiguration(); @@ -71,16 +131,15 @@ void testOMUpgradeFinalizationFromSnapshot() throws Exception { cluster.waitForClusterToBeReady(); try (OzoneClient client = cluster.newClient()) { - List runningOms = cluster.getOzoneManagersList(); - final int shutdownOMIndex = 2; - OzoneManager downedOM = cluster.getOzoneManager(shutdownOMIndex); - cluster.stopOzoneManager(shutdownOMIndex); - assertFalse(downedOM.isRunning()); - assertEquals(runningOms.remove(shutdownOMIndex), downedOM); + // One OM was never started (setNumOfActiveOMs(2)); find it. + OzoneManager inactiveOM = cluster.getOzoneManagersList().stream() + .filter(om -> !cluster.isOMActive(om.getOMNodeId())) + .findFirst() + .get(); OzoneManagerProtocol omClient = client.getObjectStore().getClientProxy() .getOzoneManagerClient(); - + StatusAndMessages response = omClient.finalizeUpgrade("finalize-test"); System.out.println("Finalization Messages : " + response.msgs()); @@ -88,27 +147,19 @@ void testOMUpgradeFinalizationFromSnapshot() throws Exception { waitForFinalization(omClient); - // TODO need to purge follower logs to test snapshot install + // The finalization log entries, combined with SNAPSHOT_THRESHOLD=1 and + // LOG_PURGE_GAP=1, are sufficient to trigger a snapshot and purge the + // logs the inactive OM never received, forcing it to install a snapshot. + cluster.startInactiveOM(inactiveOM.getOMNodeId()); - cluster.restartOzoneManager(downedOM, true); - - // Wait for the downed OM to finish installing a snapshot. - OzoneManagerStateMachine omStateMachine = downedOM.getOmRatisServer() - .getOmStateMachine(); - try { - waitFor(() -> omStateMachine.getLifeCycleState().isPausingOrPaused(), - 1000, 60000); - } catch (TimeoutException timeEx) { - assertEquals(LifeCycle.State.RUNNING, - omStateMachine.getLifeCycle().getCurrentState()); - } - waitFor(() -> !omStateMachine.getLifeCycle().getCurrentState() - .isPausingOrPaused(), 1000, 60000); + // Verify the inactive OM installed a snapshot from the leader. + AuditLogTestUtils.verifyAuditLog(OMSystemAction.DB_CHECKPOINT_INSTALL, + AuditEventStatus.SUCCESS); assertEquals(maxLayoutVersion(), - downedOM.getVersionManager().getMetadataLayoutVersion()); - String lvString = downedOM.getMetadataManager().getMetaTable() + inactiveOM.getVersionManager().getMetadataLayoutVersion()); + String lvString = inactiveOM.getMetadataManager().getMetaTable() .get(LAYOUT_VERSION_KEY); assertNotNull(lvString); assertEquals(maxLayoutVersion(), Integer.parseInt(lvString)); @@ -119,9 +170,18 @@ void testOMUpgradeFinalizationFromSnapshot() throws Exception { private static MiniOzoneHAClusterImpl newCluster(OzoneConfiguration conf) throws IOException { conf.setInt(OMStorage.TESTING_INIT_LAYOUT_VERSION_KEY, INITIAL_VERSION.layoutVersion()); + conf.setInt(OMConfigKeys.OZONE_OM_RATIS_LOG_PURGE_GAP, LOG_PURGE_GAP); + conf.setLong(OMConfigKeys.OZONE_OM_RATIS_SNAPSHOT_AUTO_TRIGGER_THRESHOLD_KEY, SNAPSHOT_THRESHOLD); + conf.setStorageSize(OMConfigKeys.OZONE_OM_RATIS_SEGMENT_SIZE_KEY, 16, StorageUnit.KB); + conf.setStorageSize(OMConfigKeys.OZONE_OM_RATIS_SEGMENT_PREALLOCATED_SIZE_KEY, 16, StorageUnit.KB); + OzoneManagerRatisServerConfig omRatisConf = + conf.getObject(OzoneManagerRatisServerConfig.class); + omRatisConf.setLogAppenderWaitTimeMin(10); + conf.setFromObject(omRatisConf); MiniOzoneHAClusterImpl.Builder builder = MiniOzoneCluster.newHABuilder(conf); builder.setOMServiceId(UUID.randomUUID().toString()) .setNumOfOzoneManagers(3) + .setNumOfActiveOMs(2) .setNumDatanodes(1); return builder.build(); } diff --git a/hadoop-ozone/integration-test/src/test/resources/auditlog.properties b/hadoop-ozone/integration-test/src/test/resources/auditlog.properties index fb644786d0b1..57f76beb1bf2 100644 --- a/hadoop-ozone/integration-test/src/test/resources/auditlog.properties +++ b/hadoop-ozone/integration-test/src/test/resources/auditlog.properties @@ -64,12 +64,17 @@ 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 +loggers=audit,sysaudit logger.audit.name=OMAudit logger.audit.level = INFO logger.audit.appenderRefs = audit logger.audit.appenderRef.file.ref = AUDITLOG +logger.sysaudit.name=OMSystemAudit +logger.sysaudit.level = INFO +logger.sysaudit.appenderRefs = audit +logger.sysaudit.appenderRef.file.ref = AUDITLOG + rootLogger.level = INFO rootLogger.appenderRefs = stdout rootLogger.appenderRef.stdout.ref = STDOUT From d201f3f1958f4927f038c33378c0e240373ef9e7 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Fri, 20 Feb 2026 17:38:11 -0500 Subject: [PATCH 12/22] Revert "Install snapshot test passes intermittently" This reverts commit 02749efbb84c8f07ba026ad9095ada501d192de1. --- .../hadoop/ozone/audit/AuditLogTestUtils.java | 2 +- .../ozone/om/TestOMUpgradeFinalization.java | 118 +++++------------- .../src/test/resources/auditlog.properties | 7 +- 3 files changed, 31 insertions(+), 96 deletions(-) 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 1bd009096ef0..0b05d4d01f0e 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 @@ -52,7 +52,7 @@ public static void verifyAuditLog(AuditAction action, throws InterruptedException, TimeoutException { waitFor( () -> auditLogContains(action.getAction(), eventStatus.getStatus()), - 1000, 100000); + 1000, 10000); } public static boolean auditLogContains(String... strings) { 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 b8b606859e4f..c4feaf0722b0 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 @@ -21,34 +21,26 @@ import static org.apache.hadoop.ozone.om.OMUpgradeTestUtils.waitForFinalization; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.INITIAL_VERSION; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutVersionManager.maxLayoutVersion; -import static org.assertj.core.api.Assertions.assertThat; +import static org.apache.ozone.test.GenericTestUtils.waitFor; 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 java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.UUID; +import java.util.concurrent.TimeoutException; import org.apache.hadoop.hdds.conf.OzoneConfiguration; -import org.apache.hadoop.hdds.conf.StorageUnit; -import org.apache.hadoop.hdds.utils.FaultInjector; -import org.apache.hadoop.hdds.utils.TransactionInfo; import org.apache.hadoop.ozone.MiniOzoneCluster; import org.apache.hadoop.ozone.MiniOzoneHAClusterImpl; import org.apache.hadoop.ozone.audit.AuditEventStatus; import org.apache.hadoop.ozone.audit.AuditLogTestUtils; import org.apache.hadoop.ozone.audit.OMAction; -import org.apache.hadoop.ozone.audit.OMSystemAction; import org.apache.hadoop.ozone.client.OzoneClient; -import org.apache.hadoop.ozone.om.OMConfigKeys; -import org.apache.hadoop.ozone.om.helpers.SnapshotInfo; import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol; -import org.apache.hadoop.ozone.om.ratis.OzoneManagerRatisServerConfig; +import org.apache.hadoop.ozone.om.ratis.OzoneManagerStateMachine; import org.apache.hadoop.ozone.upgrade.UpgradeFinalization.StatusAndMessages; -import org.apache.ozone.test.GenericTestUtils; -import org.apache.ratis.server.protocol.TermIndex; +import org.apache.ratis.util.LifeCycle; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -58,9 +50,6 @@ * TODO: can be merged into class with other OM tests with per-method cluster */ class TestOMUpgradeFinalization { - private static final int LOG_PURGE_GAP = 1; - private static final long SNAPSHOT_THRESHOLD = 1; - static { AuditLogTestUtils.enableAuditLog(); } @@ -75,55 +64,6 @@ public static void shutdown() { AuditLogTestUtils.deleteAuditLogFile(); } - @Test - void testFinalization2() throws Exception { - OzoneConfiguration conf = new OzoneConfiguration(); - try (MiniOzoneHAClusterImpl cluster = newCluster(conf)) { - cluster.waitForClusterToBeReady(); - // Get the leader OM - final String leaderOMNodeId = OmTestUtil.getCurrentOmProxyNodeId(cluster.newClient().getObjectStore()); - - OzoneManager leaderOM = cluster.getOzoneManager(leaderOMNodeId); - - // Find the inactive OM - String followerNodeId = leaderOM.getPeerNodes().get(0).getNodeId(); - if (cluster.isOMActive(followerNodeId)) { - followerNodeId = leaderOM.getPeerNodes().get(1).getNodeId(); - } -// OzoneManager followerOM = cluster.getOzoneManager(followerNodeId); - - try (OzoneClient client = cluster.newClient()) { - for (int i = 0; i < 100; i++) { - client.getObjectStore().createVolume("vol" + i); - } - } - -// // Get the latest db checkpoint from the leader OM. -// TransactionInfo transactionInfo = -// TransactionInfo.readTransactionInfo(leaderOM.getMetadataManager()); -// TermIndex leaderOMTermIndex = -// TermIndex.valueOf(transactionInfo.getTerm(), -// transactionInfo.getTransactionIndex()); -// long leaderOMSnapshotIndex = leaderOMTermIndex.getIndex(); -// long leaderOMSnapshotTermIndex = leaderOMTermIndex.getTerm(); - - // Start the inactive OM. Checkpoint installation will happen spontaneously. - cluster.startInactiveOM(followerNodeId); - - // The recently started OM should be lagging behind the leader OM. - // Wait & for follower to update transactions to leader snapshot index. - // Timeout error if follower does not load update within 10s -// GenericTestUtils.waitFor(() -> { -// long index = followerOM.getOmRatisServer().getLastAppliedTermIndex().getIndex(); -// return index >= leaderOMSnapshotIndex - 1; -// }, 100, 30_000); - - // Verify the inactive OM installed a snapshot from the leader. - AuditLogTestUtils.verifyAuditLog(OMSystemAction.DB_CHECKPOINT_INSTALL, - AuditEventStatus.SUCCESS); - } - } - @Test void testOMUpgradeFinalizationFromSnapshot() throws Exception { OzoneConfiguration conf = new OzoneConfiguration(); @@ -131,15 +71,16 @@ void testOMUpgradeFinalizationFromSnapshot() throws Exception { cluster.waitForClusterToBeReady(); try (OzoneClient client = cluster.newClient()) { - // One OM was never started (setNumOfActiveOMs(2)); find it. - OzoneManager inactiveOM = cluster.getOzoneManagersList().stream() - .filter(om -> !cluster.isOMActive(om.getOMNodeId())) - .findFirst() - .get(); + List runningOms = cluster.getOzoneManagersList(); + final int shutdownOMIndex = 2; + OzoneManager downedOM = cluster.getOzoneManager(shutdownOMIndex); + cluster.stopOzoneManager(shutdownOMIndex); + assertFalse(downedOM.isRunning()); + assertEquals(runningOms.remove(shutdownOMIndex), downedOM); OzoneManagerProtocol omClient = client.getObjectStore().getClientProxy() .getOzoneManagerClient(); - + StatusAndMessages response = omClient.finalizeUpgrade("finalize-test"); System.out.println("Finalization Messages : " + response.msgs()); @@ -147,19 +88,27 @@ void testOMUpgradeFinalizationFromSnapshot() throws Exception { waitForFinalization(omClient); - // The finalization log entries, combined with SNAPSHOT_THRESHOLD=1 and - // LOG_PURGE_GAP=1, are sufficient to trigger a snapshot and purge the - // logs the inactive OM never received, forcing it to install a snapshot. + // TODO need to purge follower logs to test snapshot install - cluster.startInactiveOM(inactiveOM.getOMNodeId()); - // Verify the inactive OM installed a snapshot from the leader. - AuditLogTestUtils.verifyAuditLog(OMSystemAction.DB_CHECKPOINT_INSTALL, - AuditEventStatus.SUCCESS); + cluster.restartOzoneManager(downedOM, true); + + // Wait for the downed OM to finish installing a snapshot. + OzoneManagerStateMachine omStateMachine = downedOM.getOmRatisServer() + .getOmStateMachine(); + try { + waitFor(() -> omStateMachine.getLifeCycleState().isPausingOrPaused(), + 1000, 60000); + } catch (TimeoutException timeEx) { + assertEquals(LifeCycle.State.RUNNING, + omStateMachine.getLifeCycle().getCurrentState()); + } + waitFor(() -> !omStateMachine.getLifeCycle().getCurrentState() + .isPausingOrPaused(), 1000, 60000); assertEquals(maxLayoutVersion(), - inactiveOM.getVersionManager().getMetadataLayoutVersion()); - String lvString = inactiveOM.getMetadataManager().getMetaTable() + downedOM.getVersionManager().getMetadataLayoutVersion()); + String lvString = downedOM.getMetadataManager().getMetaTable() .get(LAYOUT_VERSION_KEY); assertNotNull(lvString); assertEquals(maxLayoutVersion(), Integer.parseInt(lvString)); @@ -170,18 +119,9 @@ void testOMUpgradeFinalizationFromSnapshot() throws Exception { private static MiniOzoneHAClusterImpl newCluster(OzoneConfiguration conf) throws IOException { conf.setInt(OMStorage.TESTING_INIT_LAYOUT_VERSION_KEY, INITIAL_VERSION.layoutVersion()); - conf.setInt(OMConfigKeys.OZONE_OM_RATIS_LOG_PURGE_GAP, LOG_PURGE_GAP); - conf.setLong(OMConfigKeys.OZONE_OM_RATIS_SNAPSHOT_AUTO_TRIGGER_THRESHOLD_KEY, SNAPSHOT_THRESHOLD); - conf.setStorageSize(OMConfigKeys.OZONE_OM_RATIS_SEGMENT_SIZE_KEY, 16, StorageUnit.KB); - conf.setStorageSize(OMConfigKeys.OZONE_OM_RATIS_SEGMENT_PREALLOCATED_SIZE_KEY, 16, StorageUnit.KB); - OzoneManagerRatisServerConfig omRatisConf = - conf.getObject(OzoneManagerRatisServerConfig.class); - omRatisConf.setLogAppenderWaitTimeMin(10); - conf.setFromObject(omRatisConf); MiniOzoneHAClusterImpl.Builder builder = MiniOzoneCluster.newHABuilder(conf); builder.setOMServiceId(UUID.randomUUID().toString()) .setNumOfOzoneManagers(3) - .setNumOfActiveOMs(2) .setNumDatanodes(1); return builder.build(); } diff --git a/hadoop-ozone/integration-test/src/test/resources/auditlog.properties b/hadoop-ozone/integration-test/src/test/resources/auditlog.properties index 57f76beb1bf2..fb644786d0b1 100644 --- a/hadoop-ozone/integration-test/src/test/resources/auditlog.properties +++ b/hadoop-ozone/integration-test/src/test/resources/auditlog.properties @@ -64,17 +64,12 @@ 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,sysaudit +loggers=audit logger.audit.name=OMAudit logger.audit.level = INFO logger.audit.appenderRefs = audit logger.audit.appenderRef.file.ref = AUDITLOG -logger.sysaudit.name=OMSystemAudit -logger.sysaudit.level = INFO -logger.sysaudit.appenderRefs = audit -logger.sysaudit.appenderRef.file.ref = AUDITLOG - rootLogger.level = INFO rootLogger.appenderRefs = stdout rootLogger.appenderRef.stdout.ref = STDOUT From 22b5ef524cb10df44b6e9e108e711eb74af78dba Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Fri, 20 Feb 2026 18:03:08 -0500 Subject: [PATCH 13/22] Remove finalize from snapshot test for now --- .../ozone/om/TestOMUpgradeFinalization.java | 129 ------------------ 1 file changed, 129 deletions(-) delete mode 100644 hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMUpgradeFinalization.java 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 deleted file mode 100644 index c4feaf0722b0..000000000000 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMUpgradeFinalization.java +++ /dev/null @@ -1,129 +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.om; - -import static org.apache.hadoop.ozone.OzoneConsts.LAYOUT_VERSION_KEY; -import static org.apache.hadoop.ozone.om.OMUpgradeTestUtils.waitForFinalization; -import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.INITIAL_VERSION; -import static org.apache.hadoop.ozone.om.upgrade.OMLayoutVersionManager.maxLayoutVersion; -import static org.apache.ozone.test.GenericTestUtils.waitFor; -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 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.ozone.MiniOzoneCluster; -import org.apache.hadoop.ozone.MiniOzoneHAClusterImpl; -import org.apache.hadoop.ozone.audit.AuditEventStatus; -import org.apache.hadoop.ozone.audit.AuditLogTestUtils; -import org.apache.hadoop.ozone.audit.OMAction; -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.BeforeEach; -import org.junit.jupiter.api.Test; - -/** - * Tests for OM upgrade finalization. - * TODO: can be merged into class with other OM tests with per-method cluster - */ -class TestOMUpgradeFinalization { - static { - AuditLogTestUtils.enableAuditLog(); - } - - @BeforeEach - public void setup() throws Exception { - AuditLogTestUtils.truncateAuditLogFile(); - } - - @AfterAll - public static void shutdown() { - AuditLogTestUtils.deleteAuditLogFile(); - } - - @Test - void testOMUpgradeFinalizationFromSnapshot() throws Exception { - OzoneConfiguration conf = new OzoneConfiguration(); - try (MiniOzoneHAClusterImpl cluster = newCluster(conf)) { - cluster.waitForClusterToBeReady(); - - try (OzoneClient client = cluster.newClient()) { - List runningOms = cluster.getOzoneManagersList(); - final int shutdownOMIndex = 2; - OzoneManager downedOM = cluster.getOzoneManager(shutdownOMIndex); - cluster.stopOzoneManager(shutdownOMIndex); - assertFalse(downedOM.isRunning()); - assertEquals(runningOms.remove(shutdownOMIndex), downedOM); - - OzoneManagerProtocol omClient = client.getObjectStore().getClientProxy() - .getOzoneManagerClient(); - - StatusAndMessages response = - omClient.finalizeUpgrade("finalize-test"); - System.out.println("Finalization Messages : " + response.msgs()); - AuditLogTestUtils.verifyAuditLog(OMAction.UPGRADE_FINALIZE, AuditEventStatus.SUCCESS); - - waitForFinalization(omClient); - - // TODO need to purge follower logs to test snapshot install - - - cluster.restartOzoneManager(downedOM, true); - - // Wait for the downed OM to finish installing a snapshot. - OzoneManagerStateMachine omStateMachine = downedOM.getOmRatisServer() - .getOmStateMachine(); - try { - waitFor(() -> omStateMachine.getLifeCycleState().isPausingOrPaused(), - 1000, 60000); - } catch (TimeoutException timeEx) { - assertEquals(LifeCycle.State.RUNNING, - omStateMachine.getLifeCycle().getCurrentState()); - } - waitFor(() -> !omStateMachine.getLifeCycle().getCurrentState() - .isPausingOrPaused(), 1000, 60000); - - assertEquals(maxLayoutVersion(), - downedOM.getVersionManager().getMetadataLayoutVersion()); - String lvString = downedOM.getMetadataManager().getMetaTable() - .get(LAYOUT_VERSION_KEY); - assertNotNull(lvString); - assertEquals(maxLayoutVersion(), Integer.parseInt(lvString)); - } - } - } - - private static MiniOzoneHAClusterImpl newCluster(OzoneConfiguration conf) - throws IOException { - conf.setInt(OMStorage.TESTING_INIT_LAYOUT_VERSION_KEY, INITIAL_VERSION.layoutVersion()); - MiniOzoneHAClusterImpl.Builder builder = MiniOzoneCluster.newHABuilder(conf); - builder.setOMServiceId(UUID.randomUUID().toString()) - .setNumOfOzoneManagers(3) - .setNumDatanodes(1); - return builder.build(); - } - -} From 414d2ed06af61c2d3cf18a3fe89025b1d6cc6042 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Fri, 27 Feb 2026 17:15:40 -0500 Subject: [PATCH 14/22] Restore admin checks --- .../OzoneManagerRequestHandler.java | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java index 8df03dff3c1f..9a62f6809acf 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java @@ -17,6 +17,8 @@ package org.apache.hadoop.ozone.protocolPB; +import static org.apache.hadoop.hdds.utils.HddsServerUtil.getRemoteUser; +import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.PERMISSION_DENIED; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.FILESYSTEM_SNAPSHOT; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.HBASE_SUPPORT; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.MULTITENANCY_SCHEMA; @@ -167,6 +169,7 @@ import org.apache.hadoop.ozone.upgrade.UpgradeFinalization.StatusAndMessages; import org.apache.hadoop.ozone.util.PayloadUtils; import org.apache.hadoop.ozone.util.ProtobufUtils; +import org.apache.hadoop.security.UserGroupInformation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -303,19 +306,12 @@ public OMResponse handleReadRequest(OMRequest request) { responseBuilder.setPrepareStatusResponse(prepareStatusResponse); break; case Prepare: - // Prepare functionality is no longer supported. An empty successful transaction is returned for compatibility - // with older clients and upgrade processes. - PrepareResponse prepareResponse = PrepareResponse.newBuilder() - .setTxnID(0) // Dummy transaction ID - .build(); + PrepareResponse prepareResponse = prepare(); responseBuilder.setPrepareResponse(prepareResponse); responseBuilder.setMessage("Prepare is no longer required in this version"); break; case CancelPrepare: - // Cancel Prepare functionality is no longer supported. An empty successful transaction is returned for - // compatibility with older clients and upgrade processes. - CancelPrepareResponse cancelPrepareResponse = CancelPrepareResponse.newBuilder() - .build(); + CancelPrepareResponse cancelPrepareResponse = cancelPrepare(); responseBuilder.setCancelPrepareResponse(cancelPrepareResponse); responseBuilder.setMessage("Cancel Prepare is no longer required in this version"); break; @@ -1389,13 +1385,45 @@ private FinalizeUpgradeProgressResponse reportUpgradeProgress( .build(); } - private PrepareStatusResponse getPrepareStatus() { + private PrepareStatusResponse getPrepareStatus() throws IOException { + UserGroupInformation ugi = getRemoteUser(); + if (!impl.isAdmin(ugi)) { + throw new OMException("Access denied for user " + ugi + + ". Superuser privilege is required to get prepare status for OM.", PERMISSION_DENIED); + } + // Prepare is no longer used, always return PREPARE_COMPLETED for backward compatibility return PrepareStatusResponse.newBuilder() .setStatus(PrepareStatusResponse.PrepareStatus.PREPARE_COMPLETED) .setCurrentTxnIndex(0).build(); } + private PrepareResponse prepare() throws IOException { + // Prepare functionality is no longer supported. An empty successful transaction is returned for compatibility + // with older clients and upgrade processes. + UserGroupInformation ugi = getRemoteUser(); + if (!impl.isAdmin(ugi)) { + throw new OMException("Access denied for user " + ugi + + ". Superuser privilege is required to prepare OM.", PERMISSION_DENIED); + } + + return PrepareResponse.newBuilder() + .setTxnID(0) // Dummy transaction ID + .build(); + } + + private CancelPrepareResponse cancelPrepare() throws IOException { + // Cancel Prepare functionality is no longer supported. An empty successful transaction is returned for + // compatibility with older clients and upgrade processes. + UserGroupInformation ugi = getRemoteUser(); + if (!impl.isAdmin(ugi)) { + throw new OMException("Access denied for user " + ugi + + ". Superuser privilege is required to cancel prepare for OM.", PERMISSION_DENIED); + } + + return CancelPrepareResponse.newBuilder().build(); + } + private GetS3VolumeContextResponse getS3VolumeContext() throws IOException { return impl.getS3VolumeContext().getProtobuf(); From d5891a8cbf22385cff2f1ded279f27257d27d8a1 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Mon, 13 Jul 2026 18:02:31 -0400 Subject: [PATCH 15/22] Mock isAdmin for tests --- .../ozone/protocolPB/TestOzoneManagerRequestHandler.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java index 2908dea974ca..257e7d34b8ae 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java @@ -39,6 +39,7 @@ import org.apache.hadoop.ozone.om.helpers.OmKeyLocationInfoGroup; import org.apache.hadoop.ozone.om.helpers.OzoneFileStatus; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; +import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.util.Time; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -56,6 +57,8 @@ private OzoneManagerRequestHandler getRequestHandler(int limitListKeySize) { config.setMaxListSize(limitListKeySize); OzoneManager ozoneManager = Mockito.mock(OzoneManager.class); Mockito.when(ozoneManager.getConfig()).thenReturn(config); + Mockito.when(ozoneManager.isAdmin(Mockito.any(UserGroupInformation.class))) + .thenReturn(true); return new OzoneManagerRequestHandler(ozoneManager); } From 310d8604da85d0bf2cba839f2b931d9833645e52 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Tue, 14 Jul 2026 13:13:43 -0400 Subject: [PATCH 16/22] Remove robot file that was unintentionally restored during merge --- .../src/main/smoketest/upgrade/prepare.robot | 30 ------------------- 1 file changed, 30 deletions(-) delete mode 100644 hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot diff --git a/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot b/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot deleted file mode 100644 index 6004bd9a07cd..000000000000 --- a/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot +++ /dev/null @@ -1,30 +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. - -*** Settings *** -Documentation Prepares OMs -Resource ../commonlib.robot -Resource lib.robot -Test Timeout 5 minutes -Suite Setup Get Security Enabled From Config -Test Setup Run Keyword if '${SECURITY_ENABLED}' == 'true' Kinit test user testuser testuser.keytab - -*** Test Cases *** -Prepare Ozone Manager - Pass Execution If '%{OZONE_UPGRADE_FROM}' == '1.1.0' OM prepare is skipped for version %{OZONE_UPGRADE_FROM} - ${service_id} = Get OM Service ID - Pass Execution If '${service_id}' == '' OM prepare skipped in non-HA - Wait Until Keyword Succeeds 3min 10sec Prepare OM - From f7d1b8873def487c815ef1523e0c21c748bcd7e7 Mon Sep 17 00:00:00 2001 From: Ethan Rose <33912936+errose28@users.noreply.github.com> Date: Fri, 17 Jul 2026 12:12:13 -0400 Subject: [PATCH 17/22] Update response code in comment Co-authored-by: Zita Dombi <50611074+dombizita@users.noreply.github.com> --- .../interface-client/src/main/proto/OmClientProtocol.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto index 14dc5d044bd6..e998afb1a51a 100644 --- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto +++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto @@ -1712,7 +1712,7 @@ message PrepareStatusRequest { } /** - * Deprecated. Prepare functionality has been removed and server status will always be NOT_PREPARED. + * Deprecated. Prepare functionality has been removed and server status will always be PREPARE_COMPLETED. */ message PrepareStatusResponse { enum PrepareStatus { From 6a428b7f664e09ad7aba55289f47b2b09125c365 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Fri, 17 Jul 2026 12:13:48 -0400 Subject: [PATCH 18/22] Fix comment and undo line formatting change --- .../apache/hadoop/ozone/admin/om/PrepareSubCommand.java | 7 ++++--- .../src/main/java/org/apache/hadoop/ozone/OmUtils.java | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java index a1826d0210a9..246a614d28c1 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java @@ -124,9 +124,10 @@ private void execute(OzoneManagerProtocol client) throws Exception { for (Map.Entry e : omPreparedStatusMap.entrySet()) { if (!e.getValue()) { String omHost = e.getKey(); - try (OzoneManagerProtocol singleOmClient = parent.createOmClient(omServiceOption.getServiceID(), omHost, - false)) { - PrepareStatusResponse response = singleOmClient.getOzoneManagerPrepareStatus(prepareTxnId); + try (OzoneManagerProtocol singleOmClient = + parent.createOmClient(omServiceOption.getServiceID(), omHost, false)) { + PrepareStatusResponse response = + singleOmClient.getOzoneManagerPrepareStatus(prepareTxnId); PrepareStatus status = response.getStatus(); System.out.println("OM : [" + omHost + "], Prepare " + "Status : [" + status.name() + "], Current Transaction Id : [" + diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java index 9a59da439d97..842d6ef353c7 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java @@ -260,10 +260,10 @@ public static boolean isReadOnly(OMRequest omRequest) { case GetSnapshotInfo: case GetObjectTagging: case GetBucketTagging: - return true; case GetQuotaRepairStatus: case StartQuotaRepair: // Prepare and CancelPrepare are now no-ops, but still produce responses for compatability. + // They are marked as read-only since they don't modify state anymore. case Prepare: case CancelPrepare: case QueryUpgradeStatus: From ea7a7d992a4b90c8f69906cb372e24e27bb8b9b6 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Fri, 17 Jul 2026 12:24:43 -0400 Subject: [PATCH 19/22] Add comments on result codes --- .../org/apache/hadoop/ozone/om/exceptions/OMException.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java index 240e99e7d673..4504917e9bc2 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java @@ -242,8 +242,11 @@ public enum ResultCodes { REMOVE_UPGRADE_TO_LAYOUT_VERSION_FAILED, UPDATE_LAYOUT_VERSION_FAILED, LAYOUT_FEATURE_FINALIZATION_FAILED, + // Even though new OM servers do not support prepare for upgrade, clients may still encounter these result codes + // when talking to an older server, so they are left in place. PREPARE_FAILED, NOT_SUPPORTED_OPERATION_WHEN_PREPARED, + NOT_SUPPORTED_OPERATION_PRIOR_FINALIZATION, TENANT_NOT_FOUND, From fcd4fd14b8abd7bac7b44087cd4b77089febf164 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Fri, 17 Jul 2026 12:50:33 -0400 Subject: [PATCH 20/22] Update comments, fix admin check --- .../om/ha/OMFailoverProxyProviderBase.java | 1 + .../src/main/proto/OmClientProtocol.proto | 2 +- .../apache/hadoop/ozone/om/OzoneManager.java | 2 +- .../protocolPB/OzoneManagerRequestHandler.java | 18 +++--------------- 4 files changed, 6 insertions(+), 17 deletions(-) diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/ha/OMFailoverProxyProviderBase.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/ha/OMFailoverProxyProviderBase.java index a34709a3a54c..ad177a5195d1 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/ha/OMFailoverProxyProviderBase.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/ha/OMFailoverProxyProviderBase.java @@ -179,6 +179,7 @@ protected synchronized boolean shouldFailover(Exception ex) { OMException omEx = (OMException) cause; // Do not failover if the operation was blocked because the OM was // prepared. + // Current OMs do not support prepare for upgrade, but the client may encounter an old OM server. return omEx.getResult() != OMException.ResultCodes.NOT_SUPPORTED_OPERATION_WHEN_PREPARED; } diff --git a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto index e998afb1a51a..cf60eb31f3af 100644 --- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto +++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto @@ -560,7 +560,7 @@ enum Status { LAYOUT_FEATURE_FINALIZATION_FAILED = 72; PREPARE_FAILED = 73; // Deprecated NOT_SUPPORTED_OPERATION_WHEN_PREPARED = 74; // Deprecated - NOT_SUPPORTED_OPERATION_PRIOR_FINALIZATION = 75; // Deprecated + NOT_SUPPORTED_OPERATION_PRIOR_FINALIZATION = 75; TENANT_NOT_FOUND = 76; TENANT_ALREADY_EXISTS = 77; 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 fff50bbfad15..5e27bde4a2b1 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 @@ -4756,7 +4756,7 @@ public boolean isReadBlacklisted(UserGroupInformation callerUgi) { * Check ozone admin privilege, throws exception if not admin. * Only checks admin privilege if authorization is enabled. */ - private void checkAdminUserPrivilege(String operation) throws IOException { + public void checkAdminUserPrivilege(String operation) throws IOException { // Skip check if authorization is disabled if (!isAdminAuthorizationEnabled()) { return; diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java index 2847ff653c23..e4f029743714 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java @@ -1417,11 +1417,7 @@ private FinalizeUpgradeProgressResponse reportUpgradeProgress( } private PrepareStatusResponse getPrepareStatus() throws IOException { - UserGroupInformation ugi = getRemoteUser(); - if (!impl.isAdmin(ugi)) { - throw new OMException("Access denied for user " + ugi + - ". Superuser privilege is required to get prepare status for OM.", PERMISSION_DENIED); - } + impl.checkAdminUserPrivilege("get prepare status"); // Prepare is no longer used, always return PREPARE_COMPLETED for backward compatibility return PrepareStatusResponse.newBuilder() @@ -1432,11 +1428,7 @@ private PrepareStatusResponse getPrepareStatus() throws IOException { private PrepareResponse prepare() throws IOException { // Prepare functionality is no longer supported. An empty successful transaction is returned for compatibility // with older clients and upgrade processes. - UserGroupInformation ugi = getRemoteUser(); - if (!impl.isAdmin(ugi)) { - throw new OMException("Access denied for user " + ugi - + ". Superuser privilege is required to prepare OM.", PERMISSION_DENIED); - } + impl.checkAdminUserPrivilege("prepare for upgrade"); return PrepareResponse.newBuilder() .setTxnID(0) // Dummy transaction ID @@ -1446,11 +1438,7 @@ private PrepareResponse prepare() throws IOException { private CancelPrepareResponse cancelPrepare() throws IOException { // Cancel Prepare functionality is no longer supported. An empty successful transaction is returned for // compatibility with older clients and upgrade processes. - UserGroupInformation ugi = getRemoteUser(); - if (!impl.isAdmin(ugi)) { - throw new OMException("Access denied for user " + ugi - + ". Superuser privilege is required to cancel prepare for OM.", PERMISSION_DENIED); - } + impl.checkAdminUserPrivilege("cancel prepare"); return CancelPrepareResponse.newBuilder().build(); } From d9590ebee5dbd759b7e184f57e5591a342620c77 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Fri, 17 Jul 2026 12:51:39 -0400 Subject: [PATCH 21/22] Revert "Mock isAdmin for tests" This reverts commit d5891a8cbf22385cff2f1ded279f27257d27d8a1. Conflicts: hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java --- .../ozone/protocolPB/TestOzoneManagerRequestHandler.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java index 33580426111f..a2c51a7e88c3 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java @@ -49,7 +49,6 @@ import org.apache.hadoop.ozone.om.upgrade.OMVersionManager; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; import org.apache.hadoop.ozone.snapshot.SnapshotDiffResponse; -import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.util.Time; import org.apache.ratis.server.protocol.TermIndex; import org.junit.jupiter.api.Assertions; @@ -69,8 +68,6 @@ private OzoneManagerRequestHandler getRequestHandler(int limitListKeySize) { config.setMaxListSize(limitListKeySize); OzoneManager ozoneManager = Mockito.mock(OzoneManager.class); Mockito.when(ozoneManager.getConfig()).thenReturn(config); - Mockito.when(ozoneManager.isAdmin(Mockito.any(UserGroupInformation.class))) - .thenReturn(true); return new OzoneManagerRequestHandler(ozoneManager); } From fa63808162676555455068aeb6e4146d589c91a4 Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Fri, 17 Jul 2026 13:11:04 -0400 Subject: [PATCH 22/22] Checkstyle --- .../hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java index e4f029743714..e9670614677d 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java @@ -17,8 +17,6 @@ package org.apache.hadoop.ozone.protocolPB; -import static org.apache.hadoop.hdds.utils.HddsServerUtil.getRemoteUser; -import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.PERMISSION_DENIED; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.FILESYSTEM_SNAPSHOT; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.HBASE_SUPPORT; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.MULTITENANCY_SCHEMA; @@ -175,7 +173,6 @@ import org.apache.hadoop.ozone.upgrade.UpgradeFinalization.StatusAndMessages; import org.apache.hadoop.ozone.util.PayloadUtils; import org.apache.hadoop.ozone.util.ProtobufUtils; -import org.apache.hadoop.security.UserGroupInformation; import org.slf4j.Logger; import org.slf4j.LoggerFactory;