aboutsummaryrefslogtreecommitdiff
path: root/internal/persistence
diff options
context:
space:
mode:
Diffstat (limited to 'internal/persistence')
-rw-r--r--internal/persistence/atomic.go12
1 files changed, 10 insertions, 2 deletions
diff --git a/internal/persistence/atomic.go b/internal/persistence/atomic.go
index a3cc06a..2b24391 100644
--- a/internal/persistence/atomic.go
+++ b/internal/persistence/atomic.go
@@ -3,11 +3,19 @@
package persistence
import (
+ "errors"
"fmt"
"os"
"path/filepath"
)
+// ErrDurabilityUncertain reports that the atomic replacement was renamed into
+// place but the directory could not be synced (or a post-rename chmod
+// failed). The new content is visible to readers, but a crash may still lose
+// it. Callers that must distinguish "nothing happened" from "the replacement
+// landed but may not survive a crash" should test errors.Is against this.
+var ErrDurabilityUncertain = errors.New("replacement completed; durability uncertain")
+
// WriteFile writes and syncs a temporary file in the destination directory,
// atomically renames it over path, then syncs the directory. Existing file
// permissions are preserved; perm is used for new files. Callers must own the
@@ -43,11 +51,11 @@ func WriteFile(path string, data []byte, perm os.FileMode) error {
}
d, err := os.Open(dir)
if err != nil {
- return fmt.Errorf("replacement completed; open directory for sync: %w", err)
+ return fmt.Errorf("%w: open directory for sync: %v", ErrDurabilityUncertain, err)
}
defer d.Close()
if err := d.Sync(); err != nil {
- return fmt.Errorf("replacement completed; sync directory: %w", err)
+ return fmt.Errorf("%w: sync directory: %v", ErrDurabilityUncertain, err)
}
return nil
}