Skip to content

docs(cleaning): document that clean metadata records instant (start) times - #19552

Merged
voonhous merged 3 commits into
apache:asf-sitefrom
deepakpanda93:docs/instant-completion-time-cleaning-17274
Aug 9, 2026
Merged

docs(cleaning): document that clean metadata records instant (start) times#19552
voonhous merged 3 commits into
apache:asf-sitefrom
deepakpanda93:docs/instant-completion-time-cleaning-17274

Conversation

@deepakpanda93

Copy link
Copy Markdown
Collaborator

Describe the issue this Pull Request addresses

Closes #17274. (JIRA: HUDI-8258, a subtask of HUDI-8894.)

Hudi 1.x stamps every action with both a requested instant time and a completion time, and orders the timeline by
completion time. Nothing in the docs said which of the two the cleaner records, so someone reading clean metadata to
debug a retention problem has no way to tell whether earliestCommitToRetain is a start time or a completion time — and
picking the wrong interpretation sends them looking at the wrong commit.

It is a start time, on every field. The issue asks for exactly this to be stated on the Cleaning and Tech Spec pages.

Summary and Changelog

Added an ### Instant Times in Clean Metadata subsection to the Cleaning page tabulating the four timestamp fields, and
a matching field table to the Cleaning section of the 1.0 tech spec:

Field Written to Value
earliestInstantToRetain.timestamp HoodieCleanerPlan (the clean.requested instant) Instant time of the oldest commit this clean run retains
earliestCommitToRetain HoodieCleanMetadata (the completed clean instant) Copied from the plan, so also an instant time
lastCompletedCommitTimestamp both Instant time of the last completed write before the clean was planned — despite the name, a start time
startCleanTime HoodieCleanMetadata Instant time of the clean action itself

Plus a note that incremental clean planning follows the same convention, ranging over the requested instant times of
completed commits.

Files: website/docs/cleaning.md (next) and website/versioned_docs/version-1.2.0/cleaning.md (current released docs),
per the next-plus-current convention used in #19473 and #19551. website/learn/tech-specs.md has no versioned copies —
the learn plugin is configured without versioning — so it has a single edit.

Where this comes from in the code

Checked at both master and the release-1.2.0 tag; the relevant lines are identical in each.

  • CleanPlanActionExecutor.java:111 and :177 build the plan's earliestInstantToRetain from hoodieInstant.requestedTime().
  • CleanPlanner.java:655-657getLastCompletedCommitTimestamp() returns getCommitTimeline().lastInstant().requestedTime(). The source carries the same observation inline at CleanPlanActionExecutor.java:178: "Note: This is the start time of the last completed ingestion before this clean."
  • CleanActionExecutor.java:172-179 and :256 copy the plan values into HoodieCleanMetadata.
  • CleanPlanner.java:241-245 filters completed commits by instant.requestedTime() against earliestCommitToRetain.

Reproduction

Source reading alone felt insufficient here, because the page promises what a user will see — and the read path runs
CleanMetadataMigrator.upgradeToLatest, which I had not verified leaves these timestamps alone. So the values below were
read back through CleanerUtils.getCleanerMetadata, the same call a debugging user would make.

Setup: Spark 3.5.7 with hudi-spark3.5-bundle_2.12:1.2.0, a local-filesystem COW table, hoodie.clean.commits.retained=3,
hoodie.clean.incremental.enabled=true, 8 write batches. Requested and completion times differ on every instant, so no
value can match by coincidence.

Timeline (tail):

Action requested completion
commit 20260807112136674 20260807112136958
commit 20260807112137041 20260807112137362
commit 20260807112137452 20260807112137746
clean 20260807112137760 20260807112137796

Metadata read back from that last clean:

Field Value Is the...
startCleanTime 20260807112137760 clean's requested time (not …137796)
earliestCommitToRetain 20260807112136674 that commit's requested time (not …136958)
lastCompletedCommitTimestamp 20260807112137452 last commit's requested time (not …137746)
HoodieCleanerPlan.earliestInstantToRetain 20260807112136674 (action=commit, state=COMPLETED) identical to the metadata, confirming it is copied from the plan
HoodieCleanerPlan.lastCompletedCommitTimestamp 20260807112137452 same

No recorded value appears anywhere in the completion-time column. Every completion time on that timeline
(134434, 135308, 135737, 136087, 136511, 136630, 136958, 137008, 137362, 137415, 137746, 137796) is absent from the
metadata.

Two notes on method. I first ran with retained=1, where earliestCommitToRetain and lastCompletedCommitTimestamp
collapse to the same value and only one field would have been proven; the retained=3 run above separates them into two
distinct values, both still requested times. And for the incremental-planning sentence, which is not observable in
metadata, I enabled CleanPlanner INFO logging:

