func (d *DB) recoverJournal() error { s := d.s icmp := s.cmp ff0, err := s.getFiles(storage.TypeJournal) if err != nil { return err } ff1 := files(ff0) ff1.sort() ff2 := make([]storage.File, 0, len(ff1)) for _, file := range ff1 { if file.Num() >= s.stJournalNum || file.Num() == s.stPrevJournalNum { s.markFileNum(file.Num()) ff2 = append(ff2, file) } } var jr *journal.Reader var of storage.File var mem *memdb.DB batch := new(Batch) cm := newCMem(s) buf := new(util.Buffer) // Options. strict := s.o.GetStrict(opt.StrictJournal) checksum := s.o.GetStrict(opt.StrictJournalChecksum) writeBuffer := s.o.GetWriteBuffer() recoverJournal := func(file storage.File) error { s.logf("journal@recovery recovering @%d", file.Num()) reader, err := file.Open() if err != nil { return err } defer reader.Close() if jr == nil { jr = journal.NewReader(reader, dropper{s, file}, strict, checksum) } else { jr.Reset(reader, dropper{s, file}, strict, checksum) } if of != nil { if mem.Len() > 0 { if err := cm.flush(mem, 0); err != nil { return err } } if err := cm.commit(file.Num(), d.seq); err != nil { return err } cm.reset() of.Remove() of = nil } // Reset memdb. mem.Reset() for { r, err := jr.Next() if err != nil { if err == io.EOF { break } return err } buf.Reset() if _, err := buf.ReadFrom(r); err != nil { if strict { return err } continue } if err := batch.decode(buf.Bytes()); err != nil { return err } if err := batch.memReplay(mem); err != nil { return err } d.seq = batch.seq + uint64(batch.len()) if mem.Size() >= writeBuffer { // Large enough, flush it. if err := cm.flush(mem, 0); err != nil { return err } // Reset memdb. mem.Reset() } } of = file return nil } // Recover all journals. if len(ff2) > 0 { s.logf("journal@recovery F·%d", len(ff2)) mem = memdb.New(icmp, toPercent(writeBuffer, kWriteBufferPercent)) for _, file := range ff2 { if err := recoverJournal(file); err != nil { return err } } // Flush the last journal. if mem.Len() > 0 { if err := cm.flush(mem, 0); err != nil { return err } } } // Create a new journal. if _, err := d.newMem(); err != nil { return err } // Commit. if err := cm.commit(d.journalFile.Num(), d.seq); err != nil { return err } // Remove the last journal. if of != nil { of.Remove() } return nil }
// Recover a database session; need external synchronization. func (s *session) recover() (err error) { file, err := s.stor.GetManifest() if err != nil { return } reader, err := file.Open() if err != nil { return } defer reader.Close() strict := s.o.GetStrict(opt.StrictManifest) jr := journal.NewReader(reader, dropper{s, file}, strict, true) staging := s.version_NB().newStaging() rec := &sessionRecord{} for { var r io.Reader r, err = jr.Next() if err != nil { if err == io.EOF { err = nil break } return } err = rec.decode(r) if err == nil { // save compact pointers for _, rp := range rec.compactionPointers { s.stCPtrs[rp.level] = iKey(rp.key) } // commit record to version staging staging.commit(rec) } else if strict { return err } else { s.logf("manifest error: %v (skipped)", err) } rec.resetCompactionPointers() rec.resetAddedTables() rec.resetDeletedTables() } switch { case !rec.has(recComparer): return errors.New("leveldb: manifest missing comparer name") case rec.comparer != s.cmp.cmp.Name(): return errors.New("leveldb: comparer mismatch, " + "want '" + s.cmp.cmp.Name() + "', " + "got '" + rec.comparer + "'") case !rec.has(recNextNum): return errors.New("leveldb: manifest missing next file number") case !rec.has(recJournalNum): return errors.New("leveldb: manifest missing journal file number") case !rec.has(recSeq): return errors.New("leveldb: manifest missing seq number") } s.manifestFile = file s.setVersion(staging.finish()) s.setFileNum(rec.nextNum) s.recordCommited(rec) return nil }