You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hudi.apache.org by "waterWang (via GitHub)" <gi...@apache.org> on 2026/08/12 06:25:50 UTC
[PR] feat(cli): add checkpoint get/set commands for ingestion checkpoint inspection and repair [FaaFyfxR9WAQrL7FcAgEHJvztd8cVMxvjHRS55rw1nwH] [hudi]
waterWang opened a new pull request, #19588:
URL: https://github.com/apache/hudi/pull/19588
Adds two new hudi-cli commands (`checkpoint get` and `checkpoint set`) that allow operators to inspect and modify the ingestion checkpoint value stored in a Hudi table's commit metadata — without manually parsing or editing commit metadata JSON files.
## Changes
### `checkpoint get`
Reads the latest ingestion instant from the active timeline and displays the checkpoint value along with commit statistics (timestamp, action, state, records/bytes/partitions/files written).
### `checkpoint set`
Safely overwrites the checkpoint value in the latest completed ingestion commit's metadata. Supports both DeltaStreamer checkpoints (default) and arbitrary custom checkpoint keys (e.g. `latest-ingestion-timestamp`). Includes safety guardrails:
- Requires explicit `--force true` confirmation
- Backs up the original commit file to `.checkpoint_backup/` before modification
- Verifies the write by re-reading the timeline
- Preserves the backup for manual recovery on failure
### Parameters
| Parameter | Description | Default |
|---|---|---|
| `--checkpointValue` | The new checkpoint value to set | (required) |
| `--isDeltaStreamer` | Whether to target the DeltaStreamer checkpoint key | `true` |
| `--customCheckpointKey` | Custom checkpoint key name (used when `isDeltaStreamer=false`) | `""` |
| `--force` | Bypass the safety confirmation warning | `false` |
### Usage examples
```bash
# Connect to table
connect --path hdfs:///path/to/table
# Inspect current checkpoint
checkpoint get
# Set DeltaStreamer checkpoint
checkpoint set --checkpointValue 20251021164840000 --force true
# Set a custom checkpoint key
checkpoint set --checkpointValue 20251021164840000 --isDeltaStreamer false --customCheckpointKey latest-ingestion-timestamp --force true
```
This addresses the feature request in #19577.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(cli): add checkpoint get/set commands for ingestion checkpoint inspection and repair [FaaFyfxR9WAQrL7FcAgEHJvztd8cVMxvjHRS55rw1nwH] [hudi]
Posted by "voonhous (via GitHub)" <gi...@apache.org>.
voonhous closed pull request #19588: feat(cli): add checkpoint get/set commands for ingestion checkpoint inspection and repair [FaaFyfxR9WAQrL7FcAgEHJvztd8cVMxvjHRS55rw1nwH]
URL: https://github.com/apache/hudi/pull/19588
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(cli): add checkpoint get/set commands for ingestion checkpoint inspection and repair [FaaFyfxR9WAQrL7FcAgEHJvztd8cVMxvjHRS55rw1nwH] [hudi]
Posted by "hudi-agent (via GitHub)" <gi...@apache.org>.
hudi-agent commented on code in PR #19588:
URL: https://github.com/apache/hudi/pull/19588#discussion_r3764179876
##########
hudi-cli/src/main/java/org/apache/hudi/cli/commands/CheckpointCommand.java:
##########
@@ -0,0 +1,232 @@
+/*
+ * 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.cli.HoodieCLI;
+import org.apache.hudi.cli.HoodiePrintHelper;
+import org.apache.hudi.cli.TableHeader;
+import org.apache.hudi.common.model.HoodieCommitMetadata;
+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.TimelineUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.StringUtils;
+import org.apache.hudi.common.util.collection.Pair;
+import org.apache.hudi.exception.HoodieException;
+import org.apache.hudi.storage.HoodieInstantWriter;
+import org.apache.hudi.storage.StoragePath;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+import org.springframework.shell.standard.ShellOption;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV1.STREAMER_CHECKPOINT_KEY_V1;
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV1.STREAMER_CHECKPOINT_RESET_KEY_V1;
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV2.STREAMER_CHECKPOINT_KEY_V2;
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV2.STREAMER_CHECKPOINT_RESET_KEY_V2;
+
+/**
+ * CLI command to inspect and repair ingestion checkpoints stored in commit metadata.
+ */
+@ShellComponent
+@Slf4j
+public class CheckpointCommand {
+
+ private static final String[] STREAMER_CHECKPOINT_KEYS = {
+ STREAMER_CHECKPOINT_KEY_V1, STREAMER_CHECKPOINT_RESET_KEY_V1,
+ STREAMER_CHECKPOINT_KEY_V2, STREAMER_CHECKPOINT_RESET_KEY_V2
+ };
+
+ private static final String BACKUP_DIR_NAME = ".checkpoint_backup";
+
+ /**
+ * Find the latest ingestion instant that stores a checkpoint in its commit metadata.
+ */
+ private Option<Pair<HoodieInstant, HoodieCommitMetadata>> findLatestCheckpointInstant(
+ HoodieTimeline timeline, String... keys) {
+ return TimelineUtils.findLatestInCommitMetadata(
+ timeline.filterCompletedInstants(), (instant, metadata) -> {
+ boolean hasCheckpoint = Arrays.stream(keys)
+ .anyMatch(key -> !StringUtils.isNullOrEmpty(metadata.getMetadata(key)));
+ return hasCheckpoint ? Option.of(Pair.of(instant, metadata)) : Option.empty();
+ });
+ }
+
+ @ShellMethod(key = "checkpoint get", value = "Display the ingestion checkpoint value stored in the latest ingestion commit")
+ public String getCheckpoint(
+ @ShellOption(value = {"--checkpointKey"}, help = "Checkpoint metadata key to read (defaults to all streamer checkpoint keys)",
+ defaultValue = "") final String checkpointKey) {
+ HoodieTableMetaClient metaClient = HoodieCLI.getTableMetaClient();
+ try {
+ HoodieTimeline timeline = metaClient.getActiveTimeline();
+ String[] keys = StringUtils.isNullOrEmpty(checkpointKey) ? STREAMER_CHECKPOINT_KEYS : new String[] {checkpointKey};
+ Option<Pair<HoodieInstant, HoodieCommitMetadata>> checkpointInfo = findLatestCheckpointInstant(timeline, keys);
+ if (checkpointInfo.isEmpty()) {
+ return "No checkpoint found in the active timeline. The table has no ingestion commit with a checkpoint "
+ + "metadata key matching: " + String.join(", ", keys);
+ }
+
+ HoodieInstant instant = checkpointInfo.get().getLeft();
+ HoodieCommitMetadata commitMetadata = checkpointInfo.get().getRight();
+
+ final List<Comparable[]> rows = new ArrayList<>();
+ for (String key : keys) {
+ String value = commitMetadata.getMetadata(key);
+ if (value != null) {
+ rows.add(new Comparable[] {key, value});
+ }
+ }
+ if (rows.isEmpty()) {
+ rows.add(new Comparable[] {keys.length == 1 ? keys[0] : "checkpoint", ""});
+ }
+
+ final Map<String, Function<Object, String>> fieldNameToConverterMap = new HashMap<>();
+ final TableHeader header = new TableHeader()
+ .addTableHeaderField("Checkpoint-Key")
+ .addTableHeaderField("Checkpoint-Value");
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("Ingestion checkpoint found in commit ").append(instant.requestedTime())
+ .append(" (").append(instant.getAction()).append(", ").append(instant.getState()).append(")\n");
+ sb.append("Commit statistics: records=").append(commitMetadata.fetchTotalRecordsWritten())
+ .append(", bytes=").append(commitMetadata.fetchTotalBytesWritten())
+ .append(", partitions=").append(commitMetadata.fetchTotalPartitionsWritten())
+ .append(", filesInserted=").append(commitMetadata.fetchTotalFilesInsert())
+ .append(", filesUpdated=").append(commitMetadata.fetchTotalFilesUpdated())
+ .append(", errors=").append(commitMetadata.fetchTotalWriteErrors()).append("\n\n");
+ sb.append(HoodiePrintHelper.print(header, fieldNameToConverterMap, "", false, -1, false, rows, ""));
+ return sb.toString();
+ } catch (IOException e) {
+ log.error("Error reading checkpoint", e);
+ return "Error reading checkpoint: " + e.getMessage();
+ }
+ }
+
+ @ShellMethod(key = "checkpoint set", value = "Overwrite the ingestion checkpoint value in the latest completed ingestion commit")
+ public String setCheckpoint(
+ @ShellOption(value = {"--checkpointValue"}, help = "The new checkpoint value to set") final String checkpointValue,
+ @ShellOption(value = {"--isDeltaStreamer"}, help = "Whether to target the DeltaStreamer checkpoint key",
+ defaultValue = "true") final boolean isDeltaStreamer,
+ @ShellOption(value = {"--customCheckpointKey"}, help = "Custom checkpoint key name (used when isDeltaStreamer=false)",
+ defaultValue = "") final String customCheckpointKey,
+ @ShellOption(value = {"--force"}, help = "Bypass the safety confirmation warning",
+ defaultValue = "false") final boolean force) {
+ if (!force) {
+ return "This is a destructive operation: it rewrites commit metadata. "
+ + "Re-run with --force true to proceed. The original commit file is backed up to "
+ + BACKUP_DIR_NAME + "/ before modification.";
+ }
+ if (StringUtils.isNullOrEmpty(checkpointValue)) {
+ return "Error: --checkpointValue cannot be empty.";
+ }
+
+ String targetKey;
+ if (isDeltaStreamer) {
+ targetKey = STREAMER_CHECKPOINT_KEY_V1;
Review Comment:
🤖 With `--isDeltaStreamer true` this always writes `STREAMER_CHECKPOINT_KEY_V1`, but `CheckpointUtils.getCheckpoint()` prefers the V2 keys — if the target commit stored a V2 checkpoint, the streamer will keep reading the old V2 value and silently ignore the value you just set. Should the target key be derived from which checkpoint key the found commit actually contains (V1 vs V2) rather than hard-coded to V1?
<sub><i>⚠️ AI-generated; verify before applying. React 👍/👎 to flag quality.</i></sub>
##########
hudi-cli/src/main/java/org/apache/hudi/cli/commands/CheckpointCommand.java:
##########
@@ -0,0 +1,232 @@
+/*
+ * 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.cli.HoodieCLI;
+import org.apache.hudi.cli.HoodiePrintHelper;
+import org.apache.hudi.cli.TableHeader;
+import org.apache.hudi.common.model.HoodieCommitMetadata;
+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.TimelineUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.StringUtils;
+import org.apache.hudi.common.util.collection.Pair;
+import org.apache.hudi.exception.HoodieException;
+import org.apache.hudi.storage.HoodieInstantWriter;
+import org.apache.hudi.storage.StoragePath;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+import org.springframework.shell.standard.ShellOption;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV1.STREAMER_CHECKPOINT_KEY_V1;
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV1.STREAMER_CHECKPOINT_RESET_KEY_V1;
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV2.STREAMER_CHECKPOINT_KEY_V2;
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV2.STREAMER_CHECKPOINT_RESET_KEY_V2;
+
+/**
+ * CLI command to inspect and repair ingestion checkpoints stored in commit metadata.
+ */
+@ShellComponent
+@Slf4j
+public class CheckpointCommand {
+
+ private static final String[] STREAMER_CHECKPOINT_KEYS = {
+ STREAMER_CHECKPOINT_KEY_V1, STREAMER_CHECKPOINT_RESET_KEY_V1,
+ STREAMER_CHECKPOINT_KEY_V2, STREAMER_CHECKPOINT_RESET_KEY_V2
+ };
+
+ private static final String BACKUP_DIR_NAME = ".checkpoint_backup";
+
+ /**
+ * Find the latest ingestion instant that stores a checkpoint in its commit metadata.
+ */
+ private Option<Pair<HoodieInstant, HoodieCommitMetadata>> findLatestCheckpointInstant(
+ HoodieTimeline timeline, String... keys) {
+ return TimelineUtils.findLatestInCommitMetadata(
+ timeline.filterCompletedInstants(), (instant, metadata) -> {
+ boolean hasCheckpoint = Arrays.stream(keys)
+ .anyMatch(key -> !StringUtils.isNullOrEmpty(metadata.getMetadata(key)));
+ return hasCheckpoint ? Option.of(Pair.of(instant, metadata)) : Option.empty();
+ });
+ }
+
+ @ShellMethod(key = "checkpoint get", value = "Display the ingestion checkpoint value stored in the latest ingestion commit")
+ public String getCheckpoint(
+ @ShellOption(value = {"--checkpointKey"}, help = "Checkpoint metadata key to read (defaults to all streamer checkpoint keys)",
+ defaultValue = "") final String checkpointKey) {
+ HoodieTableMetaClient metaClient = HoodieCLI.getTableMetaClient();
+ try {
+ HoodieTimeline timeline = metaClient.getActiveTimeline();
+ String[] keys = StringUtils.isNullOrEmpty(checkpointKey) ? STREAMER_CHECKPOINT_KEYS : new String[] {checkpointKey};
+ Option<Pair<HoodieInstant, HoodieCommitMetadata>> checkpointInfo = findLatestCheckpointInstant(timeline, keys);
+ if (checkpointInfo.isEmpty()) {
+ return "No checkpoint found in the active timeline. The table has no ingestion commit with a checkpoint "
+ + "metadata key matching: " + String.join(", ", keys);
+ }
+
+ HoodieInstant instant = checkpointInfo.get().getLeft();
+ HoodieCommitMetadata commitMetadata = checkpointInfo.get().getRight();
+
+ final List<Comparable[]> rows = new ArrayList<>();
+ for (String key : keys) {
+ String value = commitMetadata.getMetadata(key);
+ if (value != null) {
+ rows.add(new Comparable[] {key, value});
+ }
+ }
+ if (rows.isEmpty()) {
+ rows.add(new Comparable[] {keys.length == 1 ? keys[0] : "checkpoint", ""});
+ }
+
+ final Map<String, Function<Object, String>> fieldNameToConverterMap = new HashMap<>();
+ final TableHeader header = new TableHeader()
+ .addTableHeaderField("Checkpoint-Key")
+ .addTableHeaderField("Checkpoint-Value");
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("Ingestion checkpoint found in commit ").append(instant.requestedTime())
+ .append(" (").append(instant.getAction()).append(", ").append(instant.getState()).append(")\n");
+ sb.append("Commit statistics: records=").append(commitMetadata.fetchTotalRecordsWritten())
+ .append(", bytes=").append(commitMetadata.fetchTotalBytesWritten())
+ .append(", partitions=").append(commitMetadata.fetchTotalPartitionsWritten())
+ .append(", filesInserted=").append(commitMetadata.fetchTotalFilesInsert())
+ .append(", filesUpdated=").append(commitMetadata.fetchTotalFilesUpdated())
+ .append(", errors=").append(commitMetadata.fetchTotalWriteErrors()).append("\n\n");
+ sb.append(HoodiePrintHelper.print(header, fieldNameToConverterMap, "", false, -1, false, rows, ""));
+ return sb.toString();
+ } catch (IOException e) {
+ log.error("Error reading checkpoint", e);
+ return "Error reading checkpoint: " + e.getMessage();
+ }
+ }
+
+ @ShellMethod(key = "checkpoint set", value = "Overwrite the ingestion checkpoint value in the latest completed ingestion commit")
+ public String setCheckpoint(
+ @ShellOption(value = {"--checkpointValue"}, help = "The new checkpoint value to set") final String checkpointValue,
+ @ShellOption(value = {"--isDeltaStreamer"}, help = "Whether to target the DeltaStreamer checkpoint key",
+ defaultValue = "true") final boolean isDeltaStreamer,
+ @ShellOption(value = {"--customCheckpointKey"}, help = "Custom checkpoint key name (used when isDeltaStreamer=false)",
+ defaultValue = "") final String customCheckpointKey,
+ @ShellOption(value = {"--force"}, help = "Bypass the safety confirmation warning",
+ defaultValue = "false") final boolean force) {
+ if (!force) {
+ return "This is a destructive operation: it rewrites commit metadata. "
+ + "Re-run with --force true to proceed. The original commit file is backed up to "
+ + BACKUP_DIR_NAME + "/ before modification.";
+ }
+ if (StringUtils.isNullOrEmpty(checkpointValue)) {
+ return "Error: --checkpointValue cannot be empty.";
+ }
+
+ String targetKey;
+ if (isDeltaStreamer) {
+ targetKey = STREAMER_CHECKPOINT_KEY_V1;
+ } else {
+ if (StringUtils.isNullOrEmpty(customCheckpointKey)) {
+ return "Error: --customCheckpointKey is required when --isDeltaStreamer false.";
+ }
+ targetKey = customCheckpointKey;
+ }
+
+ HoodieTableMetaClient metaClient = HoodieCLI.getTableMetaClient();
+ try {
+ HoodieTimeline timeline = metaClient.getActiveTimeline();
+ Option<Pair<HoodieInstant, HoodieCommitMetadata>> checkpointInfo =
+ findLatestCheckpointInstant(timeline, STREAMER_CHECKPOINT_KEYS);
+ if (checkpointInfo.isEmpty()) {
+ return "No ingestion commit with a checkpoint found in the active timeline. Nothing to update.";
+ }
+
+ HoodieInstant instant = checkpointInfo.get().getLeft();
+ HoodieCommitMetadata commitMetadata = checkpointInfo.get().getRight();
+
+ // Back up the original commit file before modification.
+ StoragePath timelinePath = new StoragePath(metaClient.getTimelinePath());
+ StoragePath backupDir = new StoragePath(timelinePath, BACKUP_DIR_NAME);
+ StoragePath instantFileName = new StoragePath(timelinePath,
+ metaClient.getInstantFileNameGenerator().getFileName(instant));
+ StoragePath backupPath = new StoragePath(backupDir,
+ metaClient.getInstantFileNameGenerator().getFileName(instant));
+
+ if (!metaClient.getStorage().exists(backupDir)) {
+ metaClient.getStorage().createDirectory(backupDir);
+ }
+ String backupCode;
+ if (metaClient.getStorage().exists(backupPath)) {
+ backupCode = "already-present";
+ } else {
+ // Manual copy: read the original file and write to backup.
+ try (InputStream in = metaClient.getStorage().open(instantFileName);
+ OutputStream out = metaClient.getStorage().create(backupPath, false)) {
+ byte[] buf = new byte[8192];
+ int n;
+ while ((n = in.read(buf)) != -1) {
+ out.write(buf, 0, n);
+ }
+ }
+ backupCode = "created";
+ }
+
+ // Modify the checkpoint value and write back.
+ commitMetadata.addMetadata(targetKey, checkpointValue);
+ writeCommitMetadata(metaClient, instant, commitMetadata);
+
+ // Verify by re-reading the timeline.
+ Option<Pair<HoodieInstant, HoodieCommitMetadata>> verified =
+ findLatestCheckpointInstant(metaClient.getActiveTimeline(), targetKey);
+ if (verified.isEmpty() || !checkpointValue.equals(verified.get().getRight().getMetadata(targetKey))) {
+ return "Checkpoint write could not be verified. "
+ + "The original commit file was backed up to " + backupPath + " — restore it manually if needed.";
+ }
+
+ return "Checkpoint updated in commit " + instant.requestedTime() + ": " + targetKey + " = " + checkpointValue
+ + "\nOriginal commit file backed up to " + backupPath + " (" + backupCode + ").";
+ } catch (IOException e) {
+ log.error("Error updating checkpoint", e);
+ return "Error updating checkpoint: " + e.getMessage();
+ }
+ }
+
+ private void writeCommitMetadata(HoodieTableMetaClient metaClient, HoodieInstant instant, HoodieCommitMetadata metadata)
+ throws IOException {
+ Option<HoodieInstantWriter> writerOpt = metaClient.getActiveTimeline().getInstantWriter(Option.of(metadata));
+ if (writerOpt.isEmpty()) {
+ throw new HoodieException("Unable to create instant writer for " + instant);
+ }
+ StoragePath instantPath = new StoragePath(new StoragePath(metaClient.getTimelinePath()),
+ metaClient.getInstantFileNameGenerator().getFileName(instant));
+ try (OutputStream os = metaClient.getStorage().create(instantPath, true)) {
Review Comment:
🤖 `writeCommitMetadata` overwrites the completed instant file in place with `create(instantPath, true)` (truncate). If `writeToStream` throws partway through, the completed commit file is left truncated/corrupt — and the IOException handler at line 216 returns only a generic message that doesn't mention the backup path, so the operator has no idea a `.checkpoint_backup/` copy exists to restore from. Could you surface the backup location in the failure path (and ideally write to a temp file + atomic rename instead of truncating the live file)?
<sub><i>⚠️ AI-generated; verify before applying. React 👍/👎 to flag quality.</i></sub>
##########
hudi-cli/src/main/java/org/apache/hudi/cli/commands/CheckpointCommand.java:
##########
@@ -0,0 +1,232 @@
+/*
+ * 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.cli.HoodieCLI;
+import org.apache.hudi.cli.HoodiePrintHelper;
+import org.apache.hudi.cli.TableHeader;
+import org.apache.hudi.common.model.HoodieCommitMetadata;
+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.TimelineUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.StringUtils;
+import org.apache.hudi.common.util.collection.Pair;
+import org.apache.hudi.exception.HoodieException;
+import org.apache.hudi.storage.HoodieInstantWriter;
+import org.apache.hudi.storage.StoragePath;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+import org.springframework.shell.standard.ShellOption;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV1.STREAMER_CHECKPOINT_KEY_V1;
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV1.STREAMER_CHECKPOINT_RESET_KEY_V1;
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV2.STREAMER_CHECKPOINT_KEY_V2;
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV2.STREAMER_CHECKPOINT_RESET_KEY_V2;
+
+/**
+ * CLI command to inspect and repair ingestion checkpoints stored in commit metadata.
+ */
+@ShellComponent
+@Slf4j
+public class CheckpointCommand {
+
+ private static final String[] STREAMER_CHECKPOINT_KEYS = {
+ STREAMER_CHECKPOINT_KEY_V1, STREAMER_CHECKPOINT_RESET_KEY_V1,
+ STREAMER_CHECKPOINT_KEY_V2, STREAMER_CHECKPOINT_RESET_KEY_V2
+ };
+
+ private static final String BACKUP_DIR_NAME = ".checkpoint_backup";
+
+ /**
+ * Find the latest ingestion instant that stores a checkpoint in its commit metadata.
+ */
+ private Option<Pair<HoodieInstant, HoodieCommitMetadata>> findLatestCheckpointInstant(
+ HoodieTimeline timeline, String... keys) {
+ return TimelineUtils.findLatestInCommitMetadata(
+ timeline.filterCompletedInstants(), (instant, metadata) -> {
+ boolean hasCheckpoint = Arrays.stream(keys)
+ .anyMatch(key -> !StringUtils.isNullOrEmpty(metadata.getMetadata(key)));
+ return hasCheckpoint ? Option.of(Pair.of(instant, metadata)) : Option.empty();
+ });
+ }
+
+ @ShellMethod(key = "checkpoint get", value = "Display the ingestion checkpoint value stored in the latest ingestion commit")
+ public String getCheckpoint(
+ @ShellOption(value = {"--checkpointKey"}, help = "Checkpoint metadata key to read (defaults to all streamer checkpoint keys)",
+ defaultValue = "") final String checkpointKey) {
+ HoodieTableMetaClient metaClient = HoodieCLI.getTableMetaClient();
+ try {
+ HoodieTimeline timeline = metaClient.getActiveTimeline();
+ String[] keys = StringUtils.isNullOrEmpty(checkpointKey) ? STREAMER_CHECKPOINT_KEYS : new String[] {checkpointKey};
+ Option<Pair<HoodieInstant, HoodieCommitMetadata>> checkpointInfo = findLatestCheckpointInstant(timeline, keys);
+ if (checkpointInfo.isEmpty()) {
+ return "No checkpoint found in the active timeline. The table has no ingestion commit with a checkpoint "
+ + "metadata key matching: " + String.join(", ", keys);
+ }
+
+ HoodieInstant instant = checkpointInfo.get().getLeft();
+ HoodieCommitMetadata commitMetadata = checkpointInfo.get().getRight();
+
+ final List<Comparable[]> rows = new ArrayList<>();
+ for (String key : keys) {
+ String value = commitMetadata.getMetadata(key);
+ if (value != null) {
+ rows.add(new Comparable[] {key, value});
+ }
+ }
+ if (rows.isEmpty()) {
+ rows.add(new Comparable[] {keys.length == 1 ? keys[0] : "checkpoint", ""});
+ }
+
+ final Map<String, Function<Object, String>> fieldNameToConverterMap = new HashMap<>();
+ final TableHeader header = new TableHeader()
+ .addTableHeaderField("Checkpoint-Key")
+ .addTableHeaderField("Checkpoint-Value");
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("Ingestion checkpoint found in commit ").append(instant.requestedTime())
+ .append(" (").append(instant.getAction()).append(", ").append(instant.getState()).append(")\n");
+ sb.append("Commit statistics: records=").append(commitMetadata.fetchTotalRecordsWritten())
+ .append(", bytes=").append(commitMetadata.fetchTotalBytesWritten())
+ .append(", partitions=").append(commitMetadata.fetchTotalPartitionsWritten())
+ .append(", filesInserted=").append(commitMetadata.fetchTotalFilesInsert())
+ .append(", filesUpdated=").append(commitMetadata.fetchTotalFilesUpdated())
+ .append(", errors=").append(commitMetadata.fetchTotalWriteErrors()).append("\n\n");
+ sb.append(HoodiePrintHelper.print(header, fieldNameToConverterMap, "", false, -1, false, rows, ""));
+ return sb.toString();
+ } catch (IOException e) {
+ log.error("Error reading checkpoint", e);
+ return "Error reading checkpoint: " + e.getMessage();
+ }
+ }
+
+ @ShellMethod(key = "checkpoint set", value = "Overwrite the ingestion checkpoint value in the latest completed ingestion commit")
+ public String setCheckpoint(
+ @ShellOption(value = {"--checkpointValue"}, help = "The new checkpoint value to set") final String checkpointValue,
Review Comment:
🤖 nit: `--isDeltaStreamer` conflates "which streamer type am I using" with "which checkpoint key to write". Since there's already a V2 key that this flag silently ignores, a future caller targeting `STREAMER_CHECKPOINT_KEY_V2` has no way to reach it. Could you rename this to something like `--checkpointKeyVersion` (or just accept a `--checkpointKey` directly) so the purpose is explicit and the design can accommodate V2 naturally?
<sub><i>⚠️ AI-generated; verify before applying. React 👍/👎 to flag quality.</i></sub>
##########
hudi-cli/src/main/java/org/apache/hudi/cli/commands/CheckpointCommand.java:
##########
@@ -0,0 +1,232 @@
+/*
+ * 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.cli.HoodieCLI;
+import org.apache.hudi.cli.HoodiePrintHelper;
+import org.apache.hudi.cli.TableHeader;
+import org.apache.hudi.common.model.HoodieCommitMetadata;
+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.TimelineUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.StringUtils;
+import org.apache.hudi.common.util.collection.Pair;
+import org.apache.hudi.exception.HoodieException;
+import org.apache.hudi.storage.HoodieInstantWriter;
+import org.apache.hudi.storage.StoragePath;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+import org.springframework.shell.standard.ShellOption;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV1.STREAMER_CHECKPOINT_KEY_V1;
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV1.STREAMER_CHECKPOINT_RESET_KEY_V1;
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV2.STREAMER_CHECKPOINT_KEY_V2;
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV2.STREAMER_CHECKPOINT_RESET_KEY_V2;
+
+/**
+ * CLI command to inspect and repair ingestion checkpoints stored in commit metadata.
+ */
+@ShellComponent
+@Slf4j
+public class CheckpointCommand {
+
+ private static final String[] STREAMER_CHECKPOINT_KEYS = {
+ STREAMER_CHECKPOINT_KEY_V1, STREAMER_CHECKPOINT_RESET_KEY_V1,
+ STREAMER_CHECKPOINT_KEY_V2, STREAMER_CHECKPOINT_RESET_KEY_V2
+ };
+
+ private static final String BACKUP_DIR_NAME = ".checkpoint_backup";
+
+ /**
+ * Find the latest ingestion instant that stores a checkpoint in its commit metadata.
+ */
+ private Option<Pair<HoodieInstant, HoodieCommitMetadata>> findLatestCheckpointInstant(
+ HoodieTimeline timeline, String... keys) {
+ return TimelineUtils.findLatestInCommitMetadata(
+ timeline.filterCompletedInstants(), (instant, metadata) -> {
Review Comment:
🤖 `filterCompletedInstants()` returns all completed actions (clean, rollback, savepoint, etc.), not just commits. `findLatestInCommitMetadata` then calls `readCommitMetadata` on each in reverse order, and for a clean/rollback instant that avro content won't deserialize as `HoodieCommitMetadata` — it throws IOException, so both `get` and `set` fail whenever the latest completed instant isn't a commit (very common, e.g. a clean right after ingestion). Could you scope this to `timeline.getCommitsTimeline().filterCompletedInstants()` like `StreamerCheckpointUtils` does?
<sub><i>⚠️ AI-generated; verify before applying. React 👍/👎 to flag quality.</i></sub>
##########
hudi-cli/src/main/java/org/apache/hudi/cli/commands/CheckpointCommand.java:
##########
@@ -0,0 +1,232 @@
+/*
+ * 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.cli.HoodieCLI;
+import org.apache.hudi.cli.HoodiePrintHelper;
+import org.apache.hudi.cli.TableHeader;
+import org.apache.hudi.common.model.HoodieCommitMetadata;
+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.TimelineUtils;
+import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.StringUtils;
+import org.apache.hudi.common.util.collection.Pair;
+import org.apache.hudi.exception.HoodieException;
+import org.apache.hudi.storage.HoodieInstantWriter;
+import org.apache.hudi.storage.StoragePath;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+import org.springframework.shell.standard.ShellOption;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV1.STREAMER_CHECKPOINT_KEY_V1;
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV1.STREAMER_CHECKPOINT_RESET_KEY_V1;
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV2.STREAMER_CHECKPOINT_KEY_V2;
+import static org.apache.hudi.common.table.checkpoint.StreamerCheckpointV2.STREAMER_CHECKPOINT_RESET_KEY_V2;
+
+/**
+ * CLI command to inspect and repair ingestion checkpoints stored in commit metadata.
+ */
+@ShellComponent
+@Slf4j
+public class CheckpointCommand {
+
+ private static final String[] STREAMER_CHECKPOINT_KEYS = {
+ STREAMER_CHECKPOINT_KEY_V1, STREAMER_CHECKPOINT_RESET_KEY_V1,
+ STREAMER_CHECKPOINT_KEY_V2, STREAMER_CHECKPOINT_RESET_KEY_V2
+ };
+
+ private static final String BACKUP_DIR_NAME = ".checkpoint_backup";
+
+ /**
+ * Find the latest ingestion instant that stores a checkpoint in its commit metadata.
+ */
+ private Option<Pair<HoodieInstant, HoodieCommitMetadata>> findLatestCheckpointInstant(
+ HoodieTimeline timeline, String... keys) {
+ return TimelineUtils.findLatestInCommitMetadata(
+ timeline.filterCompletedInstants(), (instant, metadata) -> {
+ boolean hasCheckpoint = Arrays.stream(keys)
+ .anyMatch(key -> !StringUtils.isNullOrEmpty(metadata.getMetadata(key)));
+ return hasCheckpoint ? Option.of(Pair.of(instant, metadata)) : Option.empty();
+ });
+ }
+
+ @ShellMethod(key = "checkpoint get", value = "Display the ingestion checkpoint value stored in the latest ingestion commit")
+ public String getCheckpoint(
+ @ShellOption(value = {"--checkpointKey"}, help = "Checkpoint metadata key to read (defaults to all streamer checkpoint keys)",
+ defaultValue = "") final String checkpointKey) {
+ HoodieTableMetaClient metaClient = HoodieCLI.getTableMetaClient();
+ try {
+ HoodieTimeline timeline = metaClient.getActiveTimeline();
+ String[] keys = StringUtils.isNullOrEmpty(checkpointKey) ? STREAMER_CHECKPOINT_KEYS : new String[] {checkpointKey};
+ Option<Pair<HoodieInstant, HoodieCommitMetadata>> checkpointInfo = findLatestCheckpointInstant(timeline, keys);
+ if (checkpointInfo.isEmpty()) {
+ return "No checkpoint found in the active timeline. The table has no ingestion commit with a checkpoint "
+ + "metadata key matching: " + String.join(", ", keys);
+ }
+
+ HoodieInstant instant = checkpointInfo.get().getLeft();
+ HoodieCommitMetadata commitMetadata = checkpointInfo.get().getRight();
+
+ final List<Comparable[]> rows = new ArrayList<>();
+ for (String key : keys) {
+ String value = commitMetadata.getMetadata(key);
+ if (value != null) {
+ rows.add(new Comparable[] {key, value});
+ }
+ }
+ if (rows.isEmpty()) {
+ rows.add(new Comparable[] {keys.length == 1 ? keys[0] : "checkpoint", ""});
+ }
+
+ final Map<String, Function<Object, String>> fieldNameToConverterMap = new HashMap<>();
+ final TableHeader header = new TableHeader()
+ .addTableHeaderField("Checkpoint-Key")
+ .addTableHeaderField("Checkpoint-Value");
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("Ingestion checkpoint found in commit ").append(instant.requestedTime())
+ .append(" (").append(instant.getAction()).append(", ").append(instant.getState()).append(")\n");
+ sb.append("Commit statistics: records=").append(commitMetadata.fetchTotalRecordsWritten())
+ .append(", bytes=").append(commitMetadata.fetchTotalBytesWritten())
+ .append(", partitions=").append(commitMetadata.fetchTotalPartitionsWritten())
+ .append(", filesInserted=").append(commitMetadata.fetchTotalFilesInsert())
+ .append(", filesUpdated=").append(commitMetadata.fetchTotalFilesUpdated())
+ .append(", errors=").append(commitMetadata.fetchTotalWriteErrors()).append("\n\n");
+ sb.append(HoodiePrintHelper.print(header, fieldNameToConverterMap, "", false, -1, false, rows, ""));
+ return sb.toString();
+ } catch (IOException e) {
+ log.error("Error reading checkpoint", e);
+ return "Error reading checkpoint: " + e.getMessage();
+ }
+ }
+
+ @ShellMethod(key = "checkpoint set", value = "Overwrite the ingestion checkpoint value in the latest completed ingestion commit")
+ public String setCheckpoint(
+ @ShellOption(value = {"--checkpointValue"}, help = "The new checkpoint value to set") final String checkpointValue,
+ @ShellOption(value = {"--isDeltaStreamer"}, help = "Whether to target the DeltaStreamer checkpoint key",
+ defaultValue = "true") final boolean isDeltaStreamer,
+ @ShellOption(value = {"--customCheckpointKey"}, help = "Custom checkpoint key name (used when isDeltaStreamer=false)",
+ defaultValue = "") final String customCheckpointKey,
+ @ShellOption(value = {"--force"}, help = "Bypass the safety confirmation warning",
+ defaultValue = "false") final boolean force) {
+ if (!force) {
+ return "This is a destructive operation: it rewrites commit metadata. "
+ + "Re-run with --force true to proceed. The original commit file is backed up to "
+ + BACKUP_DIR_NAME + "/ before modification.";
+ }
+ if (StringUtils.isNullOrEmpty(checkpointValue)) {
+ return "Error: --checkpointValue cannot be empty.";
+ }
+
+ String targetKey;
+ if (isDeltaStreamer) {
+ targetKey = STREAMER_CHECKPOINT_KEY_V1;
+ } else {
+ if (StringUtils.isNullOrEmpty(customCheckpointKey)) {
+ return "Error: --customCheckpointKey is required when --isDeltaStreamer false.";
+ }
+ targetKey = customCheckpointKey;
+ }
+
+ HoodieTableMetaClient metaClient = HoodieCLI.getTableMetaClient();
+ try {
+ HoodieTimeline timeline = metaClient.getActiveTimeline();
+ Option<Pair<HoodieInstant, HoodieCommitMetadata>> checkpointInfo =
+ findLatestCheckpointInstant(timeline, STREAMER_CHECKPOINT_KEYS);
+ if (checkpointInfo.isEmpty()) {
+ return "No ingestion commit with a checkpoint found in the active timeline. Nothing to update.";
+ }
+
+ HoodieInstant instant = checkpointInfo.get().getLeft();
+ HoodieCommitMetadata commitMetadata = checkpointInfo.get().getRight();
+
+ // Back up the original commit file before modification.
+ StoragePath timelinePath = new StoragePath(metaClient.getTimelinePath());
+ StoragePath backupDir = new StoragePath(timelinePath, BACKUP_DIR_NAME);
+ StoragePath instantFileName = new StoragePath(timelinePath,
+ metaClient.getInstantFileNameGenerator().getFileName(instant));
+ StoragePath backupPath = new StoragePath(backupDir,
+ metaClient.getInstantFileNameGenerator().getFileName(instant));
+
+ if (!metaClient.getStorage().exists(backupDir)) {
+ metaClient.getStorage().createDirectory(backupDir);
+ }
+ String backupCode;
+ if (metaClient.getStorage().exists(backupPath)) {
+ backupCode = "already-present";
+ } else {
+ // Manual copy: read the original file and write to backup.
+ try (InputStream in = metaClient.getStorage().open(instantFileName);
+ OutputStream out = metaClient.getStorage().create(backupPath, false)) {
Review Comment:
🤖 nit: the manual 8 KB read/write loop could be replaced with `IOUtils.copy(in, out)` (Apache Commons IO, already on the Hudi classpath) — same semantics, a lot less noise.
<sub><i>⚠️ AI-generated; verify before applying. React 👍/👎 to flag quality.</i></sub>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(cli): add checkpoint get/set commands for ingestion checkpoint inspection and repair [FaaFyfxR9WAQrL7FcAgEHJvztd8cVMxvjHRS55rw1nwH] [hudi]
Posted by "voonhous (via GitHub)" <gi...@apache.org>.
voonhous commented on PR #19588:
URL: https://github.com/apache/hudi/pull/19588#issuecomment-5279087463
https://github.com/apache/hudi/pull/19580#issuecomment-5265754460
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(cli): add checkpoint get/set commands for ingestion checkpoint inspection and repair [FaaFyfxR9WAQrL7FcAgEHJvztd8cVMxvjHRS55rw1nwH] [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #19588:
URL: https://github.com/apache/hudi/pull/19588#issuecomment-5263231389
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "d5120a0c2c1470b5ab4adbe885a358e3fcc439e2",
"status" : "PENDING",
"url" : "https://dev.azure.com/apachehudi/a1a51da7-8592-47d4-88dc-fd67bed336bb/_build/results?buildId=16235",
"triggerID" : "d5120a0c2c1470b5ab4adbe885a358e3fcc439e2",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* d5120a0c2c1470b5ab4adbe885a358e3fcc439e2 Azure: [PENDING](https://dev.azure.com/apachehudi/a1a51da7-8592-47d4-88dc-fd67bed336bb/_build/results?buildId=16235)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(cli): add checkpoint get/set commands for ingestion checkpoint inspection and repair [FaaFyfxR9WAQrL7FcAgEHJvztd8cVMxvjHRS55rw1nwH] [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #19588:
URL: https://github.com/apache/hudi/pull/19588#issuecomment-5263187015
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "d5120a0c2c1470b5ab4adbe885a358e3fcc439e2",
"status" : "UNKNOWN",
"url" : "TBD",
"triggerID" : "d5120a0c2c1470b5ab4adbe885a358e3fcc439e2",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* d5120a0c2c1470b5ab4adbe885a358e3fcc439e2 UNKNOWN
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(cli): add checkpoint get/set commands for ingestion checkpoint inspection and repair [FaaFyfxR9WAQrL7FcAgEHJvztd8cVMxvjHRS55rw1nwH] [hudi]
Posted by "hudi-bot (via GitHub)" <gi...@apache.org>.
hudi-bot commented on PR #19588:
URL: https://github.com/apache/hudi/pull/19588#issuecomment-5263365597
<!--
Meta data
{
"version" : 1,
"metaDataEntries" : [ {
"hash" : "d5120a0c2c1470b5ab4adbe885a358e3fcc439e2",
"status" : "FAILURE",
"url" : "https://dev.azure.com/apachehudi/a1a51da7-8592-47d4-88dc-fd67bed336bb/_build/results?buildId=16235",
"triggerID" : "d5120a0c2c1470b5ab4adbe885a358e3fcc439e2",
"triggerType" : "PUSH"
} ]
}-->
## CI report:
* d5120a0c2c1470b5ab4adbe885a358e3fcc439e2 Azure: [FAILURE](https://dev.azure.com/apachehudi/a1a51da7-8592-47d4-88dc-fd67bed336bb/_build/results?buildId=16235)
<details>
<summary>Bot commands</summary>
@hudi-bot supports the following commands:
- `@hudi-bot run azure` re-run the last Azure build
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@hudi.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org