Incremental Cleaning mode is enabled. Looking up partition-paths that have changed since last clean at 20260807112242500.
New Instant to retain Option{val=[20260807112242946__20260807112243250__commit__COMPLETED]}.

20260807112242500 is the requested time of 20260807112242500_20260807112242806.commit, not its completion.

A note on scope

The parent ticket HUDI-8077, which would have moved clean metadata onto completion time, is resolved Won't Do and its
PR #11972 was closed unmerged. This PR therefore documents the behaviour as it actually stands, which matches the
issue's own wording that "the start/instant time is still used in the clean metadata".

Impact

Documentation only. No code, config, or behaviour change.

Risk Level

none

Documentation Update

This PR is the documentation update — the Cleaning page (/docs/cleaning, /docs/next/cleaning) and the technical
specification (/learn/tech-specs).

Contributor's checklist

  • Read through contributor's guide
  • Enough context is provided in the sections above
  • Adequate tests were added if applicable

@hudi-agent hudi-agent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ 🤖 This review was generated by an AI agent and may contain mistakes. Please verify any suggestions before applying.

Thanks for the docs update! The changes clearly document that Hudi's cleaner records instant (start) times across its plan and metadata fields, and the new tables in cleaning.md and the tech spec are consistent with each other and with the cited code paths. One small clarity note on the incremental-cleaning phrasing; please defer to a Hudi committer or PMC member (e.g. @yihua) for final confirmation.

cc @yihua

Comment thread website/docs/cleaning.md Outdated
| `startCleanTime` | `HoodieCleanMetadata` | Instant time of the clean action itself. |

Incremental clean planning follows the same convention: it selects the commits whose **requested** instant time falls
between the previous clean's `earliestCommitToRetain` and the current one, and scans only the partitions those commits

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 This sentence reads clearly overall, but "between the previous clean's earliestCommitToRetain and the current one" leaves "the current one" a little ambiguous — it could help to spell out that it means the current clean's earliestCommitToRetain, so readers don't have to infer the endpoint.

⚠️ AI-generated; verify before applying. React 👍/👎 to flag quality.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point — fixed in b55e39f, and I made the bounds exact while I was there.

CleanPlanner.java:241-245 filters completed commits with:

requestedTime >= cleanMetadata.getEarliestCommitToRetain()   // previous clean's
requestedTime <  newInstantToRetain.requestedTime()          // this clean's

so the range is half-open — inclusive at the lower end, exclusive at the upper. And newInstantToRetain is the instant returned by CleanPlanner#getEarliestCommitToRetain (CleanPlanActionExecutor.java:129), which is the same one written into the plan as earliestInstantToRetain (:177) and then copied to HoodieCleanMetadata.earliestCommitToRetain. So naming the endpoint "this clean's earliestCommitToRetain" is accurate, not just clearer.

New wording:

Incremental clean planning follows the same convention: it selects the commits whose requested instant time is at or after the previous clean's earliestCommitToRetain and before this clean's, then scans only the partitions those commits touched.

Applied to both cleaning.md copies and to the tech spec, which carried the same sentence. Build is clean with no new warnings.

@github-actions github-actions Bot added docs size:S PR with lines of changes in (10, 100] labels Aug 7, 2026
deepakpanda93 added a commit to deepakpanda93/hudi that referenced this pull request Aug 7, 2026
Review feedback on apache#19552: "between the previous clean's
earliestCommitToRetain and the current one" left the upper endpoint to be
inferred.

Name it, and while here state the bounds exactly. CleanPlanner:241-245 filters
completed commits with

    requestedTime >= cleanMetadata.getEarliestCommitToRetain()
    requestedTime <  newInstantToRetain.requestedTime()

so the range is half-open: inclusive of the previous clean's
earliestCommitToRetain, exclusive of this clean's. newInstantToRetain is the
instant returned by CleanPlanner#getEarliestCommitToRetain
(CleanPlanActionExecutor:129), the same one written to the plan as
earliestInstantToRetain (:177), so "this clean's earliestCommitToRetain" names
it accurately.

Applied to both cleaning.md copies and the tech spec, which carried the same
sentence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@hudi-agent hudi-agent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ 🤖 This review was generated by an AI agent and may contain mistakes. Please verify any suggestions before applying.

Thanks for the docs update! This PR adds a clear field-by-field table documenting that clean plan/metadata timestamps are instant (start) times, plus the incremental-clean planning range, across cleaning.md, its 1.2.0 versioned copy, and the tech spec. I verified each field mapping and the incremental range bounds against the current cleaner source and they are accurate. Please route to a Hudi committer or PMC member for final confirmation.

