lib/fs: replace WriteFileAndSync with MustWriteAndSync

When WriteFileAndSync fails, then the caller eventually logs the error message
and exits. The error message returned by WriteFileAndSync already contains the path
to the file, which couldn't be created. This information alongside the call stack
is enough for debugging the issue. So just use log.Panicf("FATAL: ...") inside MustWriteAndSync().
This simplifies error handling at caller side a bit.
This commit is contained in:
Aliaksandr Valialkin 2023-04-13 21:33:15 -07:00
parent 25f089de9d
commit 2a8395be05
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
7 changed files with 31 additions and 54 deletions

View file

@ -25,7 +25,7 @@ func MustSyncPath(path string) {
mustSyncPath(path)
}
// WriteFileAndSync writes data to the file at path and then calls fsync on the created file.
// MustWriteFileAndSync writes data to the file at path and then calls fsync on the created file.
//
// The fsync guarantees that the written data survives hardware reset after successful call.
//
@ -33,20 +33,19 @@ func MustSyncPath(path string) {
// in the middle of the write.
// Use WriteFileAtomically 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 WriteFileAndSync(path string, data []byte) error {
func MustWriteFileAndSync(path string, data []byte) {
f, err := filestream.Create(path, false)
if err != nil {
return err
logger.Panicf("FATAL: cannot create file: %s", err)
}
if _, err := f.Write(data); err != nil {
f.MustClose()
// Do not call MustRemoveAll(path), so the user could inspect
// the file contents during investigation of the issue.
return fmt.Errorf("cannot write %d bytes to %q: %w", len(data), path, err)
logger.Panicf("FATAL: cannot write %d bytes to %q: %s", len(data), path, err)
}
// Sync and close the file.
f.MustClose()
return nil
}
// WriteFileAtomically atomically writes data to the given file path.
@ -70,9 +69,7 @@ func WriteFileAtomically(path string, data []byte, canOverwrite bool) error {
// Write data to a temporary file.
n := atomic.AddUint64(&tmpFileNum, 1)
tmpPath := fmt.Sprintf("%s.tmp.%d", path, n)
if err := WriteFileAndSync(tmpPath, data); err != nil {
return fmt.Errorf("cannot write data to temporary file: %w", err)
}
MustWriteFileAndSync(tmpPath, data)
// Atomically move the temporary file from tmpPath to path.
if err := os.Rename(tmpPath, path); err != nil {

View file

@ -38,24 +38,19 @@ func (mp *inmemoryPart) StoreToDisk(path string) error {
return fmt.Errorf("cannot create directory %q: %w", path, err)
}
metaindexPath := filepath.Join(path, metaindexFilename)
if err := fs.WriteFileAndSync(metaindexPath, mp.metaindexData.B); err != nil {
return fmt.Errorf("cannot store metaindex: %w", err)
}
fs.MustWriteFileAndSync(metaindexPath, mp.metaindexData.B)
indexPath := filepath.Join(path, indexFilename)
if err := fs.WriteFileAndSync(indexPath, mp.indexData.B); err != nil {
return fmt.Errorf("cannot store index: %w", err)
}
fs.MustWriteFileAndSync(indexPath, mp.indexData.B)
itemsPath := filepath.Join(path, itemsFilename)
if err := fs.WriteFileAndSync(itemsPath, mp.itemsData.B); err != nil {
return fmt.Errorf("cannot store items: %w", err)
}
fs.MustWriteFileAndSync(itemsPath, mp.itemsData.B)
lensPath := filepath.Join(path, lensFilename)
if err := fs.WriteFileAndSync(lensPath, mp.lensData.B); err != nil {
return fmt.Errorf("cannot store lens: %w", err)
}
if err := mp.ph.WriteMetadata(path); err != nil {
return fmt.Errorf("cannot store metadata: %w", err)
}
fs.MustWriteFileAndSync(lensPath, mp.lensData.B)
mp.ph.MustWriteMetadata(path)
fs.MustSyncPath(path)
// Do not sync parent directory - it must be synced by the caller.
return nil

View file

@ -113,7 +113,7 @@ func (ph *partHeader) ReadMetadata(partPath string) error {
return nil
}
func (ph *partHeader) WriteMetadata(partPath string) error {
func (ph *partHeader) MustWriteMetadata(partPath string) {
phj := &partHeaderJSON{
ItemsCount: ph.itemsCount,
BlocksCount: ph.blocksCount,
@ -128,8 +128,5 @@ func (ph *partHeader) WriteMetadata(partPath string) error {
// There is no need in calling fs.WriteFileAtomically() here,
// since the file is created only once during part creatinng
// and the part directory is synced aftewards.
if err := fs.WriteFileAndSync(metadataPath, metadata); err != nil {
return fmt.Errorf("cannot create %q: %w", metadataPath, err)
}
return nil
fs.MustWriteFileAndSync(metadataPath, metadata)
}

View file

@ -1245,9 +1245,7 @@ func (tb *Table) mergePartsInternal(dstPartPath string, bsw *blockStreamWriter,
return nil, fmt.Errorf("cannot merge %d parts to %s: %w", len(bsrs), dstPartPath, err)
}
if dstPartPath != "" {
if err := ph.WriteMetadata(dstPartPath); err != nil {
logger.Panicf("FATAL: cannot write metadata to %s: %s", dstPartPath, err)
}
ph.MustWriteMetadata(dstPartPath)
}
return &ph, nil
}

View file

@ -41,24 +41,19 @@ func (mp *inmemoryPart) StoreToDisk(path string) error {
return fmt.Errorf("cannot create directory %q: %w", path, err)
}
timestampsPath := filepath.Join(path, timestampsFilename)
if err := fs.WriteFileAndSync(timestampsPath, mp.timestampsData.B); err != nil {
return fmt.Errorf("cannot store timestamps: %w", err)
}
fs.MustWriteFileAndSync(timestampsPath, mp.timestampsData.B)
valuesPath := filepath.Join(path, valuesFilename)
if err := fs.WriteFileAndSync(valuesPath, mp.valuesData.B); err != nil {
return fmt.Errorf("cannot store values: %w", err)
}
fs.MustWriteFileAndSync(valuesPath, mp.valuesData.B)
indexPath := filepath.Join(path, indexFilename)
if err := fs.WriteFileAndSync(indexPath, mp.indexData.B); err != nil {
return fmt.Errorf("cannot store index: %w", err)
}
fs.MustWriteFileAndSync(indexPath, mp.indexData.B)
metaindexPath := filepath.Join(path, metaindexFilename)
if err := fs.WriteFileAndSync(metaindexPath, mp.metaindexData.B); err != nil {
return fmt.Errorf("cannot store metaindex: %w", err)
}
if err := mp.ph.WriteMetadata(path); err != nil {
return fmt.Errorf("cannot store metadata: %w", err)
}
fs.MustWriteFileAndSync(metaindexPath, mp.metaindexData.B)
mp.ph.MustWriteMetadata(path)
fs.MustSyncPath(path)
// Do not sync parent directory - it must be synced by the caller.
return nil

View file

@ -165,7 +165,7 @@ func (ph *partHeader) ReadMetadata(partPath string) error {
return nil
}
func (ph *partHeader) WriteMetadata(partPath string) error {
func (ph *partHeader) MustWriteMetadata(partPath string) {
metadata, err := json.Marshal(ph)
if err != nil {
logger.Panicf("BUG: cannot marshal partHeader metadata: %s", err)
@ -174,8 +174,5 @@ func (ph *partHeader) WriteMetadata(partPath string) error {
// There is no need in calling fs.WriteFileAtomically() here,
// since the file is created only once during part creatinng
// and the part directory is synced aftewards.
if err := fs.WriteFileAndSync(metadataPath, metadata); err != nil {
return fmt.Errorf("cannot create %q: %w", metadataPath, err)
}
return nil
fs.MustWriteFileAndSync(metadataPath, metadata)
}

View file

@ -1465,9 +1465,7 @@ func (pt *partition) mergePartsInternal(dstPartPath string, bsw *blockStreamWrit
}
if dstPartPath != "" {
ph.MinDedupInterval = GetDedupInterval()
if err := ph.WriteMetadata(dstPartPath); err != nil {
logger.Panicf("FATAL: cannot store metadata to %s: %s", dstPartPath, err)
}
ph.MustWriteMetadata(dstPartPath)
}
return &ph, nil
}