// 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 } if err := os.MkdirAll(dirpath, privateDirMode); err != nil { return nil, err } p := path.Join(dirpath, walName(0, 0)) f, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600) if err != nil { return nil, err } l, err := fileutil.NewLock(f.Name()) if err != nil { return nil, err } err = l.Lock() if err != nil { return nil, err } w := &WAL{ dir: dirpath, metadata: metadata, seq: 0, f: f, encoder: newEncoder(f, 0), } w.locks = append(w.locks, l) 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 } return w, nil }
func openAtIndex(dirpath string, snap walpb.Snapshot, all bool) (*WAL, error) { names, err := fileutil.ReadDir(dirpath) if err != nil { return nil, err } names = checkWalNames(names) if len(names) == 0 { return nil, ErrFileNotFound } nameIndex, ok := searchIndex(names, snap.Index) if !ok || !isValidSeq(names[nameIndex:]) { return nil, ErrFileNotFound } // open the wal files for reading rcs := make([]io.ReadCloser, 0) ls := make([]fileutil.Lock, 0) for _, name := range names[nameIndex:] { f, err := os.Open(path.Join(dirpath, name)) if err != nil { return nil, err } l, err := fileutil.NewLock(f.Name()) if err != nil { return nil, err } err = l.TryLock() if err != nil { if all { return nil, err } else { log.Printf("wal: opened all the files until %s, since it is still in use by an etcd server", name) break } } rcs = append(rcs, f) ls = append(ls, l) } rc := MultiReadCloser(rcs...) // open the lastest wal file for appending seq, _, err := parseWalName(names[len(names)-1]) if err != nil { rc.Close() return nil, err } last := path.Join(dirpath, names[len(names)-1]) f, err := os.OpenFile(last, os.O_WRONLY|os.O_APPEND, 0) if err != nil { rc.Close() return nil, err } // create a WAL ready for reading w := &WAL{ dir: dirpath, start: snap, decoder: newDecoder(rc), f: f, seq: seq, locks: ls, } return w, nil }
// Cut closes current file written and creates a new one ready to append. // cut first creates a temp wal file and writes necessary headers into it. // Then cut atomtically rename temp wal file to a wal file. func (w *WAL) Cut() error { // close old wal file if err := w.sync(); err != nil { return err } if err := w.f.Close(); err != nil { return err } fpath := path.Join(w.dir, walName(w.seq+1, w.enti+1)) ftpath := fpath + ".tmp" // create a temp wal file with name sequence + 1, or tuncate the existing one ft, err := os.OpenFile(ftpath, os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { return err } // update writer and save the previous crc w.f = ft prevCrc := w.encoder.crc.Sum32() w.encoder = newEncoder(w.f, prevCrc) if err := w.saveCrc(prevCrc); err != nil { return err } if err := w.encoder.encode(&walpb.Record{Type: metadataType, Data: w.metadata}); err != nil { return err } if err := w.saveState(&w.state); err != nil { return err } // close temp wal file if err := w.sync(); err != nil { return err } if err := w.f.Close(); err != nil { return err } // atomically move temp wal file to wal file if err := os.Rename(ftpath, fpath); err != nil { return err } // open the wal file and update writer again f, err := os.OpenFile(fpath, os.O_WRONLY|os.O_APPEND, 0600) if err != nil { return err } w.f = f prevCrc = w.encoder.crc.Sum32() w.encoder = newEncoder(w.f, prevCrc) // lock the new wal file l, err := fileutil.NewLock(f.Name()) if err != nil { return err } err = l.Lock() if err != nil { return err } w.locks = append(w.locks, l) // increase the wal seq w.seq++ log.Printf("wal: segmented wal file %v is created", fpath) return nil }
func openAtIndex(dirpath string, snap walpb.Snapshot, write bool) (*WAL, error) { names, err := fileutil.ReadDir(dirpath) if err != nil { return nil, err } names = checkWalNames(names) if len(names) == 0 { return nil, ErrFileNotFound } nameIndex, ok := searchIndex(names, snap.Index) if !ok || !isValidSeq(names[nameIndex:]) { return nil, ErrFileNotFound } // open the wal files for reading rcs := make([]io.ReadCloser, 0) ls := make([]fileutil.Lock, 0) for _, name := range names[nameIndex:] { f, err := os.Open(path.Join(dirpath, name)) if err != nil { return nil, err } l, err := fileutil.NewLock(f.Name()) if err != nil { return nil, err } err = l.TryLock() if err != nil { if write { return nil, err } } rcs = append(rcs, f) ls = append(ls, l) } rc := MultiReadCloser(rcs...) // create a WAL ready for reading w := &WAL{ dir: dirpath, start: snap, decoder: newDecoder(rc), locks: ls, } if write { // open the lastest wal file for appending seq, _, err := parseWalName(names[len(names)-1]) if err != nil { rc.Close() return nil, err } last := path.Join(dirpath, names[len(names)-1]) f, err := os.OpenFile(last, os.O_WRONLY|os.O_APPEND, 0) if err != nil { rc.Close() return nil, err } err = fileutil.Preallocate(f, segmentSizeBytes) if err != nil { rc.Close() plog.Errorf("failed to allocate space when creating new wal file (%v)", err) return nil, err } w.f = f w.seq = seq } return w, nil }