Skip to content

fix(parquet): propagate row-group and page-index write errors#921

Merged
zeroshade merged 4 commits into
apache:mainfrom
fallintoplace:fix/parquet-writer-error-propagation
Jul 14, 2026
Merged

fix(parquet): propagate row-group and page-index write errors#921
zeroshade merged 4 commits into
apache:mainfrom
fallintoplace:fix/parquet-writer-error-propagation

Conversation

@fallintoplace

Copy link
Copy Markdown
Contributor

Row-group transitions and footer flushes used to discard close and page-index errors, which could leave a truncated file that still appeared to write successfully.

This change adds checked row-group APIs, preserves legacy panic behavior for the no-error APIs, persists writer failures so a later footer cannot be written, returns Arrow Parquet row-group errors, and checks buffered metadata serialization and short writes.

Tests:

  • go test ./parquet/file -run 'Test(AppendRowGroupCheckedPropagatesCloseError|BufferedRowGroupPropagatesShortWrite|FlushWithFooterPropagatesPageIndexWriteError|NewParquetWriterWithError|PqarrowNewFileWriter)'
  • go test ./parquet/... -run '^$'

@fallintoplace fallintoplace requested a review from zeroshade as a code owner July 11, 2026 07:31

@zeroshade zeroshade 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 core change — persisting writeErr on file.Writer and propagating row-group/page-index close/write errors — is valuable, and the file.Writer side is done well (existing signatures preserved, ...Checked variants added). Two blockers inline. Also worth a changelog note: the deprecated AppendRowGroup()/AppendBufferedRowGroup() now panic on errors that were previously swallowed. The failure-injection tests look good.

Comment thread parquet/pqarrow/file_writer.go Outdated
// NewRowGroup does what it says on the tin, creates a new row group in the underlying file.
// Equivalent to `AppendRowGroup` on a file.Writer
func (fw *FileWriter) NewRowGroup() {
func (fw *FileWriter) NewRowGroup() error {

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.

Blocking (exported API break): changing NewRowGroup() to NewRowGroup() error alters a public method signature and breaks downstream method-values / interface satisfaction. Please mirror the approach you used in file.Writer: keep NewRowGroup() (panic on error) and add NewRowGroupChecked() error, with the internal write paths calling the checked variant.

Comment thread parquet/pqarrow/file_writer.go Outdated
// row group len. If using Records, this should be paired with WriteBuffered, while
// Write will always write a new record as a row group in and of itself.
func (fw *FileWriter) NewBufferedRowGroup() {
func (fw *FileWriter) NewBufferedRowGroup() error {

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.

Same exported-API concern — keep NewBufferedRowGroup() and add NewBufferedRowGroupChecked() error. Note the existing callers at encode_arrow_test.go:231,330,385,1374,1399 invoke these as bare statements and now silently drop the returned error; with the compatibility shape above they can remain unchanged.

@zeroshade zeroshade 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.

Good direction on propagating the writer errors. Two gaps remain before this is complete — see inline.

Comment thread parquet/file/row_group_writer.go Outdated
@@ -251,7 +256,7 @@ func (rg *rowGroupWriter) Close() error {
rg.metadata.SetNumRows(rg.nrows)
rg.metadata.Finish(rg.bytesWritten, rg.ordinal)

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.

rg.metadata.Finish(...) still discards its returned error. If it fails (for example, closing a serial row group before all schema columns have been initialized), Close() will report success and the file writer may continue with incomplete row-group metadata. Please capture it into rg.closeErr and return it, consistent with the other close failures handled just above.

if fw.pageIndexBuilder != nil {
// serialize page index after all row groups have been written and report
// location to the file metadata
fw.pageIndexBuilder.Finish()

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.

writePageIndex permanently Finish()es the PageIndexBuilder, but this path is reachable from FlushWithFooter, which is documented to leave the writer open for additional row groups. With page indexes enabled, a subsequent AppendRowGroupChecked/AppendBufferedRowGroupChecked will then call AppendRowGroup on a finished builder and record a sticky error. Please keep page-index state appendable after an intermediate footer, or explicitly document/enforce that cumulative footers are unsupported when page indexes are enabled.

@zeroshade zeroshade 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.

Thanks — all prior review feedback is addressed. LGTM.

@zeroshade zeroshade merged commit 0b04f33 into apache:main Jul 14, 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.

2 participants