You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Contract owner: backend/fs_durability.py — _sync_descriptor documents that plain os.fsync() is not a durability barrier on macOS
Observed problem
After #129 / EF-018, managed-PDF linearization flushes through fsync_file_path (macOS F_FULLFSYNC when available). The primary write paths that publish canonical managed PDF bytes were only partially upgraded:
Directory sync was moved to fsync_directory via fsync_managed_pdf_parent.
Content sync still calls os.fsync(fp.fileno()) in both atomic_replace_managed_pdf_bytes and store_new_managed_pdf_bytes.
So the bytes that become the durable Work PDF can be published under the weak macOS barrier, while the optional linearize step (when it runs and succeeds) is the only path that applies the stronger one.
Why this is real (not theoretical)
backend/fs_durability.py states explicitly that os.fsync() on macOS returns after handing data to the drive cache without waiting for stable media; _sync_descriptor prefers F_FULLFSYNC for that reason.
Tip 664cb8e still has two os.fsync(fp.fileno()) sites in work_pdf_replace.py and only imports fsync_directory from fs_durability — not fsync_file_path / an open-fd sync helper.
Annotation materialization and uploads return success after these write helpers complete. Linearize is optional (PRKS_PDF_LINEARIZE=0, missing qpdf, or sync-failed): when it does not rewrite, the weak first write is the only content barrier. When it later succeeds, there is still a crash window after the weak publish and before linearize's durable replace.
On macOS (supported local Python runtime), with linearization disabled or unavailable so it does not re-write the file.
Save durable PDF annotations (materialize → atomic_replace_managed_pdf_bytes) or upload a new PDF (store_new_managed_pdf_bytes).
Observe HTTP/API success while content was only os.fsync'd.
Power loss shortly after the write returns.
Observed risk: the managed basename can point at bytes that never reached stable storage, so reopen shows a truncated/corrupt/previous PDF despite a successful save/upload.
(Hard to stage a real drive-cache power loss in CI; regression should spy the sync primitive the same way tests/test_pdf_linearize_durability.py forces the _FULLFSYNC path.)
Expected behavior
Any path that publishes canonical managed PDF bytes must flush file contents with the same strongest platform barrier fs_durability already defines for linearize — before os.replace / before treating an exclusive create as durable — and must fail closed if that content sync raises. Optional linearize must not be the content durability barrier.
Keep raising on content-sync failure before replace/publish.
Optionally surface failed parent-dir sync like linearize’s ok-unsynced-dir rather than ignoring fsync_managed_pdf_parent’s bool.
Do not rely on maybe_linearize_pdf_in_place as the primary content barrier.
Suggested regression tests
With _FULLFSYNC forced (same pattern as tests/test_pdf_linearize_durability.py), assert atomic_replace_managed_pdf_bytes and store_new_managed_pdf_bytes invoke F_FULLFSYNC (not only plain os.fsync) before the file is treated as canonical.
Assert replace / successful store is abandoned when content sync raises.
Keep a non-macOS path asserting plain os.fsync still runs where F_FULLFSYNC is absent.
Severity
P2
Affected files / functions
backend/services/work_pdf_replace.py—atomic_replace_managed_pdf_bytes(content sync beforeos.replace),store_new_managed_pdf_bytes(content sync after exclusive create)backend/pdf_linearize.py— usesfsync_file_path/fsync_directoryfrombackend/fs_durability.pybackend/fs_durability.py—_sync_descriptordocuments that plainos.fsync()is not a durability barrier on macOSObserved problem
After #129 / EF-018, managed-PDF linearization flushes through
fsync_file_path(macOSF_FULLFSYNCwhen available). The primary write paths that publish canonical managed PDF bytes were only partially upgraded:fsync_directoryviafsync_managed_pdf_parent.os.fsync(fp.fileno())in bothatomic_replace_managed_pdf_bytesandstore_new_managed_pdf_bytes.So the bytes that become the durable Work PDF can be published under the weak macOS barrier, while the optional linearize step (when it runs and succeeds) is the only path that applies the stronger one.
Why this is real (not theoretical)
backend/fs_durability.pystates explicitly thatos.fsync()on macOS returns after handing data to the drive cache without waiting for stable media;_sync_descriptorprefersF_FULLFSYNCfor that reason.664cb8estill has twoos.fsync(fp.fileno())sites inwork_pdf_replace.pyand only importsfsync_directoryfromfs_durability— notfsync_file_path/ an open-fd sync helper.PRKS_PDF_LINEARIZE=0, missingqpdf, orsync-failed): when it does not rewrite, the weak first write is the only content barrier. When it later succeeds, there is still a crash window after the weak publish and before linearize's durable replace.Realistic repro / failure path
atomic_replace_managed_pdf_bytes) or upload a new PDF (store_new_managed_pdf_bytes).os.fsync'd.(Hard to stage a real drive-cache power loss in CI; regression should spy the sync primitive the same way
tests/test_pdf_linearize_durability.pyforces the_FULLFSYNCpath.)Expected behavior
Any path that publishes canonical managed PDF bytes must flush file contents with the same strongest platform barrier
fs_durabilityalready defines for linearize — beforeos.replace/ before treating an exclusive create as durable — and must fail closed if that content sync raises. Optional linearize must not be the content durability barrier.Fix direction
_sync_descriptor(export a smallfsync_open_file/fsync_open_fdif needed; PR fix: make the restore journal and component renames power-loss durable #133 already generalizes this for restore) instead of bareos.fsync.ok-unsynced-dirrather than ignoringfsync_managed_pdf_parent’s bool.maybe_linearize_pdf_in_placeas the primary content barrier.Suggested regression tests
_FULLFSYNCforced (same pattern astests/test_pdf_linearize_durability.py), assertatomic_replace_managed_pdf_bytesandstore_new_managed_pdf_bytesinvokeF_FULLFSYNC(not only plainos.fsync) before the file is treated as canonical.os.fsyncstill runs whereF_FULLFSYNCis absent.Evidence links
Reviewed and commented using Grok Bot.