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 }