[HUDI-2432] Adding restore.requested instant and restore plan for restore action (#4605)
- This adds a restore plan and serializes it to restore.requested meta file in timeline. This also means that we are introducing schedule and execution phases for restore which was not present before.
This commit is contained in:
committed by
GitHub
parent
0ababcfaa7
commit
e7ec3a82dc
@@ -23,6 +23,7 @@ import org.apache.hudi.avro.model.HoodieCleanerPlan;
|
||||
import org.apache.hudi.avro.model.HoodieClusteringPlan;
|
||||
import org.apache.hudi.avro.model.HoodieCompactionPlan;
|
||||
import org.apache.hudi.avro.model.HoodieRestoreMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRestorePlan;
|
||||
import org.apache.hudi.avro.model.HoodieRollbackMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRollbackPlan;
|
||||
import org.apache.hudi.callback.HoodieWriteCommitCallback;
|
||||
@@ -690,16 +691,21 @@ public abstract class BaseHoodieWriteClient<T extends HoodieRecordPayload, I, K,
|
||||
Timer.Context timerContext = metrics.getRollbackCtx();
|
||||
try {
|
||||
HoodieTable<T, I, K, O> table = createTable(config, hadoopConf, config.isMetadataTableEnabled());
|
||||
HoodieRestoreMetadata restoreMetadata = table.restore(context, restoreInstantTime, instantTime);
|
||||
if (timerContext != null) {
|
||||
final long durationInMs = metrics.getDurationInMs(timerContext.stop());
|
||||
final long totalFilesDeleted = restoreMetadata.getHoodieRestoreMetadata().values().stream()
|
||||
.flatMap(Collection::stream)
|
||||
.mapToLong(HoodieRollbackMetadata::getTotalFilesDeleted)
|
||||
.sum();
|
||||
metrics.updateRollbackMetrics(durationInMs, totalFilesDeleted);
|
||||
Option<HoodieRestorePlan> restorePlanOption = table.scheduleRestore(context, restoreInstantTime, instantTime);
|
||||
if (restorePlanOption.isPresent()) {
|
||||
HoodieRestoreMetadata restoreMetadata = table.restore(context, restoreInstantTime, instantTime);
|
||||
if (timerContext != null) {
|
||||
final long durationInMs = metrics.getDurationInMs(timerContext.stop());
|
||||
final long totalFilesDeleted = restoreMetadata.getHoodieRestoreMetadata().values().stream()
|
||||
.flatMap(Collection::stream)
|
||||
.mapToLong(HoodieRollbackMetadata::getTotalFilesDeleted)
|
||||
.sum();
|
||||
metrics.updateRollbackMetrics(durationInMs, totalFilesDeleted);
|
||||
}
|
||||
return restoreMetadata;
|
||||
} else {
|
||||
throw new HoodieRestoreException("Failed to restore " + config.getBasePath() + " to commit " + instantTime);
|
||||
}
|
||||
return restoreMetadata;
|
||||
} catch (Exception e) {
|
||||
throw new HoodieRestoreException("Failed to restore to " + instantTime, e);
|
||||
}
|
||||
|
||||
@@ -23,4 +23,8 @@ public class HoodieRestoreException extends HoodieException {
|
||||
public HoodieRestoreException(String msg, Throwable e) {
|
||||
super(msg, e);
|
||||
}
|
||||
|
||||
public HoodieRestoreException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.apache.hudi.avro.model.HoodieCleanerPlan;
|
||||
import org.apache.hudi.avro.model.HoodieClusteringPlan;
|
||||
import org.apache.hudi.avro.model.HoodieCompactionPlan;
|
||||
import org.apache.hudi.avro.model.HoodieRestoreMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRestorePlan;
|
||||
import org.apache.hudi.avro.model.HoodieRollbackMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRollbackPlan;
|
||||
import org.apache.hudi.avro.model.HoodieSavepointMetadata;
|
||||
@@ -346,6 +347,13 @@ public abstract class HoodieTable<T extends HoodieRecordPayload, I, K, O> implem
|
||||
return getActiveTimeline().getRollbackTimeline();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get restore timeline.
|
||||
*/
|
||||
public HoodieTimeline getRestoreTimeline() {
|
||||
return getActiveTimeline().getRestoreTimeline();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get only the completed (no-inflights) savepoint timeline.
|
||||
*/
|
||||
@@ -497,6 +505,13 @@ public abstract class HoodieTable<T extends HoodieRecordPayload, I, K, O> implem
|
||||
String restoreInstantTime,
|
||||
String instantToRestore);
|
||||
|
||||
/**
|
||||
* Schedules Restore for the table to the given instant.
|
||||
*/
|
||||
public abstract Option<HoodieRestorePlan> scheduleRestore(HoodieEngineContext context,
|
||||
String restoreInstantTime,
|
||||
String instantToRestore);
|
||||
|
||||
/**
|
||||
* Rollback failed compactions. Inflight rollbacks for compactions revert the .inflight file
|
||||
* to the .requested file.
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
|
||||
package org.apache.hudi.table.action.restore;
|
||||
|
||||
import org.apache.hudi.avro.model.HoodieInstantInfo;
|
||||
import org.apache.hudi.avro.model.HoodieRestoreMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRestorePlan;
|
||||
import org.apache.hudi.avro.model.HoodieRollbackMetadata;
|
||||
import org.apache.hudi.client.transaction.TransactionManager;
|
||||
import org.apache.hudi.common.engine.HoodieEngineContext;
|
||||
@@ -29,14 +31,18 @@ import org.apache.hudi.common.table.timeline.HoodieTimeline;
|
||||
import org.apache.hudi.common.table.timeline.TimelineMetadataUtils;
|
||||
import org.apache.hudi.common.util.HoodieTimer;
|
||||
import org.apache.hudi.common.util.Option;
|
||||
import org.apache.hudi.common.util.ValidationUtils;
|
||||
import org.apache.hudi.config.HoodieWriteConfig;
|
||||
import org.apache.hudi.exception.HoodieRestoreException;
|
||||
import org.apache.hudi.exception.HoodieRollbackException;
|
||||
import org.apache.hudi.table.HoodieTable;
|
||||
import org.apache.hudi.table.action.BaseActionExecutor;
|
||||
|
||||
import org.apache.log4j.LogManager;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -65,29 +71,53 @@ public abstract class BaseRestoreActionExecutor<T extends HoodieRecordPayload, I
|
||||
HoodieTimer restoreTimer = new HoodieTimer();
|
||||
restoreTimer.startTimer();
|
||||
|
||||
// Get all the commits on the timeline after the provided commit time
|
||||
List<HoodieInstant> instantsToRollback = table.getActiveTimeline().getWriteTimeline()
|
||||
.getReverseOrderedInstants()
|
||||
.filter(instant -> HoodieActiveTimeline.GREATER_THAN.test(instant.getTimestamp(), restoreInstantTime))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Map<String, List<HoodieRollbackMetadata>> instantToMetadata = new HashMap<>();
|
||||
table.getActiveTimeline().createNewInstant(new HoodieInstant(true, HoodieTimeline.RESTORE_ACTION, instantTime));
|
||||
instantsToRollback.forEach(instant -> {
|
||||
instantToMetadata.put(instant.getTimestamp(), Collections.singletonList(rollbackInstant(instant)));
|
||||
LOG.info("Deleted instant " + instant);
|
||||
});
|
||||
|
||||
Option<HoodieInstant> restoreInstant = table.getRestoreTimeline()
|
||||
.filterInflightsAndRequested()
|
||||
.filter(instant -> instant.getTimestamp().equals(instantTime))
|
||||
.firstInstant();
|
||||
if (!restoreInstant.isPresent()) {
|
||||
throw new HoodieRollbackException("No pending restore instants found to execute restore");
|
||||
}
|
||||
try {
|
||||
List<HoodieInstant> instantsToRollback = getInstantsToRollback(restoreInstant.get());
|
||||
ValidationUtils.checkArgument(restoreInstant.get().getState().equals(HoodieInstant.State.REQUESTED)
|
||||
|| restoreInstant.get().getState().equals(HoodieInstant.State.INFLIGHT));
|
||||
Map<String, List<HoodieRollbackMetadata>> instantToMetadata = new HashMap<>();
|
||||
if (restoreInstant.get().isRequested()) {
|
||||
table.getActiveTimeline().transitionRestoreRequestedToInflight(restoreInstant.get());
|
||||
}
|
||||
|
||||
instantsToRollback.forEach(instant -> {
|
||||
instantToMetadata.put(instant.getTimestamp(), Collections.singletonList(rollbackInstant(instant)));
|
||||
LOG.info("Deleted instant " + instant);
|
||||
});
|
||||
|
||||
return finishRestore(instantToMetadata,
|
||||
instantsToRollback,
|
||||
restoreTimer.endTimer()
|
||||
);
|
||||
} catch (IOException io) {
|
||||
throw new HoodieRollbackException("unable to rollback instants " + instantsToRollback, io);
|
||||
throw new HoodieRestoreException("unable to Restore instant " + restoreInstant.get(), io);
|
||||
}
|
||||
}
|
||||
|
||||
private List<HoodieInstant> getInstantsToRollback(HoodieInstant restoreInstant) throws IOException {
|
||||
List<HoodieInstant> instantsToRollback = new ArrayList<>();
|
||||
HoodieRestorePlan restorePlan = RestoreUtils.getRestorePlan(table.getMetaClient(), restoreInstant);
|
||||
for (HoodieInstantInfo instantInfo : restorePlan.getInstantsToRollback()) {
|
||||
// If restore crashed mid-way, there are chances that some commits are already rolled back,
|
||||
// but some are not. so, we can ignore those commits which are fully rolledback in previous attempt if any.
|
||||
Option<HoodieInstant> rollbackInstantOpt = table.getActiveTimeline().getWriteTimeline()
|
||||
.filter(instant -> instant.getTimestamp().equals(instantInfo.getCommitTime()) && instant.getAction().equals(instantInfo.getAction())).firstInstant();
|
||||
if (rollbackInstantOpt.isPresent()) {
|
||||
instantsToRollback.add(rollbackInstantOpt.get());
|
||||
} else {
|
||||
LOG.warn("Ignoring already rolledback instant " + instantInfo.toString());
|
||||
}
|
||||
}
|
||||
return instantsToRollback;
|
||||
}
|
||||
|
||||
protected abstract HoodieRollbackMetadata rollbackInstant(HoodieInstant rollbackInstant);
|
||||
|
||||
private HoodieRestoreMetadata finishRestore(Map<String, List<HoodieRollbackMetadata>> instantToMetadata,
|
||||
@@ -99,7 +129,7 @@ public abstract class BaseRestoreActionExecutor<T extends HoodieRecordPayload, I
|
||||
writeToMetadata(restoreMetadata);
|
||||
table.getActiveTimeline().saveAsComplete(new HoodieInstant(true, HoodieTimeline.RESTORE_ACTION, instantTime),
|
||||
TimelineMetadataUtils.serializeRestoreMetadata(restoreMetadata));
|
||||
// get all rollbacks instants after restore instant time and delete them.
|
||||
// get all pending rollbacks instants after restore instant time and delete them.
|
||||
// if not, rollbacks will be considered not completed and might hinder metadata table compaction.
|
||||
List<HoodieInstant> instantsToRollback = table.getActiveTimeline().getRollbackTimeline()
|
||||
.getReverseOrderedInstants()
|
||||
@@ -115,6 +145,7 @@ public abstract class BaseRestoreActionExecutor<T extends HoodieRecordPayload, I
|
||||
|
||||
/**
|
||||
* Update metadata table if available. Any update to metadata table happens within data table lock.
|
||||
*
|
||||
* @param restoreMetadata instance of {@link HoodieRestoreMetadata} to be applied to metadata.
|
||||
*/
|
||||
private void writeToMetadata(HoodieRestoreMetadata restoreMetadata) {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.hudi.table.action.restore;
|
||||
|
||||
import org.apache.hudi.avro.model.HoodieRestorePlan;
|
||||
import org.apache.hudi.common.table.HoodieTableMetaClient;
|
||||
import org.apache.hudi.common.table.timeline.HoodieInstant;
|
||||
import org.apache.hudi.common.table.timeline.HoodieTimeline;
|
||||
import org.apache.hudi.common.table.timeline.TimelineMetadataUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class RestoreUtils {
|
||||
|
||||
/**
|
||||
* Get Latest version of Restore plan corresponding to a restore instant.
|
||||
*
|
||||
* @param metaClient Hoodie Table Meta Client
|
||||
* @param restoreInstant Instant referring to restore action
|
||||
* @return Rollback plan corresponding to rollback instant
|
||||
* @throws IOException
|
||||
*/
|
||||
public static HoodieRestorePlan getRestorePlan(HoodieTableMetaClient metaClient, HoodieInstant restoreInstant)
|
||||
throws IOException {
|
||||
final HoodieInstant requested = HoodieTimeline.getRollbackRequestedInstant(restoreInstant);
|
||||
return TimelineMetadataUtils.deserializeAvroMetadata(
|
||||
metaClient.getActiveTimeline().readRestoreInfoAsBytes(requested).get(), HoodieRestorePlan.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hudi.table.action.rollback;
|
||||
|
||||
import org.apache.hudi.avro.model.HoodieInstantInfo;
|
||||
import org.apache.hudi.avro.model.HoodieRestorePlan;
|
||||
import org.apache.hudi.common.engine.HoodieEngineContext;
|
||||
import org.apache.hudi.common.model.HoodieRecordPayload;
|
||||
import org.apache.hudi.common.table.timeline.HoodieActiveTimeline;
|
||||
import org.apache.hudi.common.table.timeline.HoodieInstant;
|
||||
import org.apache.hudi.common.table.timeline.HoodieTimeline;
|
||||
import org.apache.hudi.common.table.timeline.TimelineMetadataUtils;
|
||||
import org.apache.hudi.common.util.Option;
|
||||
import org.apache.hudi.config.HoodieWriteConfig;
|
||||
import org.apache.hudi.exception.HoodieIOException;
|
||||
import org.apache.hudi.table.HoodieTable;
|
||||
import org.apache.hudi.table.action.BaseActionExecutor;
|
||||
|
||||
import org.apache.log4j.LogManager;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Plans the restore action and add a restore.requested meta file to timeline.
|
||||
*/
|
||||
public class RestorePlanActionExecutor<T extends HoodieRecordPayload, I, K, O> extends BaseActionExecutor<T, I, K, O, Option<HoodieRestorePlan>> {
|
||||
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(RestorePlanActionExecutor.class);
|
||||
|
||||
public static final Integer RESTORE_PLAN_VERSION_1 = 1;
|
||||
public static final Integer LATEST_RESTORE_PLAN_VERSION = RESTORE_PLAN_VERSION_1;
|
||||
private final String restoreInstantTime;
|
||||
|
||||
public RestorePlanActionExecutor(HoodieEngineContext context,
|
||||
HoodieWriteConfig config,
|
||||
HoodieTable<T, I, K, O> table,
|
||||
String instantTime,
|
||||
String restoreInstantTime) {
|
||||
super(context, config, table, instantTime);
|
||||
this.restoreInstantTime = restoreInstantTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Option<HoodieRestorePlan> execute() {
|
||||
final HoodieInstant restoreInstant = new HoodieInstant(HoodieInstant.State.REQUESTED, HoodieTimeline.RESTORE_ACTION, instantTime);
|
||||
try {
|
||||
// Get all the commits on the timeline after the provided commit time
|
||||
List<HoodieInstantInfo> instantsToRollback = table.getActiveTimeline().getWriteTimeline()
|
||||
.getReverseOrderedInstants()
|
||||
.filter(instant -> HoodieActiveTimeline.GREATER_THAN.test(instant.getTimestamp(), restoreInstantTime)).map(entry -> new HoodieInstantInfo(entry.getTimestamp(), entry.getAction()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
HoodieRestorePlan restorePlan = new HoodieRestorePlan(instantsToRollback, LATEST_RESTORE_PLAN_VERSION);
|
||||
table.getActiveTimeline().saveToRestoreRequested(restoreInstant, TimelineMetadataUtils.serializeRestorePlan(restorePlan));
|
||||
table.getMetaClient().reloadActiveTimeline();
|
||||
LOG.info("Requesting Restore with instant time " + restoreInstant);
|
||||
return Option.of(restorePlan);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Got exception when saving restore requested file", e);
|
||||
throw new HoodieIOException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import org.apache.hudi.avro.model.HoodieCleanerPlan;
|
||||
import org.apache.hudi.avro.model.HoodieClusteringPlan;
|
||||
import org.apache.hudi.avro.model.HoodieCompactionPlan;
|
||||
import org.apache.hudi.avro.model.HoodieRestoreMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRestorePlan;
|
||||
import org.apache.hudi.avro.model.HoodieRollbackMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRollbackPlan;
|
||||
import org.apache.hudi.avro.model.HoodieSavepointMetadata;
|
||||
@@ -342,6 +343,11 @@ public class HoodieFlinkCopyOnWriteTable<T extends HoodieRecordPayload>
|
||||
throw new HoodieNotSupportedException("Savepoint is not supported yet");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Option<HoodieRestorePlan> scheduleRestore(HoodieEngineContext context, String restoreInstantTime, String instantToRestore) {
|
||||
throw new HoodieNotSupportedException("Restore is not supported yet");
|
||||
}
|
||||
|
||||
@Override
|
||||
public HoodieRestoreMetadata restore(HoodieEngineContext context, String restoreInstantTime, String instantToRestore) {
|
||||
throw new HoodieNotSupportedException("Savepoint and restore is not supported yet");
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.apache.hudi.avro.model.HoodieCleanerPlan;
|
||||
import org.apache.hudi.avro.model.HoodieClusteringPlan;
|
||||
import org.apache.hudi.avro.model.HoodieCompactionPlan;
|
||||
import org.apache.hudi.avro.model.HoodieRestoreMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRestorePlan;
|
||||
import org.apache.hudi.avro.model.HoodieRollbackMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRollbackPlan;
|
||||
import org.apache.hudi.avro.model.HoodieSavepointMetadata;
|
||||
@@ -63,6 +64,7 @@ import org.apache.hudi.table.action.commit.JavaUpsertPreppedCommitActionExecutor
|
||||
import org.apache.hudi.table.action.restore.CopyOnWriteRestoreActionExecutor;
|
||||
import org.apache.hudi.table.action.rollback.BaseRollbackPlanActionExecutor;
|
||||
import org.apache.hudi.table.action.rollback.CopyOnWriteRollbackActionExecutor;
|
||||
import org.apache.hudi.table.action.rollback.RestorePlanActionExecutor;
|
||||
import org.apache.hudi.table.action.savepoint.SavepointActionExecutor;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -247,6 +249,11 @@ public class HoodieJavaCopyOnWriteTable<T extends HoodieRecordPayload>
|
||||
context, config, this, instantToSavepoint, user, comment).execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Option<HoodieRestorePlan> scheduleRestore(HoodieEngineContext context, String restoreInstantTime, String instantToRestore) {
|
||||
return new RestorePlanActionExecutor(context, config, this, restoreInstantTime, instantToRestore).execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HoodieRestoreMetadata restore(HoodieEngineContext context,
|
||||
String restoreInstantTime,
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.apache.hudi.avro.model.HoodieCleanerPlan;
|
||||
import org.apache.hudi.avro.model.HoodieClusteringPlan;
|
||||
import org.apache.hudi.avro.model.HoodieCompactionPlan;
|
||||
import org.apache.hudi.avro.model.HoodieRestoreMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRestorePlan;
|
||||
import org.apache.hudi.avro.model.HoodieRollbackMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRollbackPlan;
|
||||
import org.apache.hudi.avro.model.HoodieSavepointMetadata;
|
||||
@@ -74,6 +75,7 @@ import org.apache.hudi.table.action.commit.SparkUpsertPreppedCommitActionExecuto
|
||||
import org.apache.hudi.table.action.restore.CopyOnWriteRestoreActionExecutor;
|
||||
import org.apache.hudi.table.action.rollback.BaseRollbackPlanActionExecutor;
|
||||
import org.apache.hudi.table.action.rollback.CopyOnWriteRollbackActionExecutor;
|
||||
import org.apache.hudi.table.action.rollback.RestorePlanActionExecutor;
|
||||
import org.apache.hudi.table.action.savepoint.SavepointActionExecutor;
|
||||
|
||||
import org.apache.avro.Schema;
|
||||
@@ -258,6 +260,7 @@ public class HoodieSparkCopyOnWriteTable<T extends HoodieRecordPayload>
|
||||
|
||||
@Override
|
||||
public void rollbackBootstrap(HoodieEngineContext context, String instantTime) {
|
||||
new RestorePlanActionExecutor<>(context, config, this, instantTime, HoodieTimeline.INIT_INSTANT_TS).execute();
|
||||
new CopyOnWriteRestoreActionExecutor(context, config, this, instantTime, HoodieTimeline.INIT_INSTANT_TS).execute();
|
||||
}
|
||||
|
||||
@@ -353,4 +356,8 @@ public class HoodieSparkCopyOnWriteTable<T extends HoodieRecordPayload>
|
||||
return new CopyOnWriteRestoreActionExecutor(context, config, this, restoreInstantTime, instantToRestore).execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Option<HoodieRestorePlan> scheduleRestore(HoodieEngineContext context, String restoreInstantTime, String instantToRestore) {
|
||||
return new RestorePlanActionExecutor(context, config, this, restoreInstantTime, instantToRestore).execute();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import org.apache.hudi.table.action.deltacommit.SparkUpsertPreppedDeltaCommitAct
|
||||
import org.apache.hudi.table.action.restore.MergeOnReadRestoreActionExecutor;
|
||||
import org.apache.hudi.table.action.rollback.BaseRollbackPlanActionExecutor;
|
||||
import org.apache.hudi.table.action.rollback.MergeOnReadRollbackActionExecutor;
|
||||
import org.apache.hudi.table.action.rollback.RestorePlanActionExecutor;
|
||||
|
||||
import org.apache.spark.api.java.JavaRDD;
|
||||
|
||||
@@ -150,6 +151,7 @@ public class HoodieSparkMergeOnReadTable<T extends HoodieRecordPayload> extends
|
||||
|
||||
@Override
|
||||
public void rollbackBootstrap(HoodieEngineContext context, String instantTime) {
|
||||
new RestorePlanActionExecutor<>(context, config, this, instantTime, HoodieTimeline.INIT_INSTANT_TS).execute();
|
||||
new MergeOnReadRestoreActionExecutor(context, config, this, instantTime, HoodieTimeline.INIT_INSTANT_TS).execute();
|
||||
}
|
||||
|
||||
|
||||
@@ -576,6 +576,7 @@ public class TestHoodieClientOnCopyOnWriteStorage extends HoodieClientTestBase {
|
||||
HoodieWriteConfig newConfig = getConfigBuilder().withProps(config.getProps()).withTimelineLayoutVersion(
|
||||
TimelineLayoutVersion.CURR_VERSION).build();
|
||||
client = getHoodieWriteClient(newConfig);
|
||||
|
||||
client.restoreToInstant("004");
|
||||
|
||||
assertFalse(metaClient.reloadActiveTimeline().getRollbackTimeline().lastInstant().isPresent());
|
||||
|
||||
37
hudi-common/src/main/avro/HoodieRestorePlan.avsc
Normal file
37
hudi-common/src/main/avro/HoodieRestorePlan.avsc
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
{
|
||||
"namespace":"org.apache.hudi.avro.model",
|
||||
"type":"record",
|
||||
"name":"HoodieRestorePlan",
|
||||
"fields":[
|
||||
{
|
||||
"name": "instantsToRollback",
|
||||
"default": [],
|
||||
"type": {
|
||||
"type": "array",
|
||||
"default": null,
|
||||
"items": "HoodieInstantInfo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name":"version",
|
||||
"type":["int", "null"],
|
||||
"default": 1
|
||||
}]
|
||||
}
|
||||
@@ -70,7 +70,7 @@ public class HoodieActiveTimeline extends HoodieDefaultTimeline {
|
||||
SAVEPOINT_EXTENSION, INFLIGHT_SAVEPOINT_EXTENSION,
|
||||
CLEAN_EXTENSION, REQUESTED_CLEAN_EXTENSION, INFLIGHT_CLEAN_EXTENSION,
|
||||
INFLIGHT_COMPACTION_EXTENSION, REQUESTED_COMPACTION_EXTENSION,
|
||||
INFLIGHT_RESTORE_EXTENSION, RESTORE_EXTENSION,
|
||||
REQUESTED_RESTORE_EXTENSION, INFLIGHT_RESTORE_EXTENSION, RESTORE_EXTENSION,
|
||||
ROLLBACK_EXTENSION, REQUESTED_ROLLBACK_EXTENSION, INFLIGHT_ROLLBACK_EXTENSION,
|
||||
REQUESTED_REPLACE_COMMIT_EXTENSION, INFLIGHT_REPLACE_COMMIT_EXTENSION, REPLACE_COMMIT_EXTENSION));
|
||||
private static final Logger LOG = LogManager.getLogger(HoodieActiveTimeline.class);
|
||||
@@ -289,6 +289,11 @@ public class HoodieActiveTimeline extends HoodieDefaultTimeline {
|
||||
return readDataFromPath(new Path(metaClient.getMetaPath(), instant.getFileName()));
|
||||
}
|
||||
|
||||
public Option<byte[]> readRestoreInfoAsBytes(HoodieInstant instant) {
|
||||
// Rollback metadata are always stored only in timeline .hoodie
|
||||
return readDataFromPath(new Path(metaClient.getMetaPath(), instant.getFileName()));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// BEGIN - COMPACTION RELATED META-DATA MANAGEMENT.
|
||||
//-----------------------------------------------------------------
|
||||
@@ -429,6 +434,21 @@ public class HoodieActiveTimeline extends HoodieDefaultTimeline {
|
||||
return inflight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transition Restore State from requested to inflight.
|
||||
*
|
||||
* @param requestedInstant requested instant
|
||||
* @return commit instant
|
||||
*/
|
||||
public HoodieInstant transitionRestoreRequestedToInflight(HoodieInstant requestedInstant) {
|
||||
ValidationUtils.checkArgument(requestedInstant.getAction().equals(HoodieTimeline.RESTORE_ACTION), "Transition to inflight requested for a restore instant with diff action "
|
||||
+ requestedInstant.toString());
|
||||
ValidationUtils.checkArgument(requestedInstant.isRequested(), "Transition to inflight requested for an instant not in requested state " + requestedInstant.toString());
|
||||
HoodieInstant inflight = new HoodieInstant(State.INFLIGHT, RESTORE_ACTION, requestedInstant.getTimestamp());
|
||||
transitionState(requestedInstant, inflight, Option.empty());
|
||||
return inflight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transition replace requested file to replace inflight.
|
||||
*
|
||||
@@ -599,6 +619,13 @@ public class HoodieActiveTimeline extends HoodieDefaultTimeline {
|
||||
createFileInMetaPath(instant.getFileName(), content, false);
|
||||
}
|
||||
|
||||
public void saveToRestoreRequested(HoodieInstant instant, Option<byte[]> content) {
|
||||
ValidationUtils.checkArgument(instant.getAction().equals(HoodieTimeline.RESTORE_ACTION));
|
||||
ValidationUtils.checkArgument(instant.getState().equals(State.REQUESTED));
|
||||
// Plan is stored in meta path
|
||||
createFileInMetaPath(instant.getFileName(), content, false);
|
||||
}
|
||||
|
||||
private void createFileInMetaPath(String filename, Option<byte[]> content, boolean allowOverwrite) {
|
||||
Path fullPath = new Path(metaClient.getMetaPath(), filename);
|
||||
if (allowOverwrite || metaClient.getTimelineLayoutVersion().isNullVersion()) {
|
||||
|
||||
@@ -166,6 +166,7 @@ public class HoodieInstant implements Serializable, Comparable<HoodieInstant> {
|
||||
}
|
||||
} else if (HoodieTimeline.RESTORE_ACTION.equals(action)) {
|
||||
return isInflight() ? HoodieTimeline.makeInflightRestoreFileName(timestamp)
|
||||
: isRequested() ? HoodieTimeline.makeRequestedRestoreFileName(timestamp)
|
||||
: HoodieTimeline.makeRestoreFileName(timestamp);
|
||||
} else if (HoodieTimeline.REPLACE_COMMIT_ACTION.equals(action)) {
|
||||
return isInflight() ? HoodieTimeline.makeInflightReplaceFileName(timestamp)
|
||||
|
||||
@@ -78,6 +78,7 @@ public interface HoodieTimeline extends Serializable {
|
||||
String REQUESTED_COMPACTION_SUFFIX = StringUtils.join(COMPACTION_ACTION, REQUESTED_EXTENSION);
|
||||
String REQUESTED_COMPACTION_EXTENSION = StringUtils.join(".", REQUESTED_COMPACTION_SUFFIX);
|
||||
String INFLIGHT_COMPACTION_EXTENSION = StringUtils.join(".", COMPACTION_ACTION, INFLIGHT_EXTENSION);
|
||||
String REQUESTED_RESTORE_EXTENSION = "." + RESTORE_ACTION + REQUESTED_EXTENSION;
|
||||
String INFLIGHT_RESTORE_EXTENSION = "." + RESTORE_ACTION + INFLIGHT_EXTENSION;
|
||||
String RESTORE_EXTENSION = "." + RESTORE_ACTION;
|
||||
String INFLIGHT_REPLACE_COMMIT_EXTENSION = "." + REPLACE_COMMIT_ACTION + INFLIGHT_EXTENSION;
|
||||
@@ -386,6 +387,10 @@ public interface HoodieTimeline extends Serializable {
|
||||
return StringUtils.join(instant, HoodieTimeline.REQUESTED_ROLLBACK_EXTENSION);
|
||||
}
|
||||
|
||||
static String makeRequestedRestoreFileName(String instant) {
|
||||
return StringUtils.join(instant, HoodieTimeline.REQUESTED_RESTORE_EXTENSION);
|
||||
}
|
||||
|
||||
static String makeInflightRollbackFileName(String instant) {
|
||||
return StringUtils.join(instant, HoodieTimeline.INFLIGHT_ROLLBACK_EXTENSION);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.apache.hudi.avro.model.HoodieInstantInfo;
|
||||
import org.apache.hudi.avro.model.HoodieReplaceCommitMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRequestedReplaceMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRestoreMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRestorePlan;
|
||||
import org.apache.hudi.avro.model.HoodieRollbackMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRollbackPartitionMetadata;
|
||||
import org.apache.hudi.avro.model.HoodieRollbackPlan;
|
||||
@@ -112,6 +113,10 @@ public class TimelineMetadataUtils {
|
||||
return serializeAvroMetadata(rollbackPlan, HoodieRollbackPlan.class);
|
||||
}
|
||||
|
||||
public static Option<byte[]> serializeRestorePlan(HoodieRestorePlan restorePlan) throws IOException {
|
||||
return serializeAvroMetadata(restorePlan, HoodieRestorePlan.class);
|
||||
}
|
||||
|
||||
public static Option<byte[]> serializeCleanMetadata(HoodieCleanMetadata metadata) throws IOException {
|
||||
return serializeAvroMetadata(metadata, HoodieCleanMetadata.class);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user