github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/libs/db/memdb/db.go (about)

     1  package memdb
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sync"
     7  
     8  	"github.com/google/btree"
     9  
    10  	lldb "github.com/lazyledger/lazyledger-core/libs/db"
    11  )
    12  
    13  const (
    14  	// The approximate number of items and children per B-tree node. Tuned with benchmarks.
    15  	bTreeDegree = 32
    16  )
    17  
    18  // item is a btree.Item with byte slices as keys and values
    19  type item struct {
    20  	key   []byte
    21  	value []byte
    22  }
    23  
    24  // Less implements btree.Item.
    25  func (i *item) Less(other btree.Item) bool {
    26  	// this considers nil == []byte{}, but that's ok since we handle nil endpoints
    27  	// in iterators specially anyway
    28  	return bytes.Compare(i.key, other.(*item).key) == -1
    29  }
    30  
    31  // newKey creates a new key item.
    32  func newKey(key []byte) *item {
    33  	return &item{key: key}
    34  }
    35  
    36  // newPair creates a new pair item.
    37  func newPair(key, value []byte) *item {
    38  	return &item{key: key, value: value}
    39  }
    40  
    41  // MemDB is an in-memory database backend using a B-tree for storage.
    42  //
    43  // For performance reasons, all given and returned keys and values are pointers to the in-memory
    44  // database, so modifying them will cause the stored values to be modified as well. All DB methods
    45  // already specify that keys and values should be considered read-only, but this is especially
    46  // important with MemDB.
    47  type MemDB struct {
    48  	mtx   sync.RWMutex
    49  	btree *btree.BTree
    50  }
    51  
    52  var _ lldb.DB = (*MemDB)(nil)
    53  
    54  // NewDB creates a new in-memory database.
    55  func NewDB() *MemDB {
    56  	database := &MemDB{
    57  		btree: btree.New(bTreeDegree),
    58  	}
    59  	return database
    60  }
    61  
    62  // Get implements DB.
    63  func (db *MemDB) Get(key []byte) ([]byte, error) {
    64  	if len(key) == 0 {
    65  		return nil, lldb.ErrKeyEmpty
    66  	}
    67  	db.mtx.RLock()
    68  	defer db.mtx.RUnlock()
    69  
    70  	i := db.btree.Get(newKey(key))
    71  	if i != nil {
    72  		return i.(*item).value, nil
    73  	}
    74  	return nil, nil
    75  }
    76  
    77  // Has implements DB.
    78  func (db *MemDB) Has(key []byte) (bool, error) {
    79  	if len(key) == 0 {
    80  		return false, lldb.ErrKeyEmpty
    81  	}
    82  	db.mtx.RLock()
    83  	defer db.mtx.RUnlock()
    84  
    85  	return db.btree.Has(newKey(key)), nil
    86  }
    87  
    88  // Set implements DB.
    89  func (db *MemDB) Set(key []byte, value []byte) error {
    90  	if len(key) == 0 {
    91  		return lldb.ErrKeyEmpty
    92  	}
    93  	if value == nil {
    94  		return lldb.ErrValueNil
    95  	}
    96  	db.mtx.Lock()
    97  	defer db.mtx.Unlock()
    98  
    99  	db.set(key, value)
   100  	return nil
   101  }
   102  
   103  // set sets a value without locking the mutex.
   104  func (db *MemDB) set(key []byte, value []byte) {
   105  	db.btree.ReplaceOrInsert(newPair(key, value))
   106  }
   107  
   108  // SetSync implements DB.
   109  func (db *MemDB) SetSync(key []byte, value []byte) error {
   110  	return db.Set(key, value)
   111  }
   112  
   113  // Delete implements DB.
   114  func (db *MemDB) Delete(key []byte) error {
   115  	if len(key) == 0 {
   116  		return lldb.ErrKeyEmpty
   117  	}
   118  	db.mtx.Lock()
   119  	defer db.mtx.Unlock()
   120  
   121  	db.delete(key)
   122  	return nil
   123  }
   124  
   125  // delete deletes a key without locking the mutex.
   126  func (db *MemDB) delete(key []byte) {
   127  	db.btree.Delete(newKey(key))
   128  }
   129  
   130  // DeleteSync implements DB.
   131  func (db *MemDB) DeleteSync(key []byte) error {
   132  	return db.Delete(key)
   133  }
   134  
   135  // Close implements DB.
   136  func (db *MemDB) Close() error {
   137  	// Close is a noop since for an in-memory database, we don't have a destination to flush
   138  	// contents to nor do we want any data loss on invoking Close().
   139  	// See the discussion in https://github.com/tendermint/tendermint/libs/pull/56
   140  	return nil
   141  }
   142  
   143  // Print implements DB.
   144  func (db *MemDB) Print() error {
   145  	db.mtx.RLock()
   146  	defer db.mtx.RUnlock()
   147  
   148  	db.btree.Ascend(func(i btree.Item) bool {
   149  		item := i.(*item)
   150  		fmt.Printf("[%X]:\t[%X]\n", item.key, item.value)
   151  		return true
   152  	})
   153  	return nil
   154  }
   155  
   156  // Stats implements DB.
   157  func (db *MemDB) Stats() map[string]string {
   158  	db.mtx.RLock()
   159  	defer db.mtx.RUnlock()
   160  
   161  	stats := make(map[string]string)
   162  	stats["database.type"] = "memDB"
   163  	stats["database.size"] = fmt.Sprintf("%d", db.btree.Len())
   164  	return stats
   165  }
   166  
   167  // NewBatch implements DB.
   168  func (db *MemDB) NewBatch() lldb.Batch {
   169  	return newMemDBBatch(db)
   170  }
   171  
   172  // Iterator implements DB.
   173  // Takes out a read-lock on the database until the iterator is closed.
   174  func (db *MemDB) Iterator(start, end []byte) (lldb.Iterator, error) {
   175  	if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) {
   176  		return nil, lldb.ErrKeyEmpty
   177  	}
   178  	return newMemDBIterator(db, start, end, false), nil
   179  }
   180  
   181  // ReverseIterator implements DB.
   182  // Takes out a read-lock on the database until the iterator is closed.
   183  func (db *MemDB) ReverseIterator(start, end []byte) (lldb.Iterator, error) {
   184  	if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) {
   185  		return nil, lldb.ErrKeyEmpty
   186  	}
   187  	return newMemDBIterator(db, start, end, true), nil
   188  }