github.com/df-mc/goleveldb@v1.1.9/leveldb/db_state.go (about) 1 // Copyright (c) 2013, 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 "errors" 11 "sync/atomic" 12 "time" 13 14 "github.com/df-mc/goleveldb/leveldb/journal" 15 "github.com/df-mc/goleveldb/leveldb/memdb" 16 "github.com/df-mc/goleveldb/leveldb/storage" 17 ) 18 19 var ( 20 errHasFrozenMem = errors.New("has frozen mem") 21 ) 22 23 type memDB struct { 24 db *DB 25 *memdb.DB 26 ref int32 27 } 28 29 func (m *memDB) getref() int32 { 30 return atomic.LoadInt32(&m.ref) 31 } 32 33 func (m *memDB) incref() { 34 atomic.AddInt32(&m.ref, 1) 35 } 36 37 func (m *memDB) decref() { 38 if ref := atomic.AddInt32(&m.ref, -1); ref == 0 { 39 // Only put back memdb with std capacity. 40 if m.Capacity() == m.db.s.o.GetWriteBuffer() { 41 m.Reset() 42 m.db.mpoolPut(m.DB) 43 } 44 m.db = nil 45 m.DB = nil 46 } else if ref < 0 { 47 panic("negative memdb ref") 48 } 49 } 50 51 // Get latest sequence number. 52 func (db *DB) getSeq() uint64 { 53 return atomic.LoadUint64(&db.seq) 54 } 55 56 // Atomically adds delta to seq. 57 func (db *DB) addSeq(delta uint64) { 58 atomic.AddUint64(&db.seq, delta) 59 } 60 61 func (db *DB) setSeq(seq uint64) { 62 atomic.StoreUint64(&db.seq, seq) 63 } 64 65 func (db *DB) sampleSeek(ikey internalKey) { 66 v := db.s.version() 67 if v.sampleSeek(ikey) { 68 // Trigger table compaction. 69 db.compTrigger(db.tcompCmdC) 70 } 71 v.release() 72 } 73 74 func (db *DB) mpoolPut(mem *memdb.DB) { 75 if !db.isClosed() { 76 select { 77 case db.memPool <- mem: 78 default: 79 } 80 } 81 } 82 83 func (db *DB) mpoolGet(n int) *memDB { 84 var mdb *memdb.DB 85 select { 86 case mdb = <-db.memPool: 87 default: 88 } 89 if mdb == nil || mdb.Capacity() < n { 90 mdb = memdb.New(db.s.icmp, maxInt(db.s.o.GetWriteBuffer(), n)) 91 } 92 return &memDB{ 93 db: db, 94 DB: mdb, 95 } 96 } 97 98 func (db *DB) mpoolDrain() { 99 ticker := time.NewTicker(30 * time.Second) 100 for { 101 select { 102 case <-ticker.C: 103 select { 104 case <-db.memPool: 105 default: 106 } 107 case <-db.closeC: 108 ticker.Stop() 109 // Make sure the pool is drained. 110 select { 111 case <-db.memPool: 112 case <-time.After(time.Second): 113 } 114 close(db.memPool) 115 return 116 } 117 } 118 } 119 120 // Create new memdb and froze the old one; need external synchronization. 121 // newMem only called synchronously by the writer. 122 func (db *DB) newMem(n int) (mem *memDB, err error) { 123 fd := storage.FileDesc{Type: storage.TypeJournal, Num: db.s.allocFileNum()} 124 w, err := db.s.stor.Create(fd) 125 if err != nil { 126 db.s.reuseFileNum(fd.Num) 127 return 128 } 129 130 db.memMu.Lock() 131 defer db.memMu.Unlock() 132 133 if db.frozenMem != nil { 134 return nil, errHasFrozenMem 135 } 136 137 if db.journal == nil { 138 db.journal = journal.NewWriter(w) 139 } else { 140 db.journal.Reset(w) 141 db.journalWriter.Close() 142 db.frozenJournalFd = db.journalFd 143 } 144 db.journalWriter = w 145 db.journalFd = fd 146 db.frozenMem = db.mem 147 mem = db.mpoolGet(n) 148 mem.incref() // for self 149 mem.incref() // for caller 150 db.mem = mem 151 // The seq only incremented by the writer. And whoever called newMem 152 // should hold write lock, so no need additional synchronization here. 153 db.frozenSeq = db.seq 154 return 155 } 156 157 // Get all memdbs. 158 func (db *DB) getMems() (e, f *memDB) { 159 db.memMu.RLock() 160 defer db.memMu.RUnlock() 161 if db.mem != nil { 162 db.mem.incref() 163 } else if !db.isClosed() { 164 panic("nil effective mem") 165 } 166 if db.frozenMem != nil { 167 db.frozenMem.incref() 168 } 169 return db.mem, db.frozenMem 170 } 171 172 // Get effective memdb. 173 func (db *DB) getEffectiveMem() *memDB { 174 db.memMu.RLock() 175 defer db.memMu.RUnlock() 176 if db.mem != nil { 177 db.mem.incref() 178 } else if !db.isClosed() { 179 panic("nil effective mem") 180 } 181 return db.mem 182 } 183 184 // Check whether we has frozen memdb. 185 func (db *DB) hasFrozenMem() bool { 186 db.memMu.RLock() 187 defer db.memMu.RUnlock() 188 return db.frozenMem != nil 189 } 190 191 // Get frozen memdb. 192 func (db *DB) getFrozenMem() *memDB { 193 db.memMu.RLock() 194 defer db.memMu.RUnlock() 195 if db.frozenMem != nil { 196 db.frozenMem.incref() 197 } 198 return db.frozenMem 199 } 200 201 // Drop frozen memdb; assume that frozen memdb isn't nil. 202 func (db *DB) dropFrozenMem() { 203 db.memMu.Lock() 204 if err := db.s.stor.Remove(db.frozenJournalFd); err != nil { 205 db.logf("journal@remove removing @%d %q", db.frozenJournalFd.Num, err) 206 } else { 207 db.logf("journal@remove removed @%d", db.frozenJournalFd.Num) 208 } 209 db.frozenJournalFd = storage.FileDesc{} 210 db.frozenMem.decref() 211 db.frozenMem = nil 212 db.memMu.Unlock() 213 } 214 215 // Clear mems ptr; used by DB.Close(). 216 func (db *DB) clearMems() { 217 db.memMu.Lock() 218 db.mem = nil 219 db.frozenMem = nil 220 db.memMu.Unlock() 221 } 222 223 // Set closed flag; return true if not already closed. 224 func (db *DB) setClosed() bool { 225 return atomic.CompareAndSwapUint32(&db.closed, 0, 1) 226 } 227 228 // Check whether DB was closed. 229 func (db *DB) isClosed() bool { 230 return atomic.LoadUint32(&db.closed) != 0 231 } 232 233 // Check read ok status. 234 func (db *DB) ok() error { 235 if db.isClosed() { 236 return ErrClosed 237 } 238 return nil 239 }