Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PBM-1329 Write to temporary file name and sync before renaming #954

Merged
merged 2 commits into from
Aug 5, 2024

Conversation

DanielOliverRJ
Copy link
Contributor

Whilst trying to understand historic cases where we have seen BSON deserialisation errors, I realised it was possible for filesystem writes to be incomplete, but be unable to determine the fact. In particular, once an oplog file exists on the filesystem, it is assumed to be complete even though the write may have been interrupted.

This change performs all filesystem writes to a temporary file in the same directory and does a rename once the sync has returned.

In the event of a failure, calling Close() on the same file handle does not cause an error because the deferred call discards the error.

I do not believe non-filesystem storage are affected by this problem, as the providers have other checks in place to ensure the upload completed.

@it-percona-cla
Copy link

it-percona-cla commented Jun 25, 2024

CLA assistant check
All committers have signed the CLA.

@defbin
Copy link
Member

defbin commented Jun 27, 2024

hi @DanielOliverRJ. thank you for your contributions. Could you please try to review/sign the CLA again so we can proceed?

@DanielOliverRJ
Copy link
Contributor Author

Sorry, I had to get the CLA text approved before I could sign. This is now completed.

@defbin
Copy link
Member

defbin commented Jul 24, 2024

Hi @DanielOliverRJ

Thank you for your contribution.

I see one problem here:

err := os.MkdirAll(path.Dir(filepath), os.ModeDir|0o755)
if err != nil {
return errors.Wrapf(err, "create path %s", path.Dir(filepath))
}
fw, err := os.Create(filepath)
if err != nil {
return errors.Wrapf(err, "create destination file <%s>", filepath)
}
defer fw.Close()
err = os.Chmod(filepath, 0o644)
if err != nil {
return errors.Wrapf(err, "change permissions for file <%s>", filepath)
}
_, err = io.Copy(fw, data)
if err != nil {
return errors.Wrapf(err, "copy file <%s>", filepath)
}
err = fw.Sync()
if err != nil {
return errors.Wrapf(err, "sync file <%s>", filepath)
}
err = fw.Close()
if err != nil {
return errors.Wrapf(err, "close file <%s>", filepath)
}
err = os.Rename(filepath, finalpath)

Please ensure that the temporary file is deleted in case of an error before the function exists. Thus, the caller code may remain unaware of the implementation details specific to file system storage.

@DanielOliverRJ
Copy link
Contributor Author

I've re-factored slightly to move write to a common function and attempt to remove the temporary file on any error.

Copy link
Member

@defbin defbin left a comment

Choose a reason for hiding this comment

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

tested by debugging

@defbin defbin merged commit 64b3a06 into percona:dev Aug 5, 2024
24 checks passed
@defbin defbin added this to the 2.6.0 milestone Aug 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants