Skip to content

Commit de4e738

Browse files
committed
Adding rollback plan and rollback requested instant
1 parent 2791fb9 commit de4e738

35 files changed

Lines changed: 1044 additions & 415 deletions

File tree

hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/AbstractHoodieWriteClient.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.hudi.avro.model.HoodieCompactionPlan;
2525
import org.apache.hudi.avro.model.HoodieRestoreMetadata;
2626
import org.apache.hudi.avro.model.HoodieRollbackMetadata;
27+
import org.apache.hudi.avro.model.HoodieRollbackPlan;
2728
import org.apache.hudi.callback.HoodieWriteCommitCallback;
2829
import org.apache.hudi.callback.common.HoodieWriteCommitCallbackMessage;
2930
import org.apache.hudi.callback.util.HoodieCommitCallbackFactory;
@@ -590,12 +591,19 @@ public boolean rollback(final String commitInstantTime) throws HoodieRollbackExc
590591
.filter(instant -> HoodieActiveTimeline.EQUALS.test(instant.getTimestamp(), commitInstantTime))
591592
.findFirst());
592593
if (commitInstantOpt.isPresent()) {
593-
HoodieRollbackMetadata rollbackMetadata = table.rollback(context, rollbackInstantTime, commitInstantOpt.get(), true);
594-
if (timerContext != null) {
595-
long durationInMs = metrics.getDurationInMs(timerContext.stop());
596-
metrics.updateRollbackMetrics(durationInMs, rollbackMetadata.getTotalFilesDeleted());
594+
LOG.info("Scheduling Rollback at instant time :" + rollbackInstantTime);
595+
Option<HoodieRollbackPlan> rollbackPlanOption = table.scheduleRollback(context, rollbackInstantTime, commitInstantOpt.get(), false);
596+
if (rollbackPlanOption.isPresent()) {
597+
// execute rollback
598+
HoodieRollbackMetadata rollbackMetadata = table.rollback(context, rollbackInstantTime, commitInstantOpt.get(), true);
599+
if (timerContext != null) {
600+
long durationInMs = metrics.getDurationInMs(timerContext.stop());
601+
metrics.updateRollbackMetrics(durationInMs, rollbackMetadata.getTotalFilesDeleted());
602+
}
603+
return true;
604+
} else {
605+
throw new HoodieRollbackException("Failed to rollback " + config.getBasePath() + " commits " + commitInstantTime);
597606
}
598-
return true;
599607
} else {
600608
LOG.warn("Cannot find instant " + commitInstantTime + " in the timeline, for rollback");
601609
return false;
@@ -776,7 +784,9 @@ protected abstract void completeCompaction(HoodieCommitMetadata metadata, O writ
776784
* @param table Hoodie Table
777785
*/
778786
public void rollbackInflightCompaction(HoodieInstant inflightInstant, HoodieTable<T, I, K, O> table) {
779-
table.rollback(context, HoodieActiveTimeline.createNewInstantTime(), inflightInstant, false);
787+
String commitTime = HoodieActiveTimeline.createNewInstantTime();
788+
table.scheduleRollback(context, commitTime, inflightInstant, false);
789+
table.rollback(context, commitTime, inflightInstant, false);
780790
table.getActiveTimeline().revertCompactionInflightToRequested(inflightInstant);
781791
}
782792

@@ -978,7 +988,9 @@ protected Option<String> inlineCluster(Option<Map<String, String>> extraMetadata
978988
}
979989

980990
protected void rollbackInflightClustering(HoodieInstant inflightInstant, HoodieTable<T, I, K, O> table) {
981-
table.rollback(context, HoodieActiveTimeline.createNewInstantTime(), inflightInstant, false);
991+
String commitTime = HoodieActiveTimeline.createNewInstantTime();
992+
table.scheduleRollback(context, commitTime, inflightInstant, false);
993+
table.rollback(context, commitTime, inflightInstant, false);
982994
table.getActiveTimeline().revertReplaceCommitInflightToRequested(inflightInstant);
983995
}
984996

hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/HoodieTable.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.apache.hudi.avro.model.HoodieCompactionPlan;
2626
import org.apache.hudi.avro.model.HoodieRestoreMetadata;
2727
import org.apache.hudi.avro.model.HoodieRollbackMetadata;
28+
import org.apache.hudi.avro.model.HoodieRollbackPlan;
2829
import org.apache.hudi.avro.model.HoodieSavepointMetadata;
2930
import org.apache.hudi.common.config.HoodieMetadataConfig;
3031
import org.apache.hudi.common.config.SerializableConfiguration;
@@ -316,6 +317,13 @@ public HoodieTimeline getCleanTimeline() {
316317
return getActiveTimeline().getCleanerTimeline();
317318
}
318319

320+
/**
321+
* Get rollback timeline.
322+
*/
323+
public HoodieTimeline getRollbackTimeline() {
324+
return getActiveTimeline().getRollbackTimeline();
325+
}
326+
319327
/**
320328
* Get only the completed (no-inflights) savepoint timeline.
321329
*/
@@ -417,6 +425,19 @@ public abstract Option<HoodieCleanerPlan> scheduleCleaning(HoodieEngineContext c
417425
*/
418426
public abstract HoodieCleanMetadata clean(HoodieEngineContext context, String cleanInstantTime);
419427

428+
/**
429+
* Schedule rollback for the instant time.
430+
*
431+
* @param context HoodieEngineContext
432+
* @param instantTime Instant Time for scheduling rollback
433+
* @param instantToRollback instant to be rolled back
434+
* @return HoodieRollbackPlan containing info on rollback.
435+
*/
436+
public abstract Option<HoodieRollbackPlan> scheduleRollback(HoodieEngineContext context,
437+
String instantTime,
438+
HoodieInstant instantToRollback,
439+
boolean skipTimelinePublish);
440+
420441
/**
421442
* Rollback the (inflight/committed) record changes with the given commit time.
422443
* <pre>

hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/action/rollback/BaseRollbackActionExecutor.java

Lines changed: 77 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package org.apache.hudi.table.action.rollback;
2020

2121
import org.apache.hudi.avro.model.HoodieRollbackMetadata;
22+
import org.apache.hudi.avro.model.HoodieRollbackPlan;
2223
import org.apache.hudi.client.heartbeat.HoodieHeartbeatClient;
2324
import org.apache.hudi.common.HoodieRollbackStat;
2425
import org.apache.hudi.common.bootstrap.index.BootstrapIndex;
@@ -43,7 +44,6 @@
4344
import org.apache.log4j.Logger;
4445

4546
import java.io.IOException;
46-
import java.io.Serializable;
4747
import java.util.Collections;
4848
import java.util.List;
4949
import java.util.Objects;
@@ -53,11 +53,6 @@ public abstract class BaseRollbackActionExecutor<T extends HoodieRecordPayload,
5353

5454
private static final Logger LOG = LogManager.getLogger(BaseRollbackActionExecutor.class);
5555

56-
interface RollbackStrategy extends Serializable {
57-
58-
List<HoodieRollbackStat> execute(HoodieInstant instantToRollback);
59-
}
60-
6156
protected final HoodieInstant instantToRollback;
6257
protected final boolean deleteInstants;
6358
protected final boolean skipTimelinePublish;
@@ -92,30 +87,74 @@ public BaseRollbackActionExecutor(HoodieEngineContext context,
9287
}
9388
}
9489

95-
protected abstract RollbackStrategy getRollbackStrategy();
90+
/**
91+
* Execute actual rollback and fetch list of RollbackStats.
92+
* @param hoodieRollbackPlan instance of {@link HoodieRollbackPlan} that needs to be executed.
93+
* @return a list of {@link HoodieRollbackStat}s.
94+
* @throws IOException
95+
*/
96+
protected abstract List<HoodieRollbackStat> executeRollback(HoodieRollbackPlan hoodieRollbackPlan) throws IOException;
97+
98+
private HoodieRollbackMetadata runRollback(HoodieTable<T, I, K, O> table, HoodieInstant rollbackInstant, HoodieRollbackPlan rollbackPlan) {
99+
ValidationUtils.checkArgument(rollbackInstant.getState().equals(HoodieInstant.State.REQUESTED)
100+
|| rollbackInstant.getState().equals(HoodieInstant.State.INFLIGHT));
101+
try {
102+
final HoodieInstant inflightInstant;
103+
final HoodieTimer timer = new HoodieTimer();
104+
timer.startTimer();
105+
if (rollbackInstant.isRequested()) {
106+
inflightInstant = table.getActiveTimeline().transitionRollbackRequestedToInflight(rollbackInstant,
107+
TimelineMetadataUtils.serializeRollbackPlan(rollbackPlan));
108+
} else {
109+
inflightInstant = rollbackInstant;
110+
}
111+
112+
HoodieTimer rollbackTimer = new HoodieTimer().startTimer();
113+
List<HoodieRollbackStat> stats = doRollbackAndGetStats(rollbackPlan);
114+
HoodieRollbackMetadata rollbackMetadata = TimelineMetadataUtils.convertRollbackMetadata(
115+
instantTime,
116+
Option.of(rollbackTimer.endTimer()),
117+
Collections.singletonList(instantToRollback),
118+
stats);
119+
if (!skipTimelinePublish) {
120+
finishRollback(inflightInstant, rollbackMetadata);
121+
}
96122

97-
protected abstract List<HoodieRollbackStat> executeRollback() throws IOException;
123+
// Finally, remove the markers post rollback.
124+
WriteMarkersFactory.get(config.getMarkersType(), table, instantToRollback.getTimestamp())
125+
.quietDeleteMarkerDir(context, config.getMarkersDeleteParallelism());
98126

99-
protected abstract List<HoodieRollbackStat> executeRollbackUsingFileListing(HoodieInstant instantToRollback);
127+
return rollbackMetadata;
128+
} catch (IOException e) {
129+
throw new HoodieIOException("Failed to rollback commit ", e);
130+
}
131+
}
100132

101133
@Override
102134
public HoodieRollbackMetadata execute() {
103-
HoodieTimer rollbackTimer = new HoodieTimer().startTimer();
104-
List<HoodieRollbackStat> stats = doRollbackAndGetStats();
105-
HoodieRollbackMetadata rollbackMetadata = TimelineMetadataUtils.convertRollbackMetadata(
106-
instantTime,
107-
Option.of(rollbackTimer.endTimer()),
108-
Collections.singletonList(instantToRollback),
109-
stats);
110-
if (!skipTimelinePublish) {
111-
finishRollback(rollbackMetadata);
135+
table.getMetaClient().reloadActiveTimeline();
136+
List<HoodieInstant> rollBackInstants = table.getRollbackTimeline()
137+
.filterInflightsAndRequested().getInstants().collect(Collectors.toList());
138+
if (rollBackInstants.isEmpty()) {
139+
throw new HoodieRollbackException("No Requested Rollback Instants found to execute rollback ");
140+
}
141+
HoodieInstant rollbackInstant = null;
142+
for (HoodieInstant instant : rollBackInstants) {
143+
if (instantTime.equals(instant.getTimestamp())) {
144+
rollbackInstant = instant;
145+
break;
146+
}
147+
}
148+
if (rollbackInstant != null) {
149+
try {
150+
HoodieRollbackPlan rollbackPlan = RollbackUtils.getRollbackPlan(table.getMetaClient(), rollbackInstant);
151+
return runRollback(table, rollBackInstants.get(0), rollbackPlan);
152+
} catch (IOException e) {
153+
throw new HoodieIOException("Failed to fetch rollback plan to rollback commit " + rollbackInstant.getTimestamp(), e);
154+
}
155+
} else {
156+
throw new HoodieIOException("No inflight rollback instants found for commit time " + instantTime);
112157
}
113-
114-
// Finally, remove the markers post rollback.
115-
WriteMarkersFactory.get(config.getMarkersType(), table, instantToRollback.getTimestamp())
116-
.quietDeleteMarkerDir(context, config.getMarkersDeleteParallelism());
117-
118-
return rollbackMetadata;
119158
}
120159

121160
private void validateSavepointRollbacks() {
@@ -173,7 +212,7 @@ private void rollBackIndex() {
173212
LOG.info("Index rolled back for commits " + instantToRollback);
174213
}
175214

176-
public List<HoodieRollbackStat> doRollbackAndGetStats() {
215+
public List<HoodieRollbackStat> doRollbackAndGetStats(HoodieRollbackPlan hoodieRollbackPlan) {
177216
final String instantTimeToRollback = instantToRollback.getTimestamp();
178217
final boolean isPendingCompaction = Objects.equals(HoodieTimeline.COMPACTION_ACTION, instantToRollback.getAction())
179218
&& !instantToRollback.isCompleted();
@@ -186,7 +225,7 @@ public List<HoodieRollbackStat> doRollbackAndGetStats() {
186225
}
187226

188227
try {
189-
List<HoodieRollbackStat> stats = executeRollback();
228+
List<HoodieRollbackStat> stats = executeRollback(hoodieRollbackPlan);
190229
LOG.info("Rolled back inflight instant " + instantTimeToRollback);
191230
if (!isPendingCompaction) {
192231
rollBackIndex();
@@ -197,12 +236,19 @@ public List<HoodieRollbackStat> doRollbackAndGetStats() {
197236
}
198237
}
199238

200-
protected void finishRollback(HoodieRollbackMetadata rollbackMetadata) throws HoodieIOException {
239+
/**
240+
* Execute rollback and fetch rollback stats.
241+
* @param instantToRollback instant to be rolled back.
242+
* @param rollbackPlan instance of {@link HoodieRollbackPlan} for which rollback needs to be executed.
243+
* @return list of {@link HoodieRollbackStat}s.
244+
*/
245+
protected List<HoodieRollbackStat> executeRollback(HoodieInstant instantToRollback, HoodieRollbackPlan rollbackPlan) {
246+
return new BaseRollbackHelper(table.getMetaClient(), config).performRollback(context, instantToRollback, rollbackPlan.getRollbackRequests());
247+
}
248+
249+
protected void finishRollback(HoodieInstant inflightInstant, HoodieRollbackMetadata rollbackMetadata) throws HoodieIOException {
201250
try {
202-
table.getActiveTimeline().createNewInstant(
203-
new HoodieInstant(HoodieInstant.State.INFLIGHT, HoodieTimeline.ROLLBACK_ACTION, instantTime));
204-
table.getActiveTimeline().saveAsComplete(
205-
new HoodieInstant(true, HoodieTimeline.ROLLBACK_ACTION, instantTime),
251+
table.getActiveTimeline().transitionRollbackInflightToComplete(inflightInstant,
206252
TimelineMetadataUtils.serializeRollbackMetadata(rollbackMetadata));
207253
LOG.info("Rollback of Commits " + rollbackMetadata.getCommitsRollback() + " is complete");
208254
} catch (IOException e) {

0 commit comments

Comments
 (0)