func (db *DB) recoverJournal() error { // Get all journals and sort it by file number. allJournalFiles, err := db.s.getFiles(storage.TypeJournal) if err != nil { return err } files(allJournalFiles).sort() // Journals that will be recovered. var recJournalFiles []storage.File for _, jf := range allJournalFiles { if jf.Num() >= db.s.stJournalNum || jf.Num() == db.s.stPrevJournalNum { recJournalFiles = append(recJournalFiles, jf) } } var ( of storage.File // Obsolete file. rec = &sessionRecord{} ) // Recover journals. if len(recJournalFiles) > 0 { db.logf("journal@recovery F·%d", len(recJournalFiles)) // Mark file number as used. db.s.markFileNum(recJournalFiles[len(recJournalFiles)-1].Num()) var ( // Options. strict = db.s.o.GetStrict(opt.StrictJournal) checksum = db.s.o.GetStrict(opt.StrictJournalChecksum) writeBuffer = db.s.o.GetWriteBuffer() jr *journal.Reader mdb = memdb.New(db.s.icmp, writeBuffer) buf = &util.Buffer{} batch = &Batch{} ) for _, jf := range recJournalFiles { db.logf("journal@recovery recovering @%d", jf.Num()) fr, err := jf.Open() if err != nil { return err } // Create or reset journal reader instance. if jr == nil { jr = journal.NewReader(fr, dropper{db.s, jf}, strict, checksum) } else { jr.Reset(fr, dropper{db.s, jf}, strict, checksum) } // Flush memdb and remove obsolete journal file. if of != nil { if mdb.Len() > 0 { if _, err := db.s.flushMemdb(rec, mdb, -1); err != nil { fr.Close() return err } } rec.setJournalNum(jf.Num()) rec.setSeqNum(db.seq) if err := db.s.commit(rec); err != nil { fr.Close() return err } rec.resetAddedTables() of.Remove() of = nil } // Replay journal to memdb. mdb.Reset() for { r, err := jr.Next() if err != nil { if err == io.EOF { break } fr.Close() return errors.SetFile(err, jf) } buf.Reset() if _, err := buf.ReadFrom(r); err != nil { if err == io.ErrUnexpectedEOF { // This is error returned due to corruption, with strict == false. continue } fr.Close() return errors.SetFile(err, jf) } if err := batch.memDecodeAndReplay(db.seq, buf.Bytes(), mdb); err != nil { if !strict && errors.IsCorrupted(err) { db.s.logf("journal error: %v (skipped)", err) // We won't apply sequence number as it might be corrupted. continue } fr.Close() return errors.SetFile(err, jf) } // Save sequence number. db.seq = batch.seq + uint64(batch.Len()) // Flush it if large enough. if mdb.Size() >= writeBuffer { if _, err := db.s.flushMemdb(rec, mdb, 0); err != nil { fr.Close() return err } mdb.Reset() } } fr.Close() of = jf } // Flush the last memdb. if mdb.Len() > 0 { if _, err := db.s.flushMemdb(rec, mdb, 0); err != nil { return err } } } // Create a new journal. if _, err := db.newMem(0); err != nil { return err } // Commit. rec.setJournalNum(db.journalFile.Num()) rec.setSeqNum(db.seq) if err := db.s.commit(rec); err != nil { // Close journal on error. if db.journal != nil { db.journal.Close() db.journalWriter.Close() } return err } // Remove the last obsolete journal file. if of != nil { of.Remove() } return nil }
func (db *DB) recoverJournalRO() error { // Get all journals and sort it by file number. allJournalFiles, err := db.s.getFiles(storage.TypeJournal) if err != nil { return err } files(allJournalFiles).sort() // Journals that will be recovered. var recJournalFiles []storage.File for _, jf := range allJournalFiles { if jf.Num() >= db.s.stJournalNum || jf.Num() == db.s.stPrevJournalNum { recJournalFiles = append(recJournalFiles, jf) } } var ( // Options. strict = db.s.o.GetStrict(opt.StrictJournal) checksum = db.s.o.GetStrict(opt.StrictJournalChecksum) writeBuffer = db.s.o.GetWriteBuffer() mdb = memdb.New(db.s.icmp, writeBuffer) ) // Recover journals. if len(recJournalFiles) > 0 { db.logf("journal@recovery RO·Mode F·%d", len(recJournalFiles)) var ( jr *journal.Reader buf = &util.Buffer{} batch = &Batch{} ) for _, jf := range recJournalFiles { db.logf("journal@recovery recovering @%d", jf.Num()) fr, err := jf.Open() if err != nil { return err } // Create or reset journal reader instance. if jr == nil { jr = journal.NewReader(fr, dropper{db.s, jf}, strict, checksum) } else { jr.Reset(fr, dropper{db.s, jf}, strict, checksum) } // Replay journal to memdb. for { r, err := jr.Next() if err != nil { if err == io.EOF { break } fr.Close() return errors.SetFile(err, jf) } buf.Reset() if _, err := buf.ReadFrom(r); err != nil { if err == io.ErrUnexpectedEOF { // This is error returned due to corruption, with strict == false. continue } fr.Close() return errors.SetFile(err, jf) } if err := batch.memDecodeAndReplay(db.seq, buf.Bytes(), mdb); err != nil { if !strict && errors.IsCorrupted(err) { db.s.logf("journal error: %v (skipped)", err) // We won't apply sequence number as it might be corrupted. continue } fr.Close() return errors.SetFile(err, jf) } // Save sequence number. db.seq = batch.seq + uint64(batch.Len()) } fr.Close() } } // Set memDB. db.mem = &memDB{db: db, DB: mdb, ref: 1} return nil }
// Recover a database session; need external synchronization. func (s *session) recover() (err error) { defer func() { if os.IsNotExist(err) { // Don't return os.ErrNotExist if the underlying storage contains // other files that belong to LevelDB. So the DB won't get trashed. if files, _ := s.stor.GetFiles(storage.TypeAll); len(files) > 0 { err = &errors.ErrCorrupted{File: &storage.FileInfo{Type: storage.TypeManifest}, Err: &errors.ErrMissingFiles{}} } } }() m, err := s.stor.GetManifest() if err != nil { return } reader, err := m.Open() if err != nil { return } defer reader.Close() var ( // Options. numLevel = s.o.GetNumLevel() strict = s.o.GetStrict(opt.StrictManifest) jr = journal.NewReader(reader, dropper{s, m}, strict, true) rec = &sessionRecord{} staging = s.stVersion.newStaging() ) for { var r io.Reader r, err = jr.Next() if err != nil { if err == io.EOF { err = nil break } return errors.SetFile(err, m) } err = rec.decode(r, numLevel) if err == nil { // save compact pointers for _, r := range rec.compPtrs { s.stCompPtrs[r.level] = iKey(r.ikey) } // commit record to version staging staging.commit(rec) } else { err = errors.SetFile(err, m) if strict || !errors.IsCorrupted(err) { return } else { s.logf("manifest error: %v (skipped)", errors.SetFile(err, m)) } } rec.resetCompPtrs() rec.resetAddedTables() rec.resetDeletedTables() } switch { case !rec.has(recComparer): return newErrManifestCorrupted(m, "comparer", "missing") case rec.comparer != s.icmp.uName(): return newErrManifestCorrupted(m, "comparer", fmt.Sprintf("mismatch: want '%s', got '%s'", s.icmp.uName(), rec.comparer)) case !rec.has(recNextFileNum): return newErrManifestCorrupted(m, "next-file-num", "missing") case !rec.has(recJournalNum): return newErrManifestCorrupted(m, "journal-file-num", "missing") case !rec.has(recSeqNum): return newErrManifestCorrupted(m, "seq-num", "missing") } s.manifestFile = m s.setVersion(staging.finish()) s.setNextFileNum(rec.nextFileNum) s.recordCommited(rec) return nil }