cc @yihua

@voonhous voonhous left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

deepakpanda93 and others added 3 commits August 9, 2026 15:29
…times

Hudi 1.x stamps every action with both a requested instant time and a
completion time and orders the timeline by completion time, but nothing in
the docs said which of the two the cleaner records. A user reading clean
metadata to debug cannot tell whether earliestCommitToRetain is a start time
or a completion time.

It is a start time, on every field, verified at release-1.2.0 and master:

- HoodieCleanerPlan.earliestInstantToRetain is built from
  hoodieInstant.requestedTime() (CleanPlanActionExecutor:111 and :177), and
  HoodieCleanMetadata.earliestCommitToRetain is copied from the plan
  (CleanActionExecutor:172-179 and :265).
- lastCompletedCommitTimestamp is
  getCommitTimeline().lastInstant().requestedTime() (CleanPlanner:655-657),
  so despite the name it is a start time; the source carries the same note
  at CleanPlanActionExecutor:178.
- startCleanTime is inflightInstant.requestedTime() on both the normal and
  the empty-clean path (CleanActionExecutor:228-232 and :256).
- Incremental clean planning likewise ranges over instant.requestedTime()
  (CleanPlanner:241-245).

Add a section to the Cleaning page tabulating these four fields, and a
matching field table to the Cleaning section of the 1.0 tech spec.

Note this documents behavior as it stands. The parent ticket HUDI-8077,
which would have moved clean metadata to completion time, is resolved
Won't Do and its PR apache#11972 was closed unmerged.

Applied to next and to version-1.2.0, the current released docs. The tech
spec is served unversioned from the learn plugin, so it has a single copy.

Closes apache#17274.
Review feedback on apache#19552: "between the previous clean's
earliestCommitToRetain and the current one" left the upper endpoint to be
inferred.

Name it, and while here state the bounds exactly. CleanPlanner:241-245 filters
completed commits with

    requestedTime >= cleanMetadata.getEarliestCommitToRetain()
    requestedTime <  newInstantToRetain.requestedTime()

so the range is half-open: inclusive of the previous clean's
earliestCommitToRetain, exclusive of this clean's. newInstantToRetain is the
instant returned by CleanPlanner#getEarliestCommitToRetain
(CleanPlanActionExecutor:129), the same one written to the plan as
earliestInstantToRetain (:177), so "this clean's earliestCommitToRetain" names
it accurately.

Applied to both cleaning.md copies and the tech spec, which carried the same
sentence.
Two values in the new clean-metadata table are easy to misread, and neither
is visible in a single-writer reproduction.

lastCompletedCommitTimestamp mixes the two orderings. CleanPlanner:655-656
is getCommitTimeline().lastInstant().map(HoodieInstant::requestedTime), and
getCommitTimeline() (CleanPlanner:121-126) is getCompletedCommitsTimeline(),
which 1.x orders by completion time. So the instant is picked by completion
order and then recorded as a start time. Given commits A (requested t1,
completed t4) and B (requested t2, completed t3) with t1 < t2 < t3 < t4, the
field holds t1 even though the completed commit B started at t2. Saying "the
last completed write" invited reading it as the largest requested time.

earliestCommitToRetain can be an empty string. CleanerUtils:126-156 has no
branch for KEEP_LATEST_FILE_VERSIONS, so getEarliestCommitToRetain() is empty
under that policy, and CleanPlanActionExecutor:177 stores null in the plan.
That policy still cleans - getPartitionPathsToClean falls through to
getPartitionPathsForFullCleaning (CleanPlanner:161-165) - so the run produces
clean stats, CleanActionExecutor:184 substitutes "" for the missing instant,
and CleanerUtils.convertCleanMetadata:93 copies it straight into
HoodieCleanMetadata. A reader debugging retention on that policy sees an
empty field and no explanation.

Applied to both cleaning.md copies as a short list under the table, and
folded into the corresponding tech spec rows.
@voonhous
voonhous force-pushed the docs/instant-completion-time-cleaning-17274 branch from b55e39f to 31d35b0 Compare August 9, 2026 07:55
@github-actions github-actions Bot added size:XL PR with lines of changes > 1000 and removed size:S PR with lines of changes in (10, 100] labels Aug 9, 2026
@voonhous
voonhous force-pushed the docs/instant-completion-time-cleaning-17274 branch from 31d35b0 to c46c28f Compare August 9, 2026 08:03
@voonhous
voonhous merged commit e313ed2 into apache:asf-site Aug 9, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs size:XL PR with lines of changes > 1000

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants