Skip to content

[core][common] Fix format tables reading and deleting files staged by a committer#8861

Open
sundapeng wants to merge 2 commits into
apache:masterfrom
sundapeng:fix/format-table-hidden-dir-listing
Open

[core][common] Fix format tables reading and deleting files staged by a committer#8861
sundapeng wants to merge 2 commits into
apache:masterfrom
sundapeng:fix/format-table-hidden-dir-listing

Conversation

@sundapeng

@sundapeng sundapeng commented Jul 27, 2026

Copy link
Copy Markdown
Member

Purpose

A committer stages its output inside the partition it writes, under a hidden directory, and the staged files carry ordinary data file names:

<table>/dt=20260727/part-00000.parquet                              committed
<table>/dt=20260727/__magic_job-<id>/tasks/attempt_<id>/part-00010  another job, still staging
<table>/dt=20260727/_temporary/attempt_<id>/part-00011              another job, still staging

A format table lists the partition recursively and then filters on the leaf file name only, so all three become splits. A zero-length placeholder then fails the query with ... is not a Parquet file (length is too low: 0), and INSERT OVERWRITE, driven by the same listing, deletes the other job's files. From the other side, TempFileCommitter#clean deletes the whole _temporary after every commit, and that directory is shared with everyone else writing there.

This PR skips hidden entries while walking instead of filtering names afterwards — what PartitionPathUtils#isHiddenFile already does for partition discovery (#6522) — and makes clean() drop only its own temporary file, removing the shared directory only when a non-recursive delete succeeds. The two halves are needed together: keeping a staging tree alive while the reader still treats it as data would put uncommitted rows into query results.

partitionLevels marks the levels that hold partition directories. They are descended whatever they are named, because a partition value may start with _ — the default partition name does, in the value-only layout — and INSERT OVERWRITE PARTITION (year = '2025') deletes under a prefix.

Brief change log

  • FormatTableScan#listDataFiles(fileIO, root, partitionLevels): new; walks with listStatus and prunes hidden subtrees. Used by SplitEnumerator#createSplits and FormatTableCommit#deletePreviousDataFile.
  • RenamingTwoPhaseOutputStream.TempFileCommitter#clean: delete this stream's temporary file; remove the shared directory only if a non-recursive delete succeeds.
  • PartitionPathUtils#isHiddenName: extracted from isHiddenFile.

Tests

  • FormatTableScanTest / FormatTableCommitTest: staged files produce no splits in either partition layout; a table under a _-prefixed path still reads; INSERT OVERWRITE leaves both staging trees intact yet still clears the default partition directory.
  • RenamingTwoPhaseOutputStreamTest: a peer's pending file survives clean(); an uncontended commit leaves no _temporary behind.
  • Every new case fails without the change; mvn -pl paimon-common,paimon-core test is green.

API and Format

  • No storage format change; PartitionPathUtils gains one public helper (isHiddenName).

Documentation

  • No documentation change needed

@sundapeng
sundapeng force-pushed the fix/format-table-hidden-dir-listing branch from 8c84e6b to 9420b55 Compare July 27, 2026 09:16
@sundapeng sundapeng changed the title [core][common] Do not treat committer staging files as format table data [core] Skip committer staging directories when listing format table files Jul 27, 2026
@sundapeng
sundapeng force-pushed the fix/format-table-hidden-dir-listing branch from 9420b55 to 44f32aa Compare July 27, 2026 09:40
@sundapeng sundapeng changed the title [core] Skip committer staging directories when listing format table files 【WIP】[core] Skip committer staging directories when listing format table files Jul 27, 2026
…iles

A format table lists a partition recursively but decides what is data from
the leaf file name only, so files staged by a committer inside the partition
(__magic_job-<id>/tasks/attempt_*/__base/part-*, _temporary/..., .hive-staging_*)
are read as data and deleted by INSERT OVERWRITE. A 0-byte magic-committer
placeholder fails the whole query.

List the directory by directory and skip hidden entries before descending, the
way Hive, Trino, Spark and Hadoop MapReduce exclude hidden path components from
a recursive listing. Pruning rather than post-filtering also keeps the staging
tree from being enumerated at all, which matters because leftover staging trees
are a normal steady state; for the flat layout a partition normally has, both
cost the same single listing.

Two things the walk must not confuse with staging. Entries above the listed
directory are never judged, so a table under a warehouse path such as
oss://bucket/_warehouse/db/t still reads. And a partition value may itself start
with '_' - the default partition name does, in the value-only layout - so the
levels that hold partition directories are descended whatever they are named:
an INSERT OVERWRITE naming only the leading partition keys deletes under a
prefix, and skipping the default partition there would leave its rows behind.
@sundapeng
sundapeng force-pushed the fix/format-table-hidden-dir-listing branch from 44f32aa to f3c5eb6 Compare July 27, 2026 14:01
@sundapeng sundapeng changed the title 【WIP】[core] Skip committer staging directories when listing format table files [core] Skip committer staging directories when listing format table files Jul 27, 2026
…ge there

TempFileCommitter#clean deleted the whole '_temporary' directory of the target
directory after committing one file. That directory is shared with every other
writer: two concurrent writers into the same directory wiped each other's
pending files, and a Hadoop FileOutputCommitter job staging there lost its
output.

Delete this stream's own temporary file, then ask whether the directory can go
by attempting a non-recursive delete: it succeeds when nothing else is staged
there and fails while a concurrent writer still has files in it. Asking that way
costs no listing and leaves no window between the check and the delete.
@sundapeng sundapeng changed the title [core] Skip committer staging directories when listing format table files [core][common] Do not read or delete what a committer is still staging Jul 27, 2026
@sundapeng sundapeng changed the title [core][common] Do not read or delete what a committer is still staging [core][common] Fix format tables reading and deleting files staged by a committer Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant