Skip to content

Core: Keep metadata_log_entries snapshot details after expiration - #18529

Open
rshdhere wants to merge 2 commits into
apache:mainfrom
rshdhere:metadata-log-entries-expired-snapshots
Open

rshdhere wants to merge 2 commits into
apache:mainfrom
rshdhere:metadata-log-entries-expired-snapshots

Conversation

@rshdhere

@rshdhere rshdhere commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

The latest_* columns were derived from the current snapshot log, which expiring snapshots trims, so older entries started returning null. Read them from the metadata file each entry points to, only when one of those columns is projected. If that file is gone, the latest_* columns are null and a warning is logged.

Fixes #17761

The latest_* columns were derived from the current snapshot log, which
expiring snapshots trims, so older entries started returning null. Read
them from the metadata file each entry points to, only when one of those
columns is projected. If that file is gone, fall back to the previous
lookup instead of failing the scan.

Co-authored-by: yangshangqing95 <yangshangqing_tx@126.com>
@github-actions github-actions Bot added the core label Oct 15, 2026
@rshdhere

Copy link
Copy Markdown
Contributor Author

PTAL @uros-b

@uros-b uros-b 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.

The expire-snapshots fix is the right direction: latest_* from each entry's metadata file, last snapshot-log entry rather than currentSnapshot() (so REPLACE still works), and historical IO only when a latest_* field is projected.

Two things before merge:

  1. Missing-file policy. NotFoundException currently falls back to snapshotIdAsOfTime with no log, so one result mixes file-derived and time-derived rows and a missing file looks like a real no-snapshot row. An up-to-date handle cannot hit this (CatalogUtil.deleteRemovedMetadataFiles only deletes files already pruned from the log); a stale CachingCatalog handle can, after another writer committed with write.metadata.delete-after-commit.enabled. Fail-fast would break a SELECT * that works on main, so I would not go back to unguarded #17762. Prefer #16859: NotFoundException → null + LOG.warn; keep RuntimeIOException propagating. If you want to keep the time-based value, log it and say so in the description.

  2. Tests. snapshotColumnsNotProjected cannot fail: deleting the files only takes the NotFoundException path, and timestamp/file do not use it. Spy the FileIO and verify(io, never()).newInputFile(previous.file()), like #16859 doesNotLoadPropertiesWhenNotProjected (overwriting with invalid JSON also works). On snapshotDetailsAfterExpiration, after.subList(0, before.size()).isEqualTo(before) already pins the expired row from values captured before expire; replace after.get(1) asserting first with second's id / schema id / sequence number on the row written while it was current.

Not asking here: Tasks.foreach / planExecutor() prefetch. That's a follow-up and should be shared with #16859 so SELECT * does not parse every retained file twice. Do not skip file reads for rows at or after the oldest retained snapshot-log timestamp — that recreates #17761 when a middle snapshot is expired.

@rshdhere

Copy link
Copy Markdown
Contributor Author

Thanks @uros-b, both points make sense.

  1. I'll switch the missing-file case to match Core, Spark, Flink: Add table properties to metadata log entries #16859: on NotFoundException, return null and LOG.warn, and let RuntimeIOException propagate. I'll drop the snapshotIdAsOfTime fallback, so a missing file no longer shows up as a time-derived row.
  2. For the tests, snapshotColumnsNotProjected will spy on the FileIO and verify(io, never()).newInputFile(previous.file()), like doesNotLoadPropertiesWhenNotProjected. In snapshotDetailsAfterExpiration, I'll replace the after.get(1) check with assertions that first has second's snapshot ID, schema ID and sequence number from when it was current.

I'll leave the prefetch for a follow-up shared with #16859, and I won't skip reads at or after the oldest retained snapshot-log timestamp, since that would bring back #17761.

…iles

Falling back to the time-based lookup when an entry's metadata file is
gone mixed file-derived and time-derived rows in one result, and a
missing file looked like a real entry without a snapshot. Return null
and log a warning instead.

Tighten the tests: verify historical metadata files are never opened
when no latest_* column is projected, and assert the expired snapshot's
details on the row written while it was current.
@rshdhere

Copy link
Copy Markdown
Contributor Author

done with it 792b300, now let's wait for the CI to be green and ig will be ready to merge

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Metadata log entries lose snapshot details after snapshot expiration

2 participants