Skip to content

Conversation

jhurliman
Copy link
Contributor

@jhurliman jhurliman commented Mar 6, 2025

Changelog

  • cpp: Ensure data is flushed to physical media before FileWriter::end() returns

Docs

None

Description

I've observed periodic data loss writing MCAP files where I destroy the McapWriter (which triggers FileWriter::end() under the hood) then immediately call std::rename() to remove a .active extension from the written MCAP file. Most of the time this works, but maybe ~5% of my MCAPs are missing data at the end where the final bytes of the file are a partial Chunk record.

From my reading it looks like calling std::fclose() immediately followed by std::rename() may not be safe if there are writes that haven't flushed to disk. This change adds additional calls to std::fflush() and ::fsync() to guarantee the OS has delivered all pending writes to the physical media before FileWriter::end() returns, and hence before my std::rename() call.

This change was manually tested, I am still monitoring but haven't seen any partial writes since deploying this fix locally.

…end()

I've observed periodic data loss writing MCAP files where I destroy the `McapWriter` (which triggers `FileWriter::end()` under the hood) then immediately call `std::rename()` to remove a `.active` extension from the written MCAP file. Most of the time this works, but maybe ~5% of my MCAPs are missing data at the end where the final bytes of the file are a partial Chunk record.

From my reading it looks like calling `std::fclose()` immediately followed by `std::rename()` may not be safe if there are writes that haven't flushed to disk. This change adds additional calls to `std::fflush()` and `::fsync()` to guarantee the OS has delivered all pending writes to the physical media before `FileWriter::end()` returns, and hence before my `std::rename()` call.
@jhurliman jhurliman changed the title Call std::fflush() and ::fsync() before std::fclose() in FileWriter::end() [C++] Call std::fflush() and ::fsync() before std::fclose() in FileWriter::end() Mar 6, 2025
@jhurliman jhurliman changed the title [C++] Call std::fflush() and ::fsync() before std::fclose() in FileWriter::end() cpp: Call std::fflush() and ::fsync() before std::fclose() in FileWriter::end() Mar 6, 2025
void FileWriter::end() {
if (file_) {
std::fflush(file_);
#if defined(_WIN32) || defined(_WIN64)
Copy link
Collaborator

Choose a reason for hiding this comment

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

we have a different set of platform-detection ifdefs in visibility.hpp - we don't check for win64, we do check for cygwin.

From https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170, it looks like _WIN64 is redundant with _WIN32.

Also, ::fsync is only defined in unistd.h, which we don't include in mcap, so this code only works when built into another object that includes it. We probably need a conditional include at the top of the file.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Similar with _fileno, which microsoft says is defined in stdio.h - we only include cstdio, which may resolve to the same thing? I haven't tested. https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fileno?view=msvc-170

@indirectlylit indirectlylit removed their request for review July 23, 2025 21:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants