Skip to content

Propagate delete file replication factor for Hadoop ORC files - #254

Merged
cbb330 merged 1 commit into
linkedin:openhouse-1.5.2from
shanthoosh:orc-delete-file-replication
Jul 14, 2026
Merged

Propagate delete file replication factor for Hadoop ORC files#254
cbb330 merged 1 commit into
linkedin:openhouse-1.5.2from
shanthoosh:orc-delete-file-replication

Conversation

@shanthoosh

Copy link
Copy Markdown
Collaborator

Problem Summary

PRs #219 and #229 added support for configuring the replication factor of delete files
(delete-file-replication write option, spark.sql.iceberg.delete-file-replication session
conf, write.delete-file-replication table property). The configured value is resolved
correctly by SparkWriteConf and carried all the way into Iceberg's HadoopOutputFile, but
for ORC delete files it never reaches HDFS.

Root cause: ORC.newFileWriter short-circuits Hadoop-backed output files by handing ORC the
raw Hadoop FileSystem:

if (file instanceof HadoopOutputFile) {
  options.fileSystem(((HadoopOutputFile) file).getFileSystem());
}

ORC's PhysicalFsWriter then opens the output stream itself via fs.create(path, overwrite, bufferSize, fs.getDefaultReplication(path), blockSize) — using the filesystem default replication and discarding the replication factor carried by the Iceberg HadoopOutputFile. Iceberg's HadoopOutputFile.create(), the only place the configured replication is applied, is never invoked. As a result, merge-on-read DELETE/UPDATE/MERGE on ORC tables always produces delete files with the cluster default replication regardless of configuration.

Solution

  • Expose the configured replication on HadoopOutputFile via a replication() accessor (non-positive means "not configured").
  • In ORC.newFileWriter (and the matching spot in OrcFileAppender), take the raw-filesystem shortcut only when no custom replication is configured. When a replication factor is set, route the file through FileIOFSUtil.OutputFileSystem — the same path already used for non-Hadoop FileIO implementations — so the output stream is created by Iceberg's HadoopOutputFile.create(), which passes the configured replication to fs.create(...).

Behavior is unchanged when no replication is configured: the raw-filesystem shortcut is still
used and files inherit the filesystem default.

Testing Done

Unit tests — orc:TestOrcReplication (new): writes through the plain ORC FileAppender and the position-delete writer against a RawLocalFileSystem subclass that records the replication factor passed to every FileSystem.create(...) call:

  • configured replication (5) reaches stream creation for data appender and position-delete writer; the file written through the OutputFileSystem path reads back as valid OR
  • with nothing configured, the filesystem default replication is used (shortcut preserved)

Integration tests (SQL-only) — spark 3.5:TestOrcDeleteFileReplication (new): the catalog warehouse lives on a capturing filesystem, and the whole flow is driven through

CREATE TABLE (format-version 2, merge-on-read, ORC) → INSERT → DELETE →
SELECT file_path FROM t.delete_files:

  • write.delete-file-replication'='5' table property → all position delete files created with
    replication 5; data files keep the filesystem default; SELECT after DELETE return
    rows (deletes apply on read)
  • SET spark.sql.iceberg.delete-file-replication=7 session conf → delete files c
    with replication 7 (note: the hyphenated key must be backquoted in Spark SQL SET)

ORC.newFileWriter hands ORC the raw Hadoop FileSystem for
HadoopOutputFile, so ORC's PhysicalFsWriter opens the stream itself
with the filesystem default replication and the replication factor
carried by Iceberg's HadoopOutputFile never applies. Route Hadoop
output files with a custom replication factor through
FileIOFSUtil.OutputFileSystem so the stream is created by Iceberg's
HadoopOutputFile with the configured replication.

Verified end-to-end through SQL-only Spark 3.5 tests: CREATE TABLE
with merge-on-read ORC, INSERT, DELETE, and the delete_files metadata
table, with the catalog warehouse on a capturing filesystem that
records the replication factor passed to FileSystem.create. Covers the
write.delete-file-replication table property and the
spark.sql.iceberg.delete-file-replication session conf set via SQL SET
(whose hyphenated key must be backquoted), and verifies data files
keep the filesystem default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@shanthoosh shanthoosh changed the title ORC: Propagate delete file replication factor to Hadoop output streams Propagate delete file replication factor for Hadoop ORC files Jul 14, 2026

