sundapeng opened a new pull request, #8861:
URL: https://github.com/apache/paimon/pull/8861

   ### Purpose
   
   Closes #8860.
   
   A Format Table lists the files of a partition **recursively**
   (`fileIO.listFiles(path, true)`) but decides what is data from the **leaf 
file name only**
   (`FormatTableScan#isDataFileName`: `!name.startsWith(".") && 
!name.startsWith("_")`). Files staged by
   a committer inside the partition — 
`__magic_job-<id>/tasks/attempt_*/__base/part-*.parquet`,
   `_temporary/0/_temporary/attempt_*/part-*`, `.hive-staging_*/-ext-10000/*` — 
carry perfectly ordinary
   data file names; only the directories above them say they are uncommitted. 
So they are picked up as
   data, and a 0-byte magic-committer placeholder fails the whole query with
   `... is not a Parquet file (length is too low: 0)`.
   
   The same listing feeds `FormatTableCommit#deletePreviousDataFile`, so 
`INSERT OVERWRITE` deletes the
   pending files of writers that are still running in that partition.
   
   Every other engine filters a recursive listing per path component — Hive's
   `HIDDEN_FILES_FULL_PATH_FILTER` (applied to the path made relative to the 
listing root, in the same
   `fs.listFiles(base, true)` fast path Paimon uses), Hadoop MapReduce, Trino
   (`containsHiddenPathPartAfterIndex`), Spark. #6522 fixed this for partition 
*discovery*
   (`searchPartSpecAndPaths` checks `isHiddenFile` at every level); the listing 
inside a partition never
   followed.
   
   **This PR**
   
   1. `PartitionPathUtils#containsHiddenPathPart(base, path)`: is any path 
component **below `base`**
      hidden (`_` or `.` prefixed)? Used by the read path 
(`SplitEnumerator#createSplits`) and the write
      path (`FormatTableCommit#deletePreviousDataFile`) through one shared
      `FormatTableScan#isDataFile(listedRoot, file)`.
   
      Two deliberate choices:
      - **Relative to the listed directory, never absolute.** Walking up to the 
filesystem root would
        make every table under a warehouse path such as 
`oss://bucket/_warehouse/db/t` read as empty.
        Silently reading nothing is worse than the bug being fixed, so there is 
a test for it.
      - **The generic `_` / `.` rule, not a `__magic` literal.** Since 
HADOOP-18797 (Hadoop 3.4.0) the
        directory is `__magic_job-${jobId}`; the prefix rule also covers 
`_temporary`,
        `.hive-staging_*` and `_temporary_jindo`, and matches JindoOSS's own 
default
        (`fs.jfs.cache.oss.delete-marker.dirs`).
   
   2. `RenamingTwoPhaseOutputStream.TempFileCommitter#clean` deleted 
`tempPath.getParent()` — the whole
      `<partition>/_temporary` directory — after committing a single file. That 
directory is shared
      with every other writer of the same directory: two concurrent Paimon 
writers into one partition
      wiped each other's pending files, and any Hadoop `FileOutputCommitter` 
job staging there lost its
      output. It now deletes only its own temporary file (a no-op after a 
successful rename), leaving
      at most an empty `_temporary` directory that readers already skip.
   
   Worth knowing for reviewers: leftover staging trees are a supported steady 
state, not an anomaly.
   `fs.s3a.committer.magic.cleanup.enabled=false` (HADOOP-18568) is the 
documented recommendation for
   large jobs, with the `__magic` tree removed by an object-store lifecycle 
rule, and
   `MagicS3GuardCommitter.cleanupStagingDirs()` swallows deletion failures 
anyway.
   
   Not in this PR: skipping 0-byte files. It is a different question (harmless 
for text formats, always
   fatal for parquet/orc) and deserves its own discussion rather than riding 
along with a path filter.
   
   ### Tests
   
   `PartitionPathUtilsTest`
   - `testCommitterStagingTreesAreHidden`: `_temporary/...`, `__magic/...`, 
`__magic_job-<id>/...`,
     `.hive-staging_*/...`, `_temporary_jindo/...`, and a hidden leaf.
   - `testCommittedFilesAreKept`: plain and nested-but-visible files.
   - `testHiddenComponentsAboveTheListedRootAreIgnored`: a table under 
`_warehouse/.db/` still reads.
   - `testPathOutsideTheListedRootIsLeftToTheCaller`.
   
   `FormatTableScanTest`
   - `testCreateSplitsSkipsCommitterStagingFiles` (unpartitioned) and
     `testCreateSplitsSkipsStagingFilesInsidePartitions` (partitioned, both 
partition layouts): only
     the committed file becomes a split.
   - `testCreateSplitsKeepsFilesUnderAStagingLikeTableLocation`: regression 
guard for a table location
     with a leading `_`.
   
   `FormatTableCommitTest`
   - `testOverwriteKeepsFilesOfConcurrentWritersStagingTrees`: `INSERT 
OVERWRITE` removes the previous
     data file, commits the new one, and leaves both staging trees untouched.
   
   `RenamingTwoPhaseOutputStreamTest`
   - `testCleanKeepsTheSharedStagingDirectory`.
   
   All four new scan/commit tests fail on master without the change. `mvn -pl 
paimon-common,paimon-core
   test` is green.
   
   ### API and Format
   
   No format change. `PartitionPathUtils` gains two public helpers 
(`isHiddenName`,
   `containsHiddenPathPart`); `FormatTableScan#isDataFileName` keeps its 
signature and behaviour.
   `RenamingTwoPhaseOutputStream` cleanup now leaves an empty `_temporary` 
directory behind, which
   readers already ignore.
   
   ### Documentation
   
   None — this restores the behaviour the `_`/`.` convention already implies.
   


-- 
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: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to