github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/syndtr/goleveldb/leveldb/session_util.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 "sync/atomic" 12 13 "github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/journal" 14 "github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/storage" 15 ) 16 17 // Logging. 18 19 type dropper struct { 20 s *session 21 fd storage.FileDesc 22 } 23 24 func (d dropper) Drop(err error) { 25 if e, ok := err.(*journal.ErrCorrupted); ok { 26 d.s.logf("journal@drop %s-%d S·%s %q", d.fd.Type, d.fd.Num, shortenb(e.Size), e.Reason) 27 } else { 28 d.s.logf("journal@drop %s-%d %q", d.fd.Type, d.fd.Num, err) 29 } 30 } 31 32 func (s *session) log(v ...interface{}) { s.stor.Log(fmt.Sprint(v...)) } 33 func (s *session) logf(format string, v ...interface{}) { s.stor.Log(fmt.Sprintf(format, v...)) } 34 35 // File utils. 36 37 func (s *session) newTemp() storage.FileDesc { 38 num := atomic.AddInt64(&s.stTempFileNum, 1) - 1 39 return storage.FileDesc{storage.TypeTemp, num} 40 } 41 42 // Session state. 43 44 // Get current version. This will incr version ref, must call 45 // version.release (exactly once) after use. 46 func (s *session) version() *version { 47 s.vmu.Lock() 48 defer s.vmu.Unlock() 49 s.stVersion.ref++ 50 return s.stVersion 51 } 52 53 // Set current version to v. 54 func (s *session) setVersion(v *version) { 55 s.vmu.Lock() 56 v.ref = 1 // Holds by session. 57 if old := s.stVersion; old != nil { 58 v.ref++ // Holds by old version. 59 old.next = v 60 old.releaseNB() 61 } 62 s.stVersion = v 63 s.vmu.Unlock() 64 } 65 66 // Get current unused file number. 67 func (s *session) nextFileNum() int64 { 68 return atomic.LoadInt64(&s.stNextFileNum) 69 } 70 71 // Set current unused file number to num. 72 func (s *session) setNextFileNum(num int64) { 73 atomic.StoreInt64(&s.stNextFileNum, num) 74 } 75 76 // Mark file number as used. 77 func (s *session) markFileNum(num int64) { 78 nextFileNum := num + 1 79 for { 80 old, x := s.stNextFileNum, nextFileNum 81 if old > x { 82 x = old 83 } 84 if atomic.CompareAndSwapInt64(&s.stNextFileNum, old, x) { 85 break 86 } 87 } 88 } 89 90 // Allocate a file number. 91 func (s *session) allocFileNum() int64 { 92 return atomic.AddInt64(&s.stNextFileNum, 1) - 1 93 } 94 95 // Reuse given file number. 96 func (s *session) reuseFileNum(num int64) { 97 for { 98 old, x := s.stNextFileNum, num 99 if old != x+1 { 100 x = old 101 } 102 if atomic.CompareAndSwapInt64(&s.stNextFileNum, old, x) { 103 break 104 } 105 } 106 } 107 108 // Set compaction ptr at given level; need external synchronization. 109 func (s *session) setCompPtr(level int, ik internalKey) { 110 if level >= len(s.stCompPtrs) { 111 newCompPtrs := make([]internalKey, level+1) 112 copy(newCompPtrs, s.stCompPtrs) 113 s.stCompPtrs = newCompPtrs 114 } 115 s.stCompPtrs[level] = append(internalKey{}, ik...) 116 } 117 118 // Get compaction ptr at given level; need external synchronization. 119 func (s *session) getCompPtr(level int) internalKey { 120 if level >= len(s.stCompPtrs) { 121 return nil 122 } 123 return s.stCompPtrs[level] 124 } 125 126 // Manifest related utils. 127 128 // Fill given session record obj with current states; need external 129 // synchronization. 130 func (s *session) fillRecord(r *sessionRecord, snapshot bool) { 131 r.setNextFileNum(s.nextFileNum()) 132 133 if snapshot { 134 if !r.has(recJournalNum) { 135 r.setJournalNum(s.stJournalNum) 136 } 137 138 if !r.has(recSeqNum) { 139 r.setSeqNum(s.stSeqNum) 140 } 141 142 for level, ik := range s.stCompPtrs { 143 if ik != nil { 144 r.addCompPtr(level, ik) 145 } 146 } 147 148 r.setComparer(s.icmp.uName()) 149 } 150 } 151 152 // Mark if record has been committed, this will update session state; 153 // need external synchronization. 154 func (s *session) recordCommited(rec *sessionRecord) { 155 if rec.has(recJournalNum) { 156 s.stJournalNum = rec.journalNum 157 } 158 159 if rec.has(recPrevJournalNum) { 160 s.stPrevJournalNum = rec.prevJournalNum 161 } 162 163 if rec.has(recSeqNum) { 164 s.stSeqNum = rec.seqNum 165 } 166 167 for _, r := range rec.compPtrs { 168 s.setCompPtr(r.level, internalKey(r.ikey)) 169 } 170 } 171 172 // Create a new manifest file; need external synchronization. 173 func (s *session) newManifest(rec *sessionRecord, v *version) (err error) { 174 fd := storage.FileDesc{storage.TypeManifest, s.allocFileNum()} 175 writer, err := s.stor.Create(fd) 176 if err != nil { 177 return 178 } 179 jw := journal.NewWriter(writer) 180 181 if v == nil { 182 v = s.version() 183 defer v.release() 184 } 185 if rec == nil { 186 rec = &sessionRecord{} 187 } 188 s.fillRecord(rec, true) 189 v.fillRecord(rec) 190 191 defer func() { 192 if err == nil { 193 s.recordCommited(rec) 194 if s.manifest != nil { 195 s.manifest.Close() 196 } 197 if s.manifestWriter != nil { 198 s.manifestWriter.Close() 199 } 200 if !s.manifestFd.Nil() { 201 s.stor.Remove(s.manifestFd) 202 } 203 s.manifestFd = fd 204 s.manifestWriter = writer 205 s.manifest = jw 206 } else { 207 writer.Close() 208 s.stor.Remove(fd) 209 s.reuseFileNum(fd.Num) 210 } 211 }() 212 213 w, err := jw.Next() 214 if err != nil { 215 return 216 } 217 err = rec.encode(w) 218 if err != nil { 219 return 220 } 221 err = jw.Flush() 222 if err != nil { 223 return 224 } 225 err = s.stor.SetMeta(fd) 226 return 227 } 228 229 // Flush record to disk. 230 func (s *session) flushManifest(rec *sessionRecord) (err error) { 231 s.fillRecord(rec, false) 232 w, err := s.manifest.Next() 233 if err != nil { 234 return 235 } 236 err = rec.encode(w) 237 if err != nil { 238 return 239 } 240 err = s.manifest.Flush() 241 if err != nil { 242 return 243 } 244 if !s.o.GetNoSync() { 245 err = s.manifestWriter.Sync() 246 if err != nil { 247 return 248 } 249 } 250 s.recordCommited(rec) 251 return 252 }