Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

package org.apache.hadoop.hdds.upgrade;

import java.util.EnumMap;
import java.util.Optional;
import org.apache.hadoop.ozone.upgrade.LayoutFeature;

Expand Down Expand Up @@ -49,23 +48,36 @@ public enum HDDSLayoutFeature implements LayoutFeature {

private int layoutVersion;
private String description;
private EnumMap<UpgradeActionType, HDDSUpgradeAction> scmActions =
new EnumMap<>(UpgradeActionType.class);
private EnumMap<UpgradeActionType, HDDSUpgradeAction> datanodeActions =
new EnumMap<>(UpgradeActionType.class);
private HDDSUpgradeAction scmAction;
private HDDSUpgradeAction datanodeAction;

HDDSLayoutFeature(final int layoutVersion, String description) {
this.layoutVersion = layoutVersion;
this.description = description;
}

public void addScmAction(UpgradeActionType type, HDDSUpgradeAction action) {
this.scmActions.put(type, action);
/**
* Associates an SCM upgrade action with this feature. Only the first upgrade action registered will be used.
*
* @param action The upgrade action to associate with this feature.
*/
public void addScmAction(HDDSUpgradeAction action) {
// Required by SpotBugs since this setter exists in an enum.
if (this.scmAction == null) {
this.scmAction = action;
}
}

public void addDatanodeAction(UpgradeActionType type,
HDDSUpgradeAction action) {
this.datanodeActions.put(type, action);
/**
* Associates a Datanode upgrade action with this feature. Only the first upgrade action registered will be used.
*
* @param action The upgrade action to associate with this feature.
*/
public void addDatanodeAction(HDDSUpgradeAction action) {
// Required by SpotBugs since this setter exists in an enum.
if (this.datanodeAction == null) {
this.datanodeAction = action;
}
}

@Override
Expand All @@ -78,11 +90,11 @@ public String description() {
return description;
}

public Optional<HDDSUpgradeAction> scmAction(UpgradeActionType type) {
return Optional.ofNullable(scmActions.get(type));
public Optional<HDDSUpgradeAction> scmAction() {
return Optional.ofNullable(scmAction);
}

public Optional<HDDSUpgradeAction> datanodeAction(UpgradeActionType type) {
return Optional.ofNullable(datanodeActions.get(type));
public Optional<HDDSUpgradeAction> datanodeAction() {
return Optional.ofNullable(datanodeAction);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ public interface LayoutFeature extends Versioned {

String description();

default Optional<? extends UpgradeAction> action(UpgradeActionType p) {
default Optional<? extends UpgradeAction> action() {
return Optional.empty();
}

/**
* Generic UpgradeAction interface. An operation that is run on specific
* upgrade states like post finalize, pre-downgrade etc.
* Generic UpgradeAction interface. An upgrade action is an operation that
* is run at least once as a pre-requisite to finalizing a layout feature.
* @param <T>
*/
interface UpgradeAction<T> {
Expand All @@ -52,33 +52,4 @@ default String name() {
default int version() {
return this.layoutVersion();
}

/**
* Phase of execution for this action.
*/
enum UpgradeActionType {

// Run every time an un-finalized component is started up.
VALIDATE_IN_PREFINALIZE,

// Run once when an upgraded cluster is started with this new
// layout version.
// If the action fails, it will be run again when the component is
// restarted.
// If updating the VERSION file fails, the action may be run again when the
// component is restarted, even if it finished successfully.
// NOTE 1 : This will not be run in a NEW cluster!
// NOTE 2 : This needs to be a backward compatible action until a DOWNGRADE
// hook is provided!
// Even if the action fails partway through, all on disk structures should
// still be in a backwards compatible state.
// NOTE 3 : These actions are not submitted through RATIS (TODO)
ON_FIRST_UPGRADE_START,

// Run once during finalization of the layout feature.
// If the action fails, it will be run again when finalization is retried.
// If updating the VERSION file fails, the action may be run again when
// finalization is retried, even if it finished successfully.
ON_FINALIZE
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,6 @@ private void startStateMachineThread() throws IOException {
reportManager.init();
initCommandHandlerThread(conf);

upgradeFinalizer.runPrefinalizeStateActions(layoutStorage, this);
LOG.info("Ozone container server started.");
while (context.getState() != DatanodeStates.SHUTDOWN) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package org.apache.hadoop.ozone.container.upgrade;

import static org.apache.hadoop.hdds.upgrade.HDDSLayoutFeature.WITNESSED_CONTAINER_DB_PROTO_VALUE;
import static org.apache.hadoop.ozone.upgrade.LayoutFeature.UpgradeActionType.ON_FINALIZE;
import static org.apache.hadoop.ozone.upgrade.UpgradeActionHdds.Component.DATANODE;

import org.apache.hadoop.hdds.scm.container.ContainerID;
Expand All @@ -40,7 +39,7 @@
/**
* Upgrade Action for DataNode for update the table schema data of containerIds Table.
*/
@UpgradeActionHdds(feature = WITNESSED_CONTAINER_DB_PROTO_VALUE, component = DATANODE, type = ON_FINALIZE)
@UpgradeActionHdds(feature = WITNESSED_CONTAINER_DB_PROTO_VALUE, component = DATANODE)
public class ContainerTableSchemaFinalizeAction
implements HDDSUpgradeAction<DatanodeStateMachine> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
import org.apache.hadoop.hdds.upgrade.HDDSLayoutFeature;
import org.apache.hadoop.hdds.upgrade.HDDSLayoutVersionManager;
import org.apache.hadoop.ozone.common.Storage;
import org.apache.hadoop.ozone.container.common.interfaces.Container;
import org.apache.hadoop.ozone.container.common.statemachine.DatanodeStateMachine;
import org.apache.hadoop.ozone.upgrade.BasicUpgradeFinalizer;
Expand Down Expand Up @@ -83,7 +82,7 @@ public void finalizeLayoutFeature(LayoutFeature layoutFeature,
HDDSLayoutFeature hddslayoutFeature = (HDDSLayoutFeature)layoutFeature;
super.finalizeLayoutFeature(hddslayoutFeature,
hddslayoutFeature
.datanodeAction(LayoutFeature.UpgradeActionType.ON_FINALIZE),
.datanodeAction(),
dsm.getLayoutStorage());
} else {
String msg = String.format("Failed to finalize datanode layout feature " +
Expand All @@ -92,12 +91,4 @@ public void finalizeLayoutFeature(LayoutFeature layoutFeature,
UpgradeException.ResultCodes.LAYOUT_FEATURE_FINALIZATION_FAILED);
}
}

@Override
public void runPrefinalizeStateActions(Storage storage,
DatanodeStateMachine dsm)
throws IOException {
super.runPrefinalizeStateActions(
lf -> ((HDDSLayoutFeature) lf)::datanodeAction, storage, dsm);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package org.apache.hadoop.ozone.container.upgrade;

import static org.apache.hadoop.hdds.upgrade.HDDSLayoutFeature.DATANODE_SCHEMA_V2;
import static org.apache.hadoop.ozone.upgrade.LayoutFeature.UpgradeActionType.ON_FINALIZE;
import static org.apache.hadoop.ozone.upgrade.UpgradeActionHdds.Component.DATANODE;

import org.apache.hadoop.hdds.upgrade.HDDSUpgradeAction;
Expand All @@ -30,8 +29,7 @@
/**
* Upgrade Action for DataNode for the very first first Upgrade Version.
*/
@UpgradeActionHdds(feature = DATANODE_SCHEMA_V2, component = DATANODE,
type = ON_FINALIZE)
@UpgradeActionHdds(feature = DATANODE_SCHEMA_V2, component = DATANODE)
public class DatanodeSchemaV2FinalizeAction
implements HDDSUpgradeAction<DatanodeStateMachine> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package org.apache.hadoop.ozone.container.upgrade;

import static org.apache.hadoop.hdds.upgrade.HDDSLayoutFeature.DATANODE_SCHEMA_V3;
import static org.apache.hadoop.ozone.upgrade.LayoutFeature.UpgradeActionType.ON_FINALIZE;
import static org.apache.hadoop.ozone.upgrade.UpgradeActionHdds.Component.DATANODE;

import org.apache.hadoop.hdds.upgrade.HDDSUpgradeAction;
Expand All @@ -36,8 +35,7 @@
/**
* Upgrade Action for DataNode for SCHEMA V3.
*/
@UpgradeActionHdds(feature = DATANODE_SCHEMA_V3, component = DATANODE,
type = ON_FINALIZE)
@UpgradeActionHdds(feature = DATANODE_SCHEMA_V3, component = DATANODE)
public class DatanodeSchemaV3FinalizeAction
implements HDDSUpgradeAction<DatanodeStateMachine> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package org.apache.hadoop.ozone.container.upgrade;

import static org.apache.hadoop.hdds.upgrade.HDDSLayoutFeature.SCM_HA;
import static org.apache.hadoop.ozone.upgrade.LayoutFeature.UpgradeActionType.ON_FINALIZE;
import static org.apache.hadoop.ozone.upgrade.UpgradeActionHdds.Component.DATANODE;

import java.io.File;
Expand All @@ -38,8 +37,7 @@
/**
* Action to run upgrade flow for SCM HA exactly once.
*/
@UpgradeActionHdds(feature = SCM_HA, component = DATANODE,
type = ON_FINALIZE)
@UpgradeActionHdds(feature = SCM_HA, component = DATANODE)
public class ScmHAFinalizeUpgradeActionDatanode
implements HDDSUpgradeAction<DatanodeStateMachine> {
private static final Logger LOG =
Expand Down
24 changes: 4 additions & 20 deletions hadoop-hdds/docs/content/design/upgrade-dev-primer.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ author: Aravindan Vijayan
A layout feature is any new Ozone feature that makes a backward incompatible change to the on disk layout. Each layout feature is associated with a layout version that it defines. A component has a list of monotonically increasing layout features (versions) that it supports.

## Finalizing & Pre-Finalized State
When upgrading a component from an older version to a newer version which has a higher layout version, the component automatically goes into a pre-finalized state after which an explicit finalize action is required from the user to finalize it. In the pre-finalized state, commands/APIs/on disk structures used and created by newer layout features are meant to be unsupported or unused. After finalizing, the newer layout feature APIs are supported.
When upgrading a component from an older version to a newer version which has a higher layout version, the component automatically goes into a pre-finalized state after which an explicit 'finalize' action is required from the user to finalize it. In the pre-finalized state, commands/APIs/on disk structures used and created by newer layout features are meant to be unsupported or unused. After finalizing, the newer layout feature APIs are supported.

## Downgrade
Downgrade to a lower version is allowed from the pre-finalized state. This involves stopping the component, replacing the artifacts to the older version, and then starting it up again.
Expand Down Expand Up @@ -57,30 +57,14 @@ Generic factory which stores different instances of Type 'T' sharded by a key &
A typical use case during upgrade is to have multiple versions of a class / method / object and choose them based on the current layout version at runtime. Before finalizing, an older version is typically needed, and after finalization, a newer version is needed. This class serves this purpose in a generic way. For example, we can create a Factory to create multiple versions of OMRequests sharded by Request Type & Layout Version Supported.

## Upgrade Action (UpgradeActionOm & UpgradeActionHdds)
Annotation to specify upgrade action run during specific upgrade phases. Each layout feature can optionally define an upgrade action for every supported phase. These are the supported phases of action callbacks.

#### VALIDATE_IN_PREFINALIZE
A ‘validation’ action run every time a component is started up with this layout feature being unfinalized.

- Example: Stopping a component if a new configuration is used prior to it being finalized.

- Example: Cleaning up from a failed ON_FINALIZE action that may have left on disk data in an inoperable state.
- Note that because the ON_FINALIZE action failed, the feature remains pre-finalized.

#### ON_FIRST_UPGRADE_START
A backward compatible action run once when an upgraded cluster is detected with this new layout version. This differs from VALIDATE_IN_PREFINALIZE because it will not be run again once it completes successfully.
This action will be run again if it fails partway through, and may be run again if another error occurs during the upgrade. The action must always leave on disk data in a backwards compatible state, even if it fails partway through, since it is being executed before finalization.

- Example: The new version expects data in a different location even when it is pre-finalized. The action creates a symlink in the new location pointing to the old location.

#### ON_FINALIZE
An action run once during finalization of layout version (feature). This action will be run again if it fails partway through, and may be run again if another error occurs during the upgrade. This is the only action permitted to make backwards incompatible changes to on disk structures, since finalization has been initiated by the time it is run. If a failure partway through could leave the component in an inoperable state, a cleanup action should be used in VALIDATE_IN_PREFINALIZE, which will be run when the component is restarted after a failure.
Annotation to specify upgrade action run during finalization. Each layout feature can optionally define a single upgrade action that will be executed when the feature is finalized. This action should be idempotent and execute quickly. The action must complete for the feature to finish
finalizing, so if there is an error executing the action it will be retried. This partial failure should not leave the component inoperable.

- Example: Adding a new RocksDB column family.

- Example: Logging a message saying a feature is being finalized.

## Prepare the Ozone Manager
## 'Prepare' the Ozone Manager
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 in another OM.

To prepare an OM quorum, run
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ private void registerUpgradeActions(Set<Class<?>> typesAnnotatedWith) {
if (feature.layoutVersion() > getMetadataLayoutVersion()) {
LOG.info("Registering Upgrade Action : {}", action.name());
if (annotation.component() == SCM) {
feature.addScmAction(annotation.type(), action);
feature.addScmAction(action);
} else {
feature.addDatanodeAction(annotation.type(), action);
feature.addDatanodeAction(action);
}
} else {
LOG.debug("Skipping Upgrade Action {} since it has been finalized" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,8 @@

package org.apache.hadoop.ozone.upgrade;

import static org.apache.hadoop.ozone.upgrade.LayoutFeature.UpgradeActionType.ON_FIRST_UPGRADE_START;
import static org.apache.hadoop.ozone.upgrade.LayoutFeature.UpgradeActionType.VALIDATE_IN_PREFINALIZE;
import static org.apache.hadoop.ozone.upgrade.UpgradeException.ResultCodes.FIRST_UPGRADE_START_ACTION_FAILED;
import static org.apache.hadoop.ozone.upgrade.UpgradeException.ResultCodes.INVALID_REQUEST;
import static org.apache.hadoop.ozone.upgrade.UpgradeException.ResultCodes.LAYOUT_FEATURE_FINALIZATION_FAILED;
import static org.apache.hadoop.ozone.upgrade.UpgradeException.ResultCodes.PREFINALIZE_ACTION_VALIDATION_FAILED;
import static org.apache.hadoop.ozone.upgrade.UpgradeException.ResultCodes.UPDATE_LAYOUT_VERSION_FAILED;
import static org.apache.hadoop.ozone.upgrade.UpgradeFinalization.FINALIZATION_IN_PROGRESS_MSG;
import static org.apache.hadoop.ozone.upgrade.UpgradeFinalization.FINALIZATION_REQUIRED_MSG;
Expand All @@ -43,10 +39,8 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import org.apache.hadoop.ozone.common.Storage;
import org.apache.hadoop.ozone.upgrade.LayoutFeature.UpgradeAction;
import org.apache.hadoop.ozone.upgrade.LayoutFeature.UpgradeActionType;
import org.apache.hadoop.ozone.upgrade.UpgradeException.ResultCodes;
import org.apache.hadoop.ozone.upgrade.UpgradeFinalization.Status;
import org.apache.hadoop.ozone.upgrade.UpgradeFinalization.StatusAndMessages;
Expand Down Expand Up @@ -262,7 +256,7 @@ protected void finalizeLayoutFeature(LayoutFeature lf, Optional<?
}

protected void runFinalizationAction(LayoutFeature feature,
Optional<?extends UpgradeAction> action) throws UpgradeException {
Optional<? extends UpgradeAction> action) throws UpgradeException {

if (!action.isPresent()) {
emitNOOPMsg(feature.name());
Expand All @@ -276,79 +270,6 @@ protected void runFinalizationAction(LayoutFeature feature,
}
}

@VisibleForTesting
protected void runPrefinalizeStateActions(Function<LayoutFeature,
Function<UpgradeActionType, Optional<? extends UpgradeAction>>> aFunction,
Storage storage, T service) throws IOException {

if (!versionManager.needsFinalization()) {
return;
}
this.component = service;
LOG.info("Running pre-finalized state validations for unfinalized " +
"layout features.");
for (Object obj : versionManager.unfinalizedFeatures()) {
LayoutFeature lf = (LayoutFeature) obj;
Function<UpgradeActionType, Optional<? extends UpgradeAction>> function =
aFunction.apply(lf);
Optional<? extends UpgradeAction> action =
function.apply(VALIDATE_IN_PREFINALIZE);
if (action.isPresent()) {
runValidationAction(lf, action.get());
}
}

LOG.info("Running first upgrade commands for unfinalized layout features.");
for (Object obj : versionManager.unfinalizedFeatures()) {
LayoutFeature lf = (LayoutFeature) obj;
Function<UpgradeActionType, Optional<? extends UpgradeAction>> function =
aFunction.apply(lf);
Optional<? extends UpgradeAction> action =
function.apply(ON_FIRST_UPGRADE_START);
if (action.isPresent()) {
runFirstUpgradeAction(lf, action.get(), storage);
}
}
}

private void runValidationAction(LayoutFeature f, UpgradeAction action)
throws UpgradeException {
try {
LOG.info("Executing pre finalize state validation {}", action.name());
action.execute(component);
} catch (Exception ex) {
String msg = "Exception while running pre finalize state validation " +
"for feature %s";
LOG.error(String.format(msg, f.name()));
throw new UpgradeException(
String.format(msg, f.name()), ex,
PREFINALIZE_ACTION_VALIDATION_FAILED);
}
}

private void runFirstUpgradeAction(LayoutFeature f, UpgradeAction action,
Storage storage) throws IOException {
try {
int versionOnDisk = storage.getFirstUpgradeActionLayoutVersion();
if (f.layoutVersion() > versionOnDisk) {
LOG.info("Executing first upgrade start action {}", action.name());
action.execute(component);

storage.setFirstUpgradeActionLayoutVersion(f.layoutVersion());
persistStorage(storage);
} else {
LOG.info("Skipping action {} since it has already been run.",
action.name());
}
} catch (Exception ex) {
String msg = "Exception while running first upgrade run actions " +
"for feature %s";
LOG.error(String.format(msg, f.name()));
throw new UpgradeException(
String.format(msg, f.name()), ex, FIRST_UPGRADE_START_ACTION_FAILED);
}
}

protected void updateLayoutVersionInVersionFile(LayoutFeature feature,
Storage config)
throws UpgradeException {
Expand Down
Loading