// Open opens the WAL at the given snap. // The snap SHOULD have been previously saved to the WAL, or the following // ReadAll will fail. // The returned WAL is ready to read and the first record will be the one after // the given snap. The WAL cannot be appended to before reading out all of its // previous records. func Open(dirpath string, snap walpb.Snapshot) (*WAL, error) { w, err := openAtIndex(dirpath, snap, true) if err != nil { return nil, err } if w.dirFile, err = fileutil.OpenDir(w.dir); err != nil { return nil, err } return w, nil }
func (w *WAL) renameWal(tmpdirpath string) (*WAL, error) { // On non-Windows platforms, hold the lock while renaming. Releasing // the lock and trying to reacquire it quickly can be flaky because // it's possible the process will fork to spawn a process while this is // happening. The fds are set up as close-on-exec by the Go runtime, // but there is a window between the fork and the exec where another // process holds the lock. if err := os.RemoveAll(w.dir); err != nil { return nil, err } if err := os.Rename(tmpdirpath, w.dir); err != nil { return nil, err } w.fp = newFilePipeline(w.dir, SegmentSizeBytes) df, err := fileutil.OpenDir(w.dir) w.dirFile = df return w, err }
// Create creates a WAL ready for appending records. The given metadata is // recorded at the head of each WAL file, and can be retrieved with ReadAll. func Create(dirpath string, metadata []byte) (*WAL, error) { if Exist(dirpath) { return nil, os.ErrExist } // keep temporary wal directory so WAL initialization appears atomic tmpdirpath := path.Clean(dirpath) + ".tmp" if fileutil.Exist(tmpdirpath) { if err := os.RemoveAll(tmpdirpath); err != nil { return nil, err } } if err := fileutil.CreateDirAll(tmpdirpath); err != nil { return nil, err } p := path.Join(tmpdirpath, walName(0, 0)) f, err := fileutil.LockFile(p, os.O_WRONLY|os.O_CREATE, fileutil.PrivateFileMode) if err != nil { return nil, err } if _, err = f.Seek(0, os.SEEK_END); err != nil { return nil, err } if err = fileutil.Preallocate(f.File, SegmentSizeBytes, true); err != nil { return nil, err } w := &WAL{ dir: dirpath, metadata: metadata, encoder: newEncoder(f, 0), } w.locks = append(w.locks, f) if err = w.saveCrc(0); err != nil { return nil, err } if err = w.encoder.encode(&walpb.Record{Type: metadataType, Data: metadata}); err != nil { return nil, err } if err = w.SaveSnapshot(walpb.Snapshot{}); err != nil { return nil, err } if w, err = w.renameWal(tmpdirpath); err != nil { return nil, err } // directory was renamed; sync parent dir to persist rename pdir, perr := fileutil.OpenDir(path.Dir(w.dir)) if perr != nil { return nil, perr } if perr = fileutil.Fsync(pdir); perr != nil { return nil, perr } if perr = pdir.Close(); err != nil { return nil, perr } return w, nil }