lib/fs: rename WriteFileAtomically to MustWriteAtomic

Callers of this function log the returned error and exit.
So let's just log the error with the given filepath and the call stack
inside the function itself and then exit. This simplifies the code
at callers' place while leaves the same level of debuggability in case of errors.
This commit is contained in:
Aliaksandr Valialkin 2023-04-13 22:41:12 -07:00
parent 036a7b7365
commit 30425ca81a
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
8 changed files with 21 additions and 40 deletions

View file

@ -433,9 +433,7 @@ func mustLoadRollupResultCacheKeyPrefix(path string) {
func mustSaveRollupResultCacheKeyPrefix(path string) {
path = path + ".key.prefix"
data := encoding.MarshalUint64(nil, rollupResultCacheKeyPrefix)
if err := fs.WriteFileAtomically(path, data, true); err != nil {
logger.Fatalf("cannot store rollupResult cache key prefix to %q: %s", path, err)
}
fs.MustWriteAtomic(path, data, true)
}
var tooBigRollupResults = metrics.NewCounter("vm_too_big_rollup_results_total")

View file

@ -31,7 +31,7 @@ func MustSyncPath(path string) {
//
// This function may leave the file at the path in inconsistent state on app crash
// in the middle of the write.
// Use WriteFileAtomically if the file at the path must be either written in full
// Use MustWriteAtomic if the file at the path must be either written in full
// or not written at all on app crash in the middle of the write.
func MustWriteSync(path string, data []byte) {
f, err := filestream.Create(path, false)
@ -48,7 +48,7 @@ func MustWriteSync(path string, data []byte) {
f.MustClose()
}
// WriteFileAtomically atomically writes data to the given file path.
// MustWriteAtomic atomically writes data to the given file path.
//
// This function returns only after the file is fully written and synced
// to the underlying storage.
@ -58,12 +58,12 @@ func MustWriteSync(path string, data []byte) {
//
// If the file at path already exists, then the file is overwritten atomically if canOverwrite is true.
// Otherwise error is returned.
func WriteFileAtomically(path string, data []byte, canOverwrite bool) error {
func MustWriteAtomic(path string, data []byte, canOverwrite bool) {
// Check for the existing file. It is expected that
// the WriteFileAtomically function cannot be called concurrently
// the MustWriteAtomic function cannot be called concurrently
// with the same `path`.
if IsPathExist(path) && !canOverwrite {
return fmt.Errorf("cannot create file %q, since it already exists", path)
logger.Panicf("FATAL: cannot create file %q, since it already exists", path)
}
// Write data to a temporary file.
@ -75,28 +75,26 @@ func WriteFileAtomically(path string, data []byte, canOverwrite bool) error {
if err := os.Rename(tmpPath, path); err != nil {
// do not call MustRemoveAll(tmpPath) here, so the user could inspect
// the file contents during investigation of the issue.
return fmt.Errorf("cannot move temporary file %q to %q: %w", tmpPath, path, err)
logger.Panicf("FATAL: cannot move temporary file %q to %q: %s", tmpPath, path, err)
}
// Sync the containing directory, so the file is guaranteed to appear in the directory.
// See https://www.quora.com/When-should-you-fsync-the-containing-directory-in-addition-to-the-file-itself
absPath, err := filepath.Abs(path)
if err != nil {
return fmt.Errorf("cannot obtain absolute path to %q: %w", path, err)
logger.Panicf("FATAL: cannot obtain absolute path to %q: %s", path, err)
}
parentDirPath := filepath.Dir(absPath)
MustSyncPath(parentDirPath)
return nil
}
// IsTemporaryFileName returns true if fn matches temporary file name pattern
// from WriteFileAtomically.
// from MustWriteAtomic.
func IsTemporaryFileName(fn string) bool {
return tmpFileNameRe.MatchString(fn)
}
// tmpFileNameRe is regexp for temporary file name - see WriteFileAtomically for details.
// tmpFileNameRe is regexp for temporary file name - see MustWriteAtomic for details.
var tmpFileNameRe = regexp.MustCompile(`\.tmp\.\d+$`)
// MustMkdirIfNotExist creates the given path dir if it isn't exist.
@ -104,9 +102,7 @@ func MustMkdirIfNotExist(path string) {
if IsPathExist(path) {
return
}
if err := mkdirSync(path); err != nil {
logger.Panicf("FATAL: cannot create directory: %s", err)
}
mustMkdirSync(path)
}
// MustMkdirFailIfExist creates the given path dir if it isn't exist.
@ -116,20 +112,17 @@ func MustMkdirFailIfExist(path string) {
if IsPathExist(path) {
logger.Panicf("FATAL: the %q already exists", path)
}
if err := mkdirSync(path); err != nil {
logger.Panicf("FATAL: cannot create directory: %s", err)
}
mustMkdirSync(path)
}
func mkdirSync(path string) error {
func mustMkdirSync(path string) {
if err := os.MkdirAll(path, 0755); err != nil {
return err
logger.Panicf("FATAL: cannot create directory: %s", err)
}
// Sync the parent directory, so the created directory becomes visible
// in the fs after power loss.
parentDirPath := filepath.Dir(path)
MustSyncPath(parentDirPath)
return nil
}
// RemoveDirContents removes all the contents of the given dir if it exists.
@ -268,9 +261,7 @@ func MustRemoveTemporaryDirs(dir string) {
// HardLinkFiles makes hard links for all the files from srcDir in dstDir.
func HardLinkFiles(srcDir, dstDir string) error {
if err := mkdirSync(dstDir); err != nil {
return fmt.Errorf("cannot create dstDir=%q: %w", dstDir, err)
}
mustMkdirSync(dstDir)
des, err := os.ReadDir(srcDir)
if err != nil {

View file

@ -125,7 +125,7 @@ func (ph *partHeader) MustWriteMetadata(partPath string) {
logger.Panicf("BUG: cannot marshal partHeader metadata: %s", err)
}
metadataPath := filepath.Join(partPath, metadataFilename)
// There is no need in calling fs.WriteFileAtomically() here,
// There is no need in calling fs.MustWriteAtomic() here,
// since the file is created only once during part creatinng
// and the part directory is synced aftewards.
fs.MustWriteSync(metadataPath, metadata)

View file

@ -1506,9 +1506,7 @@ func mustWritePartNames(pws []*partWrapper, dstDir string) {
logger.Panicf("BUG: cannot marshal partNames to JSON: %s", err)
}
partNamesPath := filepath.Join(dstDir, partsFilename)
if err := fs.WriteFileAtomically(partNamesPath, data, true); err != nil {
logger.Panicf("FATAL: cannot update %s: %s", partNamesPath, err)
}
fs.MustWriteAtomic(partNamesPath, data, true)
}
func mustReadPartNames(srcDir string) []string {

View file

@ -210,9 +210,7 @@ func tryOpeningQueue(path, name string, chunkFileSize, maxBlockSize, maxPendingB
// Create initial chunk file.
filepath := q.chunkFilePath(0)
if err := fs.WriteFileAtomically(filepath, nil, false); err != nil {
return nil, fmt.Errorf("cannot create %q: %w", filepath, err)
}
fs.MustWriteAtomic(filepath, nil, false)
}
if mi.Name != q.name {
return nil, fmt.Errorf("unexpected queue name; got %q; want %q", mi.Name, q.name)

View file

@ -171,7 +171,7 @@ func (ph *partHeader) MustWriteMetadata(partPath string) {
logger.Panicf("BUG: cannot marshal partHeader metadata: %s", err)
}
metadataPath := filepath.Join(partPath, metadataFilename)
// There is no need in calling fs.WriteFileAtomically() here,
// There is no need in calling fs.MustWriteAtomic() here,
// since the file is created only once during part creatinng
// and the part directory is synced aftewards.
fs.MustWriteSync(metadataPath, metadata)

View file

@ -1938,9 +1938,7 @@ func mustWritePartNames(pwsSmall, pwsBig []*partWrapper, dstDir string) {
logger.Panicf("BUG: cannot marshal partNames to JSON: %s", err)
}
partNamesPath := filepath.Join(dstDir, partsFilename)
if err := fs.WriteFileAtomically(partNamesPath, data, true); err != nil {
logger.Panicf("FATAL: cannot update %s: %s", partNamesPath, err)
}
fs.MustWriteAtomic(partNamesPath, data, true)
}
func getPartNames(pws []*partWrapper) []string {

View file

@ -993,9 +993,7 @@ func mustGetMinTimestampForCompositeIndex(metadataDir string, isEmptyDB bool) in
}
minTimestamp = date * msecPerDay
dateBuf := encoding.MarshalInt64(nil, minTimestamp)
if err := fs.WriteFileAtomically(path, dateBuf, true); err != nil {
logger.Fatalf("cannot store minTimestampForCompositeIndex: %s", err)
}
fs.MustWriteAtomic(path, dateBuf, true)
return minTimestamp
}