This is an automated email from the ASF dual-hosted git repository. vinoyang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-hudi.git
The following commit(s) were added to refs/heads/master by this push: new 57132f7 [HUDI-705] Add unit test for RollbacksCommand (#1611) 57132f7 is described below commit 57132f79bb2dad6cfb215480b435a778714a442d Author: hongdd <jn_...@163.com> AuthorDate: Mon May 18 14:04:06 2020 +0800 [HUDI-705] Add unit test for RollbacksCommand (#1611) --- .../apache/hudi/cli/HoodieTableHeaderFields.java | 10 ++ .../apache/hudi/cli/commands/RollbacksCommand.java | 19 ++- .../hudi/cli/commands/TestRollbacksCommand.java | 182 +++++++++++++++++++++ 3 files changed, 204 insertions(+), 7 deletions(-) diff --git a/hudi-cli/src/main/java/org/apache/hudi/cli/HoodieTableHeaderFields.java b/hudi-cli/src/main/java/org/apache/hudi/cli/HoodieTableHeaderFields.java index 5e31e5c..4fc41a1 100644 --- a/hudi-cli/src/main/java/org/apache/hudi/cli/HoodieTableHeaderFields.java +++ b/hudi-cli/src/main/java/org/apache/hudi/cli/HoodieTableHeaderFields.java @@ -23,6 +23,7 @@ package org.apache.hudi.cli; */ public class HoodieTableHeaderFields { public static final String HEADER_PARTITION = "Partition"; + public static final String HEADER_INSTANT = "Instant"; public static final String HEADER_PARTITION_PATH = HEADER_PARTITION + " Path"; public static final String HEADER_FILE_ID = "FileId"; public static final String HEADER_BASE_INSTANT = "Base-Instant"; @@ -81,4 +82,13 @@ public class HoodieTableHeaderFields { public static final String HEADER_HOODIE_PROPERTY = "Property"; public static final String HEADER_OLD_VALUE = "Old Value"; public static final String HEADER_NEW_VALUE = "New Value"; + + /** + * Fields of Rollback. + */ + public static final String HEADER_ROLLBACK_INSTANT = "Rolledback " + HEADER_INSTANT; + public static final String HEADER_TIME_TOKEN_MILLIS = "Time taken in millis"; + public static final String HEADER_TOTAL_PARTITIONS = "Total Partitions"; + public static final String HEADER_DELETED_FILE = "Deleted File"; + public static final String HEADER_SUCCEEDED = "Succeeded"; } diff --git a/hudi-cli/src/main/java/org/apache/hudi/cli/commands/RollbacksCommand.java b/hudi-cli/src/main/java/org/apache/hudi/cli/commands/RollbacksCommand.java index 70b34bc..4feb4c1 100644 --- a/hudi-cli/src/main/java/org/apache/hudi/cli/commands/RollbacksCommand.java +++ b/hudi-cli/src/main/java/org/apache/hudi/cli/commands/RollbacksCommand.java @@ -21,6 +21,7 @@ package org.apache.hudi.cli.commands; import org.apache.hudi.avro.model.HoodieRollbackMetadata; import org.apache.hudi.cli.HoodieCLI; import org.apache.hudi.cli.HoodiePrintHelper; +import org.apache.hudi.cli.HoodieTableHeaderFields; import org.apache.hudi.cli.TableHeader; import org.apache.hudi.common.table.HoodieTableMetaClient; import org.apache.hudi.common.table.timeline.HoodieActiveTimeline; @@ -56,8 +57,7 @@ public class RollbacksCommand implements CommandMarker { @CliOption(key = {"sortBy"}, help = "Sorting Field", unspecifiedDefaultValue = "") final String sortByField, @CliOption(key = {"desc"}, help = "Ordering", unspecifiedDefaultValue = "false") final boolean descending, @CliOption(key = {"headeronly"}, help = "Print Header Only", - unspecifiedDefaultValue = "false") final boolean headerOnly) - throws IOException { + unspecifiedDefaultValue = "false") final boolean headerOnly) { HoodieActiveTimeline activeTimeline = new RollbackTimeline(HoodieCLI.getTableMetaClient()); HoodieTimeline rollback = activeTimeline.getRollbackTimeline().filterCompletedInstants(); @@ -79,9 +79,11 @@ public class RollbacksCommand implements CommandMarker { e.printStackTrace(); } }); - TableHeader header = new TableHeader().addTableHeaderField("Instant").addTableHeaderField("Rolledback Instant") - .addTableHeaderField("Total Files Deleted").addTableHeaderField("Time taken in millis") - .addTableHeaderField("Total Partitions"); + TableHeader header = new TableHeader().addTableHeaderField(HoodieTableHeaderFields.HEADER_INSTANT) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_ROLLBACK_INSTANT) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_TOTAL_FILES_DELETED) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_TIME_TOKEN_MILLIS) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_TOTAL_PARTITIONS); return HoodiePrintHelper.print(header, new HashMap<>(), sortByField, descending, limit, headerOnly, rows); } @@ -112,8 +114,11 @@ public class RollbacksCommand implements CommandMarker { rows.add(row); })); - TableHeader header = new TableHeader().addTableHeaderField("Instant").addTableHeaderField("Rolledback Instants") - .addTableHeaderField("Partition").addTableHeaderField("Deleted File").addTableHeaderField("Succeeded"); + TableHeader header = new TableHeader().addTableHeaderField(HoodieTableHeaderFields.HEADER_INSTANT) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_ROLLBACK_INSTANT) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_PARTITION) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_DELETED_FILE) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_SUCCEEDED); return HoodiePrintHelper.print(header, new HashMap<>(), sortByField, descending, limit, headerOnly, rows); } diff --git a/hudi-cli/src/test/java/org/apache/hudi/cli/commands/TestRollbacksCommand.java b/hudi-cli/src/test/java/org/apache/hudi/cli/commands/TestRollbacksCommand.java new file mode 100644 index 0000000..5a82d77 --- /dev/null +++ b/hudi-cli/src/test/java/org/apache/hudi/cli/commands/TestRollbacksCommand.java @@ -0,0 +1,182 @@ +/* + * 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.cli.commands; + +import org.apache.hudi.avro.model.HoodieRollbackMetadata; +import org.apache.hudi.cli.AbstractShellIntegrationTest; +import org.apache.hudi.cli.HoodieCLI; +import org.apache.hudi.cli.HoodiePrintHelper; +import org.apache.hudi.cli.HoodieTableHeaderFields; +import org.apache.hudi.cli.TableHeader; +import org.apache.hudi.client.HoodieWriteClient; +import org.apache.hudi.common.HoodieTestDataGenerator; +import org.apache.hudi.common.model.HoodieTableType; +import org.apache.hudi.common.model.HoodieTestUtils; +import org.apache.hudi.common.table.timeline.HoodieActiveTimeline; +import org.apache.hudi.common.table.timeline.HoodieInstant; +import org.apache.hudi.common.table.timeline.TimelineMetadataUtils; +import org.apache.hudi.common.table.timeline.versioning.TimelineLayoutVersion; +import org.apache.hudi.common.util.collection.Pair; +import org.apache.hudi.config.HoodieIndexConfig; +import org.apache.hudi.config.HoodieWriteConfig; +import org.apache.hudi.index.HoodieIndex; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.shell.core.CommandResult; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test class for {@link org.apache.hudi.cli.commands.RollbacksCommand}. + */ +public class TestRollbacksCommand extends AbstractShellIntegrationTest { + + @BeforeEach + public void init() throws IOException { + String tableName = "test_table"; + String tablePath = basePath + File.separator + tableName; + new TableCommand().createTable( + tablePath, tableName, HoodieTableType.MERGE_ON_READ.name(), + "", TimelineLayoutVersion.VERSION_1, "org.apache.hudi.common.model.HoodieAvroPayload"); + + //Create some commits files and parquet files + String commitTime1 = "100"; + String commitTime2 = "101"; + String commitTime3 = "102"; + HoodieTestDataGenerator.writePartitionMetadata(fs, HoodieTestDataGenerator.DEFAULT_PARTITION_PATHS, tablePath); + + // two commit files + HoodieTestUtils.createCommitFiles(tablePath, commitTime1, commitTime2); + // one .inflight commit file + HoodieTestUtils.createInflightCommitFiles(tablePath, commitTime3); + + // generate commit files for commits + for (String commitTime : Arrays.asList(commitTime1, commitTime2, commitTime3)) { + HoodieTestUtils.createDataFile(tablePath, HoodieTestDataGenerator.DEFAULT_FIRST_PARTITION_PATH, commitTime, "file-1"); + HoodieTestUtils.createDataFile(tablePath, HoodieTestDataGenerator.DEFAULT_SECOND_PARTITION_PATH, commitTime, "file-2"); + HoodieTestUtils.createDataFile(tablePath, HoodieTestDataGenerator.DEFAULT_THIRD_PARTITION_PATH, commitTime, "file-3"); + } + + // generate two rollback + HoodieWriteConfig config = HoodieWriteConfig.newBuilder().withPath(tablePath) + .withIndexConfig(HoodieIndexConfig.newBuilder().withIndexType(HoodieIndex.IndexType.INMEMORY).build()).build(); + + try (HoodieWriteClient client = new HoodieWriteClient(jsc, config, false)) { + // Rollback inflight commit3 and commit2 + client.rollback(commitTime3); + client.rollback(commitTime2); + } + } + + /** + * Test case for command 'show rollbacks'. + */ + @Test + public void testShowRollbacks() { + CommandResult cr = getShell().executeCommand("show rollbacks"); + assertTrue(cr.isSuccess()); + + // get rollback instants + HoodieActiveTimeline activeTimeline = new RollbacksCommand.RollbackTimeline(HoodieCLI.getTableMetaClient()); + Stream<HoodieInstant> rollback = activeTimeline.getRollbackTimeline().filterCompletedInstants().getInstants(); + + List<Comparable[]> rows = new ArrayList<>(); + rollback.sorted().forEach(instant -> { + try { + // get pair of rollback time and instant time + HoodieRollbackMetadata metadata = TimelineMetadataUtils + .deserializeAvroMetadata(activeTimeline.getInstantDetails(instant).get(), HoodieRollbackMetadata.class); + metadata.getCommitsRollback().forEach(c -> { + Comparable[] row = new Comparable[5]; + row[0] = metadata.getStartRollbackTime(); + row[1] = c; + // expect data + row[2] = 3; + row[3] = 0; + row[4] = 3; + rows.add(row); + }); + } catch (IOException e) { + e.printStackTrace(); + } + }); + + TableHeader header = new TableHeader().addTableHeaderField(HoodieTableHeaderFields.HEADER_INSTANT) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_ROLLBACK_INSTANT) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_TOTAL_FILES_DELETED) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_TIME_TOKEN_MILLIS) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_TOTAL_PARTITIONS); + String expected = HoodiePrintHelper.print(header, new HashMap<>(), "", false, -1, false, rows); + + assertEquals(expected, cr.getResult().toString()); + } + + /** + * Test case for command 'show rollback'. + */ + @Test + public void testShowRollback() throws IOException { + // get instant + HoodieActiveTimeline activeTimeline = new RollbacksCommand.RollbackTimeline(HoodieCLI.getTableMetaClient()); + Stream<HoodieInstant> rollback = activeTimeline.getRollbackTimeline().filterCompletedInstants().getInstants(); + HoodieInstant instant = rollback.findFirst().orElse(null); + assertNotNull(instant, "The instant can not be null."); + + CommandResult cr = getShell().executeCommand("show rollback --instant " + instant.getTimestamp()); + assertTrue(cr.isSuccess()); + + List<Comparable[]> rows = new ArrayList<>(); + // get metadata of instant + HoodieRollbackMetadata metadata = TimelineMetadataUtils.deserializeAvroMetadata( + activeTimeline.getInstantDetails(instant).get(), HoodieRollbackMetadata.class); + // generate expect result + metadata.getPartitionMetadata().forEach((key, value) -> Stream + .concat(value.getSuccessDeleteFiles().stream().map(f -> Pair.of(f, true)), + value.getFailedDeleteFiles().stream().map(f -> Pair.of(f, false))) + .forEach(fileWithDeleteStatus -> { + Comparable[] row = new Comparable[5]; + row[0] = metadata.getStartRollbackTime(); + row[1] = metadata.getCommitsRollback().toString(); + row[2] = key; + row[3] = fileWithDeleteStatus.getLeft(); + row[4] = fileWithDeleteStatus.getRight(); + rows.add(row); + })); + + TableHeader header = new TableHeader().addTableHeaderField(HoodieTableHeaderFields.HEADER_INSTANT) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_ROLLBACK_INSTANT) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_PARTITION) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_DELETED_FILE) + .addTableHeaderField(HoodieTableHeaderFields.HEADER_SUCCEEDED); + String expected = HoodiePrintHelper.print(header, new HashMap<>(), "", false, -1, false, rows); + + assertEquals(expected, cr.getResult().toString()); + } +}