diff --git a/tsdb/chunks/chunks.go b/tsdb/chunks/chunks.go index acdcc4a17..5a824d965 100644 --- a/tsdb/chunks/chunks.go +++ b/tsdb/chunks/chunks.go @@ -217,22 +217,35 @@ func (w *Writer) cut() error { return nil } -func cutSegmentFile(dirFile *os.File, magicNumber uint32, chunksFormat byte, allocSize int64) (headerSize int, newFile *os.File, seq int, err error) { +func cutSegmentFile(dirFile *os.File, magicNumber uint32, chunksFormat byte, allocSize int64) (headerSize int, newFile *os.File, seq int, returnErr error) { p, seq, err := nextSequenceFile(dirFile.Name()) if err != nil { - return 0, nil, 0, err + return 0, nil, 0, errors.Wrap(err, "next sequence file") } - f, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE, 0666) + ptmp := p + ".tmp" + f, err := os.OpenFile(ptmp, os.O_WRONLY|os.O_CREATE, 0666) if err != nil { - return 0, nil, 0, err + return 0, nil, 0, errors.Wrap(err, "open temp file") } + defer func() { + if returnErr != nil { + var merr tsdb_errors.MultiError + merr.Add(returnErr) + if f != nil { + merr.Add(f.Close()) + } + // Calling RemoveAll on a non-existent file does not return error. + merr.Add(os.RemoveAll(ptmp)) + returnErr = merr.Err() + } + }() if allocSize > 0 { if err = fileutil.Preallocate(f, allocSize, true); err != nil { - return 0, nil, 0, err + return 0, nil, 0, errors.Wrap(err, "preallocate") } } if err = dirFile.Sync(); err != nil { - return 0, nil, 0, err + return 0, nil, 0, errors.Wrap(err, "sync directory") } // Write header metadata for new file. @@ -242,7 +255,24 @@ func cutSegmentFile(dirFile *os.File, magicNumber uint32, chunksFormat byte, all n, err := f.Write(metab) if err != nil { - return 0, nil, 0, err + return 0, nil, 0, errors.Wrap(err, "write header") + } + if err := f.Close(); err != nil { + return 0, nil, 0, errors.Wrap(err, "close temp file") + } + f = nil + + if err := fileutil.Rename(ptmp, p); err != nil { + return 0, nil, 0, errors.Wrap(err, "replace file") + } + + f, err = os.OpenFile(p, os.O_WRONLY, 0666) + if err != nil { + return 0, nil, 0, errors.Wrap(err, "open final file") + } + // Skip header for further writes. + if _, err := f.Seek(int64(n), 0); err != nil { + return 0, nil, 0, errors.Wrap(err, "seek in final file") } return n, f, seq, nil } diff --git a/tsdb/fileutil/fileutil.go b/tsdb/fileutil/fileutil.go index be9c6f226..00bf63950 100644 --- a/tsdb/fileutil/fileutil.go +++ b/tsdb/fileutil/fileutil.go @@ -125,19 +125,5 @@ func Replace(from, to string) error { } } - if err := os.Rename(from, to); err != nil { - return err - } - - // Directory was renamed; sync parent dir to persist rename. - pdir, err := OpenDir(filepath.Dir(to)) - if err != nil { - return err - } - - if err = pdir.Sync(); err != nil { - pdir.Close() - return err - } - return pdir.Close() + return Rename(from, to) }