1 // Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
2 // All rights reserved.
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE file.
15 "github.com/syndtr/goleveldb/leveldb/errors"
16 "github.com/syndtr/goleveldb/leveldb/journal"
17 "github.com/syndtr/goleveldb/leveldb/opt"
18 "github.com/syndtr/goleveldb/leveldb/storage"
21 // ErrManifestCorrupted records manifest corruption. This error will be
22 // wrapped with errors.ErrCorrupted.
23 type ErrManifestCorrupted struct {
28 func (e *ErrManifestCorrupted) Error() string {
29 return fmt.Sprintf("leveldb: manifest corrupted (field '%s'): %s", e.Field, e.Reason)
32 func newErrManifestCorrupted(fd storage.FileDesc, field, reason string) error {
33 return errors.NewErrCorrupted(fd, &ErrManifestCorrupted{field, reason})
36 // session represent a persistent database session.
38 // Need 64-bit alignment.
39 stNextFileNum int64 // current unused file number
40 stJournalNum int64 // current journal file number; need external synchronization
41 stPrevJournalNum int64 // prev journal file number; no longer used; for compatibility with older version of leveldb
43 stSeqNum uint64 // last mem compacted seq; need external synchronization
46 storLock storage.Locker
52 manifest *journal.Writer
53 manifestWriter storage.Writer
54 manifestFd storage.FileDesc
56 stCompPtrs []internalKey // compaction pointers; need external synchronization
57 stVersion *version // current version
61 // Creates new initialized session instance.
62 func newSession(stor storage.Storage, o *opt.Options) (s *session, err error) {
64 return nil, os.ErrInvalid
66 storLock, err := stor.Lock()
73 fileRef: make(map[int64]int),
76 s.tops = newTableOps(s)
77 s.setVersion(newVersion(s))
78 s.log("log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed")
83 func (s *session) close() {
85 if s.manifest != nil {
88 if s.manifestWriter != nil {
89 s.manifestWriter.Close()
92 s.manifestWriter = nil
93 s.setVersion(&version{s: s, closing: true})
96 // Release session lock.
97 func (s *session) release() {
101 // Create a new database session; need external synchronization.
102 func (s *session) create() error {
104 return s.newManifest(nil, nil)
107 // Recover a database session; need external synchronization.
108 func (s *session) recover() (err error) {
110 if os.IsNotExist(err) {
111 // Don't return os.ErrNotExist if the underlying storage contains
112 // other files that belong to LevelDB. So the DB won't get trashed.
113 if fds, _ := s.stor.List(storage.TypeAll); len(fds) > 0 {
114 err = &errors.ErrCorrupted{Fd: storage.FileDesc{Type: storage.TypeManifest}, Err: &errors.ErrMissingFiles{}}
119 fd, err := s.stor.GetMeta()
124 reader, err := s.stor.Open(fd)
132 strict = s.o.GetStrict(opt.StrictManifest)
134 jr = journal.NewReader(reader, dropper{s, fd}, strict, true)
135 rec = &sessionRecord{}
136 staging = s.stVersion.newStaging()
146 return errors.SetFd(err, fd)
151 // save compact pointers
152 for _, r := range rec.compPtrs {
153 s.setCompPtr(r.level, internalKey(r.ikey))
155 // commit record to version staging
158 err = errors.SetFd(err, fd)
159 if strict || !errors.IsCorrupted(err) {
162 s.logf("manifest error: %v (skipped)", errors.SetFd(err, fd))
165 rec.resetAddedTables()
166 rec.resetDeletedTables()
170 case !rec.has(recComparer):
171 return newErrManifestCorrupted(fd, "comparer", "missing")
172 case rec.comparer != s.icmp.uName():
173 return newErrManifestCorrupted(fd, "comparer", fmt.Sprintf("mismatch: want '%s', got '%s'", s.icmp.uName(), rec.comparer))
174 case !rec.has(recNextFileNum):
175 return newErrManifestCorrupted(fd, "next-file-num", "missing")
176 case !rec.has(recJournalNum):
177 return newErrManifestCorrupted(fd, "journal-file-num", "missing")
178 case !rec.has(recSeqNum):
179 return newErrManifestCorrupted(fd, "seq-num", "missing")
183 s.setVersion(staging.finish())
184 s.setNextFileNum(rec.nextFileNum)
185 s.recordCommited(rec)
189 // Commit session; need external synchronization.
190 func (s *session) commit(r *sessionRecord) (err error) {
194 // spawn new version based on current version
197 if s.manifest == nil {
198 // manifest journal writer not yet created, create one
199 err = s.newManifest(r, nv)
201 err = s.flushManifest(r)
204 // finally, apply new version if no error rise