Repository navigation
Conversation
PyArrowFileIO wrote local files without fsync, so a power loss after an acknowledged commit could leave the catalog pointing at an empty metadata.json. Flush each file and its directory entry to disk when the stream is closed.
rambleraptor
left a comment
Collaborator
There was a problem hiding this comment.
Are you able to give any benchmarks on performance for this? This seems like a bit of an edge case and I'm worried that we're introducing latency on a per-file basis.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4145.
Rationale for this change
PyArrowFileIOwrites local (file://) files throughpyarrow.fs.LocalFileSystem, whose output stream never callsfsync. The catalog then commits a pointer to the newmetadata.json, so after a power loss the pointer can survive while the files it references are empty, and the table no longer loads. #4145 has the full trace and a power-cut reproduction.This change makes a local write durable when its stream is closed, which is before any catalog commit can reference it:
PyArrowLocalFileSystem.open_output_streamnow returns_SyncOnCloseOutputStream, apyarrow.BufferedOutputStreamover apyarrow.OSFile. Onclose()it flushes,fsyncs the file, and thenfsyncs the parent directory so that the new entry is durable too.open_output_streamhas to create parent directories (for examplemetadata/ordata/on the first write), it alsofsyncs the parent of each directory it created.The stream stays a native pyarrow stream with the same buffer size, so the Parquet, Avro and metadata writers are unchanged. Every caller already closes its stream explicitly or through
with.Scope, kept deliberately narrow:
PyArrowFileIO's local file system changes.FsspecFileIO's local file system has the same gap and could follow separately.file://): data files, manifests andmetadata.jsonare never fsynced before the catalog commit; after a power loss the table cannot be loaded #4145 also mentions, is not touched.compressionormetadatatoopen_output_stream, it still gets pyarrow's own stream, unsynced. PyIceberg itself only passesbuffer_size.The cost is two
fsynccalls per written file, and only on a local warehouse. Object stores are not affected. I have not added a property to turn this off. If you would like one, I can add it.Are these changes tested?
Yes.
test_pyarrow_local_fs_syncs_file_and_new_directories_on_closewrites throughPyArrowFileIOinto a path whose parent directories do not exist yet. It then checks that the file and the three directories whose entries changed were synced, by comparing the inodes of thefsynced descriptors.assert [] == [...]: nofsyncat all).tests/io/test_pyarrow.py,tests/avro,tests/catalog/test_sql.py,tests/test_serializers.pyandtests/utils/test_manifest.pygive the same results with and without the change, apart from the new test.file://): data files, manifests andmetadata.jsonare never fsynced before the catalog commit; after a power loss the table cannot be loaded #4145 (create_tableplus oneappendon a SQLite catalog) withstrace. On main there is nofsyncunder the warehouse. With this change, the Parquet file, manifest, manifest list andmetadata.jsonare each synced, together with their directory, before SQLite commits the newmetadata_location.Are there any user-facing changes?
There is no API change. Writes to a local warehouse are now durable once a commit returns, at the cost of the
fsynccalls above.