FileCodec.encode and VersionedCodec.encode write to a staging file, close it, then atomicMove it into place. Closing flushes the userspace buffer to the OS, but nothing forces the data to disk. kotlinx-io has no fsync in its API: RawSink exposes flush() only, and FileSystem has atomicMove with no sync counterpart.
A rename is a metadata operation and can land durably while the data blocks have not. On a hard kill or power loss that leaves a file that exists with zero length, which is the classic rename-without-fsync failure.
Worth investigating as the cause of #80, #157 and #162. All three show JSON input: with nothing after it, so empty files rather than truncated or interleaved ones, and in #80 and #162 it is the two-byte .version file that comes back empty. That fits a durability gap better than it fits concurrent writers. It would also explain why it clustered on one reporter's device and nobody could reproduce it on demand.
#172 stopped these from crashing, and #174 removes a separate corruption route, but neither addresses this. A store that silently empties itself is still data loss.
Likely needs an upstream request to kotlinx-io or a platform-specific expect/actual sync, so worth confirming the diagnosis before picking either.
FileCodec.encodeandVersionedCodec.encodewrite to a staging file, close it, thenatomicMoveit into place. Closing flushes the userspace buffer to the OS, but nothing forces the data to disk. kotlinx-io has no fsync in its API:RawSinkexposesflush()only, andFileSystemhasatomicMovewith no sync counterpart.A rename is a metadata operation and can land durably while the data blocks have not. On a hard kill or power loss that leaves a file that exists with zero length, which is the classic rename-without-fsync failure.
Worth investigating as the cause of #80, #157 and #162. All three show
JSON input:with nothing after it, so empty files rather than truncated or interleaved ones, and in #80 and #162 it is the two-byte.versionfile that comes back empty. That fits a durability gap better than it fits concurrent writers. It would also explain why it clustered on one reporter's device and nobody could reproduce it on demand.#172 stopped these from crashing, and #174 removes a separate corruption route, but neither addresses this. A store that silently empties itself is still data loss.
Likely needs an upstream request to kotlinx-io or a platform-specific
expect/actualsync, so worth confirming the diagnosis before picking either.