@cbb330 cbb330 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

please bump in linkedin/openhouse and set up an e2e test using sparkitest within linkedin/openhouse after this merges

@cbb330
cbb330 merged commit dbcca9b into linkedin:openhouse-1.5.2 Jul 14, 2026
23 checks passed
shanthoosh pushed a commit to shanthoosh/openhouse that referenced this pull request Jul 14, 2026
… e2e tests

Picks up com.linkedin.iceberg v1.5.2.17 (linkedin/iceberg#254), which
propagates the delete file replication factor to ORC delete files.

Adds spark-3.5 sparkitest coverage exercising the configuration through
the full OpenHouse stack via SQL: the write.delete-file-replication
table property round-trips through the tables service, and merge-on-read
DELETE on an ORC table produces ORC position delete files through the
replication-aware write path, for both the table property and the
spark.sql.iceberg.delete-file-replication session conf routes.
Verification is metadata-based (delete_files manifests) because the
itest classpath mixes shaded and unshaded iceberg, which breaks ORC
position delete reads with a TypeDescription ClassCastException.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
shanthoosh pushed a commit to shanthoosh/openhouse that referenced this pull request Jul 14, 2026
… e2e tests

Picks up com.linkedin.iceberg v1.5.2.17 (linkedin/iceberg#254), which
propagates the delete file replication factor to ORC delete files.

Adds spark-3.5 sparkitest coverage exercising the configuration through
the full OpenHouse stack via SQL: the write.delete-file-replication
table property round-trips through the tables service, and merge-on-read
DELETE on an ORC table produces ORC position delete files through the
replication-aware write path, for both the table property and the
spark.sql.iceberg.delete-file-replication session conf routes.
Verification is metadata-based (delete_files manifests) because the
itest classpath mixes shaded and unshaded iceberg, which breaks ORC
position delete reads with a TypeDescription ClassCastException.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cbb330 pushed a commit to linkedin/openhouse that referenced this pull request Jul 14, 2026
## Summary

Picks up com.linkedin.iceberg v1.5.2.17 (linkedin/iceberg#254), which
propagates the delete file replication factor to ORC delete files.

Adds spark-3.5 sparkitest coverage exercising the configuration through
the full OpenHouse stack via SQL: the write.delete-file-replication
table property round-trips through the tables service, and merge-on-read
DELETE on an ORC table produces ORC position delete files through the
replication-aware write path, for both the table property and the
spark.sql.iceberg.delete-file-replication session conf routes.
Verification is metadata-based (delete_files manifests) because the
itest classpath mixes shaded and unshaded iceberg, which breaks ORC
position delete reads with a TypeDescription ClassCastException.

## Changes

- [ ] Client-facing API Changes
- [ ] Internal API Changes
- [ ] Bug Fixes
- [X] New Features
- [ ] Performance Improvements
- [ ] Code Style
- [ ] Refactoring
- [ ] Documentation
- [ ] Tests

For all the boxes checked, please include additional details of the
changes made in this pull request.

## Testing Done
<!--- Check any relevant boxes with "x" -->

- [x] Manually Tested on local docker setup. Please include commands
ran, and their output.
- [x] Added new tests for the changes made.
- [x] Updated existing tests to reflect the changes made.
- [x] No tests added or updated. Please explain why. If unsure, please
feel free to ask for help.
- [ ] Some other form of testing like staging or soak time in
production. Please explain.

For all the boxes checked, include a detailed description of the testing
done for the changes made in this pull request.

# Additional Information

- [ ] Breaking Changes
- [ ] Deprecations
- [ ] Large PR broken into smaller PRs, and PR plan linked in the
description.

For all the boxes checked, include additional details of the changes
made in this pull request.

---------

Co-authored-by: Shanthoosh Pazhanjur Venkataraman <pvs@Shanthooshs-MacBook-Air.local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants