|
|
// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package leveldb
import ( "errors" "sync/atomic" "time"
"github.com/syndtr/goleveldb/leveldb/journal" "github.com/syndtr/goleveldb/leveldb/memdb" "github.com/syndtr/goleveldb/leveldb/storage" )
var ( errHasFrozenMem = errors.New("has frozen mem") )
type memDB struct { db *DB *memdb.DB ref int32 }
func (m *memDB) getref() int32 { return atomic.LoadInt32(&m.ref) }
func (m *memDB) incref() { atomic.AddInt32(&m.ref, 1) }
func (m *memDB) decref() { if ref := atomic.AddInt32(&m.ref, -1); ref == 0 { // Only put back memdb with std capacity.
if m.Capacity() == m.db.s.o.GetWriteBuffer() { m.Reset() m.db.mpoolPut(m.DB) } m.db = nil m.DB = nil } else if ref < 0 { panic("negative memdb ref") } }
// Get latest sequence number.
func (db *DB) getSeq() uint64 { return atomic.LoadUint64(&db.seq) }
// Atomically adds delta to seq.
func (db *DB) addSeq(delta uint64) { atomic.AddUint64(&db.seq, delta) }
func (db *DB) setSeq(seq uint64) { atomic.StoreUint64(&db.seq, seq) }
func (db *DB) sampleSeek(ikey internalKey) { v := db.s.version() if v.sampleSeek(ikey) { // Trigger table compaction.
db.compTrigger(db.tcompCmdC) } v.release() }
func (db *DB) mpoolPut(mem *memdb.DB) { if !db.isClosed() { select { case db.memPool <- mem: default: } } }
func (db *DB) mpoolGet(n int) *memDB { var mdb *memdb.DB select { case mdb = <-db.memPool: default: } if mdb == nil || mdb.Capacity() < n { mdb = memdb.New(db.s.icmp, maxInt(db.s.o.GetWriteBuffer(), n)) } return &memDB{ db: db, DB: mdb, } }
func (db *DB) mpoolDrain() { ticker := time.NewTicker(30 * time.Second) for { select { case <-ticker.C: select { case <-db.memPool: default: } case <-db.closeC: ticker.Stop() // Make sure the pool is drained.
select { case <-db.memPool: case <-time.After(time.Second): } close(db.memPool) return } } }
// Create new memdb and froze the old one; need external synchronization.
// newMem only called synchronously by the writer.
func (db *DB) newMem(n int) (mem *memDB, err error) { fd := storage.FileDesc{Type: storage.TypeJournal, Num: db.s.allocFileNum()} w, err := db.s.stor.Create(fd) if err != nil { db.s.reuseFileNum(fd.Num) return }
db.memMu.Lock() defer db.memMu.Unlock()
if db.frozenMem != nil { return nil, errHasFrozenMem }
if db.journal == nil { db.journal = journal.NewWriter(w) } else { db.journal.Reset(w) db.journalWriter.Close() db.frozenJournalFd = db.journalFd } db.journalWriter = w db.journalFd = fd db.frozenMem = db.mem mem = db.mpoolGet(n) mem.incref() // for self
mem.incref() // for caller
db.mem = mem // The seq only incremented by the writer. And whoever called newMem
// should hold write lock, so no need additional synchronization here.
db.frozenSeq = db.seq return }
// Get all memdbs.
func (db *DB) getMems() (e, f *memDB) { db.memMu.RLock() defer db.memMu.RUnlock() if db.mem != nil { db.mem.incref() } else if !db.isClosed() { panic("nil effective mem") } if db.frozenMem != nil { db.frozenMem.incref() } return db.mem, db.frozenMem }
// Get effective memdb.
func (db *DB) getEffectiveMem() *memDB { db.memMu.RLock() defer db.memMu.RUnlock() if db.mem != nil { db.mem.incref() } else if !db.isClosed() { panic("nil effective mem") } return db.mem }
// Check whether we has frozen memdb.
func (db *DB) hasFrozenMem() bool { db.memMu.RLock() defer db.memMu.RUnlock() return db.frozenMem != nil }
// Get frozen memdb.
func (db *DB) getFrozenMem() *memDB { db.memMu.RLock() defer db.memMu.RUnlock() if db.frozenMem != nil { db.frozenMem.incref() } return db.frozenMem }
// Drop frozen memdb; assume that frozen memdb isn't nil.
func (db *DB) dropFrozenMem() { db.memMu.Lock() if err := db.s.stor.Remove(db.frozenJournalFd); err != nil { db.logf("journal@remove removing @%d %q", db.frozenJournalFd.Num, err) } else { db.logf("journal@remove removed @%d", db.frozenJournalFd.Num) } db.frozenJournalFd = storage.FileDesc{} db.frozenMem.decref() db.frozenMem = nil db.memMu.Unlock() }
// Clear mems ptr; used by DB.Close().
func (db *DB) clearMems() { db.memMu.Lock() db.mem = nil db.frozenMem = nil db.memMu.Unlock() }
// Set closed flag; return true if not already closed.
func (db *DB) setClosed() bool { return atomic.CompareAndSwapUint32(&db.closed, 0, 1) }
// Check whether DB was closed.
func (db *DB) isClosed() bool { return atomic.LoadUint32(&db.closed) != 0 }
// Check read ok status.
func (db *DB) ok() error { if db.isClosed() { return ErrClosed } return nil }
|