github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/syndtr/goleveldb/leveldb/session.go (about) 1 // Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com> 2 // All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style license that can be 5 // found in the LICENSE file. 6 7 package leveldb 8 9 import ( 10 "fmt" 11 "io" 12 "os" 13 "sync" 14 15 "github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/errors" 16 "github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/journal" 17 "github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/opt" 18 "github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/storage" 19 ) 20 21 // ErrManifestCorrupted records manifest corruption. 22 type ErrManifestCorrupted struct { 23 Field string 24 Reason string 25 } 26 27 func (e *ErrManifestCorrupted) Error() string { 28 return fmt.Sprintf("leveldb: manifest corrupted (field '%s'): %s", e.Field, e.Reason) 29 } 30 31 func newErrManifestCorrupted(fd storage.FileDesc, field, reason string) error { 32 return errors.NewErrCorrupted(fd, &ErrManifestCorrupted{field, reason}) 33 } 34 35 // session represent a persistent database session. 36 type session struct { 37 // Need 64-bit alignment. 38 stNextFileNum int64 // current unused file number 39 stJournalNum int64 // current journal file number; need external synchronization 40 stPrevJournalNum int64 // prev journal file number; no longer used; for compatibility with older version of leveldb 41 stTempFileNum int64 42 stSeqNum uint64 // last mem compacted seq; need external synchronization 43 44 stor storage.Storage 45 storLock storage.Lock 46 o *cachedOptions 47 icmp *iComparer 48 tops *tOps 49 50 manifest *journal.Writer 51 manifestWriter storage.Writer 52 manifestFd storage.FileDesc 53 54 stCompPtrs []internalKey // compaction pointers; need external synchronization 55 stVersion *version // current version 56 vmu sync.Mutex 57 } 58 59 // Creates new initialized session instance. 60 func newSession(stor storage.Storage, o *opt.Options) (s *session, err error) { 61 if stor == nil { 62 return nil, os.ErrInvalid 63 } 64 storLock, err := stor.Lock() 65 if err != nil { 66 return 67 } 68 s = &session{ 69 stor: stor, 70 storLock: storLock, 71 } 72 s.setOptions(o) 73 s.tops = newTableOps(s) 74 s.setVersion(newVersion(s)) 75 s.log("log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed") 76 return 77 } 78 79 // Close session. 80 func (s *session) close() { 81 s.tops.close() 82 if s.manifest != nil { 83 s.manifest.Close() 84 } 85 if s.manifestWriter != nil { 86 s.manifestWriter.Close() 87 } 88 s.manifest = nil 89 s.manifestWriter = nil 90 s.stVersion = nil 91 } 92 93 // Release session lock. 94 func (s *session) release() { 95 s.storLock.Release() 96 } 97 98 // Create a new database session; need external synchronization. 99 func (s *session) create() error { 100 // create manifest 101 return s.newManifest(nil, nil) 102 } 103 104 // Recover a database session; need external synchronization. 105 func (s *session) recover() (err error) { 106 defer func() { 107 if os.IsNotExist(err) { 108 // Don't return os.ErrNotExist if the underlying storage contains 109 // other files that belong to LevelDB. So the DB won't get trashed. 110 if fds, _ := s.stor.List(storage.TypeAll); len(fds) > 0 { 111 err = &errors.ErrCorrupted{Fd: storage.FileDesc{Type: storage.TypeManifest}, Err: &errors.ErrMissingFiles{}} 112 } 113 } 114 }() 115 116 fd, err := s.stor.GetMeta() 117 if err != nil { 118 return 119 } 120 121 reader, err := s.stor.Open(fd) 122 if err != nil { 123 return 124 } 125 defer reader.Close() 126 127 var ( 128 // Options. 129 strict = s.o.GetStrict(opt.StrictManifest) 130 131 jr = journal.NewReader(reader, dropper{s, fd}, strict, true) 132 rec = &sessionRecord{} 133 staging = s.stVersion.newStaging() 134 ) 135 for { 136 var r io.Reader 137 r, err = jr.Next() 138 if err != nil { 139 if err == io.EOF { 140 err = nil 141 break 142 } 143 return errors.SetFd(err, fd) 144 } 145 146 err = rec.decode(r) 147 if err == nil { 148 // save compact pointers 149 for _, r := range rec.compPtrs { 150 s.setCompPtr(r.level, internalKey(r.ikey)) 151 } 152 // commit record to version staging 153 staging.commit(rec) 154 } else { 155 err = errors.SetFd(err, fd) 156 if strict || !errors.IsCorrupted(err) { 157 return 158 } 159 s.logf("manifest error: %v (skipped)", errors.SetFd(err, fd)) 160 } 161 rec.resetCompPtrs() 162 rec.resetAddedTables() 163 rec.resetDeletedTables() 164 } 165 166 switch { 167 case !rec.has(recComparer): 168 return newErrManifestCorrupted(fd, "comparer", "missing") 169 case rec.comparer != s.icmp.uName(): 170 return newErrManifestCorrupted(fd, "comparer", fmt.Sprintf("mismatch: want '%s', got '%s'", s.icmp.uName(), rec.comparer)) 171 case !rec.has(recNextFileNum): 172 return newErrManifestCorrupted(fd, "next-file-num", "missing") 173 case !rec.has(recJournalNum): 174 return newErrManifestCorrupted(fd, "journal-file-num", "missing") 175 case !rec.has(recSeqNum): 176 return newErrManifestCorrupted(fd, "seq-num", "missing") 177 } 178 179 s.manifestFd = fd 180 s.setVersion(staging.finish()) 181 s.setNextFileNum(rec.nextFileNum) 182 s.recordCommited(rec) 183 return nil 184 } 185 186 // Commit session; need external synchronization. 187 func (s *session) commit(r *sessionRecord) (err error) { 188 v := s.version() 189 defer v.release() 190 191 // spawn new version based on current version 192 nv := v.spawn(r) 193 194 if s.manifest == nil { 195 // manifest journal writer not yet created, create one 196 err = s.newManifest(r, nv) 197 } else { 198 err = s.flushManifest(r) 199 } 200 201 // finally, apply new version if no error rise 202 if err == nil { 203 s.setVersion(nv) 204 } 205 206 return 207 }