pkg/atomicwriter: use sequential file access on Windows

Using sequential file access ([FILE_FLAG_SEQUENTIAL_SCAN]) prevents
Windows from aggressively keeping files in the cache, freeing up system
memory for other tasks. On Linux, these changes have no effect, as the
sequential package use the standard (os.CreateTemp, os.OpenFile) on
non-Windows platforms. Refer to the [Win32 API documentation] for details
on sequential file access.

[Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN
[FILE_FLAG_SEQUENTIAL_SCAN]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-03-09 15:30:04 +01:00
parent ef56b83597
commit be6e92a57b

View File

@@ -6,6 +6,8 @@ import (
"io"
"os"
"path/filepath"
"github.com/moby/sys/sequential"
)
func validateDestination(fileName string) error {
@@ -66,6 +68,13 @@ func validateFileMode(mode os.FileMode) error {
// temporary file and closing it atomically changes the temporary file to
// destination path. Writing and closing concurrently is not allowed.
// NOTE: umask is not considered for the file's permissions.
//
// New uses [sequential.CreateTemp] to use sequential file access on Windows,
// avoiding depleting the standby list un-necessarily. On Linux, this equates to
// a regular [os.CreateTemp]. Refer to the [Win32 API documentation] for details
// on sequential file access.
//
// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN
func New(filename string, perm os.FileMode) (io.WriteCloser, error) {
if err := validateDestination(filename); err != nil {
return nil, err
@@ -75,7 +84,7 @@ func New(filename string, perm os.FileMode) (io.WriteCloser, error) {
return nil, err
}
f, err := os.CreateTemp(filepath.Dir(abspath), ".tmp-"+filepath.Base(filename))
f, err := sequential.CreateTemp(filepath.Dir(abspath), ".tmp-"+filepath.Base(filename))
if err != nil {
return nil, err
}
@@ -197,8 +206,15 @@ func (w syncFileCloser) Close() error {
// FileWriter opens a file writer inside the set. The file
// should be synced and closed before calling commit.
//
// FileWriter uses [sequential.OpenFile] to use sequential file access on Windows,
// avoiding depleting the standby list un-necessarily. On Linux, this equates to
// a regular [os.OpenFile]. Refer to the [Win32 API documentation] for details
// on sequential file access.
//
// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN
func (ws *WriteSet) FileWriter(name string, flag int, perm os.FileMode) (io.WriteCloser, error) {
f, err := os.OpenFile(filepath.Join(ws.root, name), flag, perm)
f, err := sequential.OpenFile(filepath.Join(ws.root, name), flag, perm)
if err != nil {
return nil, err
}