// newDbCache returns a new database cache instance backed by the provided // leveldb instance. The cache will be flushed to leveldb when the max size // exceeds the provided value or it has been longer than the provided interval // since the last flush. func newDbCache(ldb *leveldb.DB, store *blockStore, maxSize uint64, flushIntervalSecs uint32) *dbCache { return &dbCache{ ldb: ldb, store: store, maxSize: maxSize, flushInterval: time.Second * time.Duration(flushIntervalSecs), lastFlush: time.Now(), cachedKeys: treap.NewImmutable(), cachedRemove: treap.NewImmutable(), } }
// flush flushes the database cache to persistent storage. This involes syncing // the block store and replaying all transactions that have been applied to the // cache to the underlying database. // // This function MUST be called with the database write lock held. func (c *dbCache) flush() error { c.lastFlush = time.Now() // Sync the current write file associated with the block store. This is // necessary before writing the metadata to prevent the case where the // metadata contains information about a block which actually hasn't // been written yet in unexpected shutdown scenarios. if err := c.store.syncBlocks(); err != nil { return err } // Nothing to do if there are no transactions to flush. if len(c.txLog) == 0 { return nil } // Perform all leveldb updates using batches for atomicity. batchLen := 0 batchTxns := 0 batch := new(leveldb.Batch) for logTxNum, txLogEntries := range c.txLog { // Replay the transaction from the log into the current batch. for _, logEntry := range txLogEntries { switch logEntry.entryType { case entryTypeUpdate: batch.Put(logEntry.key, logEntry.value) case entryTypeRemove: batch.Delete(logEntry.key) } } batchTxns++ // Write and reset the current batch when the number of items in // it exceeds the the batch threshold or this is the last // transaction in the log. batchLen += len(txLogEntries) if batchLen > batchThreshold || logTxNum == len(c.txLog)-1 { if err := c.ldb.Write(batch, nil); err != nil { return convertErr("failed to write batch", err) } batch.Reset() batchLen = 0 // Clear the transactions that were written from the // log so the memory can be reclaimed. for i := logTxNum - (batchTxns - 1); i <= logTxNum; i++ { c.txLog[i] = nil } batchTxns = 0 } } c.txLog = c.txLog[:] // Clear the cache since it has been flushed. c.cacheLock.Lock() c.cachedKeys = treap.NewImmutable() c.cachedRemove = treap.NewImmutable() c.cacheLock.Unlock() return nil }