github.com/MetalBlockchain/metalgo@v1.11.9/database/linkeddb/linkeddb.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package linkeddb 5 6 import ( 7 "slices" 8 "sync" 9 10 "github.com/MetalBlockchain/metalgo/cache" 11 "github.com/MetalBlockchain/metalgo/database" 12 ) 13 14 const ( 15 defaultCacheSize = 1024 16 ) 17 18 var ( 19 headKey = []byte{0x01} 20 21 _ LinkedDB = (*linkedDB)(nil) 22 _ database.Iterator = (*iterator)(nil) 23 ) 24 25 // LinkedDB provides a key value interface while allowing iteration. 26 type LinkedDB interface { 27 database.KeyValueReaderWriterDeleter 28 29 IsEmpty() (bool, error) 30 HeadKey() ([]byte, error) 31 Head() (key []byte, value []byte, err error) 32 33 NewIterator() database.Iterator 34 NewIteratorWithStart(start []byte) database.Iterator 35 } 36 37 type linkedDB struct { 38 // lock ensure that this data structure handles its thread safety correctly. 39 lock sync.RWMutex 40 41 cacheLock sync.Mutex 42 // these variables provide caching for the head key. 43 headKeyIsSynced, headKeyExists, headKeyIsUpdated, updatedHeadKeyExists bool 44 headKey, updatedHeadKey []byte 45 // these variables provide caching for the nodes. 46 nodeCache cache.Cacher[string, *node] // key -> *node 47 updatedNodes map[string]*node 48 49 // db is the underlying database that this list is stored in. 50 db database.Database 51 // batch writes to [db] atomically. 52 batch database.Batch 53 } 54 55 type node struct { 56 Value []byte `serialize:"true"` 57 HasNext bool `serialize:"true"` 58 Next []byte `serialize:"true"` 59 HasPrevious bool `serialize:"true"` 60 Previous []byte `serialize:"true"` 61 } 62 63 func New(db database.Database, cacheSize int) LinkedDB { 64 return &linkedDB{ 65 nodeCache: &cache.LRU[string, *node]{Size: cacheSize}, 66 updatedNodes: make(map[string]*node), 67 db: db, 68 batch: db.NewBatch(), 69 } 70 } 71 72 func NewDefault(db database.Database) LinkedDB { 73 return New(db, defaultCacheSize) 74 } 75 76 func (ldb *linkedDB) Has(key []byte) (bool, error) { 77 ldb.lock.RLock() 78 defer ldb.lock.RUnlock() 79 80 return ldb.db.Has(nodeKey(key)) 81 } 82 83 func (ldb *linkedDB) Get(key []byte) ([]byte, error) { 84 ldb.lock.RLock() 85 defer ldb.lock.RUnlock() 86 87 node, err := ldb.getNode(key) 88 return node.Value, err 89 } 90 91 func (ldb *linkedDB) Put(key, value []byte) error { 92 ldb.lock.Lock() 93 defer ldb.lock.Unlock() 94 95 ldb.resetBatch() 96 97 // If the key already has a node in the list, update that node. 98 existingNode, err := ldb.getNode(key) 99 if err == nil { 100 existingNode.Value = slices.Clone(value) 101 if err := ldb.putNode(key, existingNode); err != nil { 102 return err 103 } 104 return ldb.writeBatch() 105 } 106 if err != database.ErrNotFound { 107 return err 108 } 109 110 // The key isn't currently in the list, so we should add it as the head. 111 newHead := node{Value: slices.Clone(value)} 112 if headKey, err := ldb.getHeadKey(); err == nil { 113 // The list currently has a head, so we need to update the old head. 114 oldHead, err := ldb.getNode(headKey) 115 if err != nil { 116 return err 117 } 118 oldHead.HasPrevious = true 119 oldHead.Previous = key 120 if err := ldb.putNode(headKey, oldHead); err != nil { 121 return err 122 } 123 124 newHead.HasNext = true 125 newHead.Next = headKey 126 } else if err != database.ErrNotFound { 127 return err 128 } 129 if err := ldb.putNode(key, newHead); err != nil { 130 return err 131 } 132 if err := ldb.putHeadKey(key); err != nil { 133 return err 134 } 135 return ldb.writeBatch() 136 } 137 138 func (ldb *linkedDB) Delete(key []byte) error { 139 ldb.lock.Lock() 140 defer ldb.lock.Unlock() 141 142 currentNode, err := ldb.getNode(key) 143 if err == database.ErrNotFound { 144 return nil 145 } 146 if err != nil { 147 return err 148 } 149 150 ldb.resetBatch() 151 152 // We're trying to delete this node. 153 if err := ldb.deleteNode(key); err != nil { 154 return err 155 } 156 157 switch { 158 case currentNode.HasPrevious: 159 // We aren't modifying the head. 160 previousNode, err := ldb.getNode(currentNode.Previous) 161 if err != nil { 162 return err 163 } 164 previousNode.HasNext = currentNode.HasNext 165 previousNode.Next = currentNode.Next 166 if err := ldb.putNode(currentNode.Previous, previousNode); err != nil { 167 return err 168 } 169 if currentNode.HasNext { 170 // We aren't modifying the tail. 171 nextNode, err := ldb.getNode(currentNode.Next) 172 if err != nil { 173 return err 174 } 175 nextNode.HasPrevious = true 176 nextNode.Previous = currentNode.Previous 177 if err := ldb.putNode(currentNode.Next, nextNode); err != nil { 178 return err 179 } 180 } 181 case !currentNode.HasNext: 182 // This is the only node, so we don't have a head anymore. 183 if err := ldb.deleteHeadKey(); err != nil { 184 return err 185 } 186 default: 187 // The next node will be the new head. 188 if err := ldb.putHeadKey(currentNode.Next); err != nil { 189 return err 190 } 191 nextNode, err := ldb.getNode(currentNode.Next) 192 if err != nil { 193 return err 194 } 195 nextNode.HasPrevious = false 196 nextNode.Previous = nil 197 if err := ldb.putNode(currentNode.Next, nextNode); err != nil { 198 return err 199 } 200 } 201 return ldb.writeBatch() 202 } 203 204 func (ldb *linkedDB) IsEmpty() (bool, error) { 205 _, err := ldb.HeadKey() 206 if err == database.ErrNotFound { 207 return true, nil 208 } 209 return false, err 210 } 211 212 func (ldb *linkedDB) HeadKey() ([]byte, error) { 213 ldb.lock.RLock() 214 defer ldb.lock.RUnlock() 215 216 return ldb.getHeadKey() 217 } 218 219 func (ldb *linkedDB) Head() ([]byte, []byte, error) { 220 ldb.lock.RLock() 221 defer ldb.lock.RUnlock() 222 223 headKey, err := ldb.getHeadKey() 224 if err != nil { 225 return nil, nil, err 226 } 227 head, err := ldb.getNode(headKey) 228 return headKey, head.Value, err 229 } 230 231 // This iterator does not guarantee that keys are returned in lexicographic 232 // order. 233 func (ldb *linkedDB) NewIterator() database.Iterator { 234 return &iterator{ldb: ldb} 235 } 236 237 // NewIteratorWithStart returns an iterator that starts at [start]. 238 // This iterator does not guarantee that keys are returned in lexicographic 239 // order. 240 // If [start] is not in the list, starts iterating from the list head. 241 func (ldb *linkedDB) NewIteratorWithStart(start []byte) database.Iterator { 242 hasStartKey, err := ldb.Has(start) 243 if err == nil && hasStartKey { 244 return &iterator{ 245 ldb: ldb, 246 initialized: true, 247 nextKey: start, 248 } 249 } 250 // If the start key isn't present, start from the head 251 return ldb.NewIterator() 252 } 253 254 func (ldb *linkedDB) getHeadKey() ([]byte, error) { 255 // If the ldb read lock is held, then there needs to be additional 256 // synchronization here to avoid racy behavior. 257 ldb.cacheLock.Lock() 258 defer ldb.cacheLock.Unlock() 259 260 if ldb.headKeyIsSynced { 261 if ldb.headKeyExists { 262 return ldb.headKey, nil 263 } 264 return nil, database.ErrNotFound 265 } 266 headKey, err := ldb.db.Get(headKey) 267 if err == nil { 268 ldb.headKeyIsSynced = true 269 ldb.headKeyExists = true 270 ldb.headKey = headKey 271 return headKey, nil 272 } 273 if err == database.ErrNotFound { 274 ldb.headKeyIsSynced = true 275 ldb.headKeyExists = false 276 return nil, database.ErrNotFound 277 } 278 return headKey, err 279 } 280 281 func (ldb *linkedDB) putHeadKey(key []byte) error { 282 ldb.headKeyIsUpdated = true 283 ldb.updatedHeadKeyExists = true 284 ldb.updatedHeadKey = key 285 return ldb.batch.Put(headKey, key) 286 } 287 288 func (ldb *linkedDB) deleteHeadKey() error { 289 ldb.headKeyIsUpdated = true 290 ldb.updatedHeadKeyExists = false 291 return ldb.batch.Delete(headKey) 292 } 293 294 func (ldb *linkedDB) getNode(key []byte) (node, error) { 295 // If the ldb read lock is held, then there needs to be additional 296 // synchronization here to avoid racy behavior. 297 ldb.cacheLock.Lock() 298 defer ldb.cacheLock.Unlock() 299 300 keyStr := string(key) 301 if n, exists := ldb.nodeCache.Get(keyStr); exists { 302 if n == nil { 303 return node{}, database.ErrNotFound 304 } 305 return *n, nil 306 } 307 308 nodeBytes, err := ldb.db.Get(nodeKey(key)) 309 if err == database.ErrNotFound { 310 ldb.nodeCache.Put(keyStr, nil) 311 return node{}, err 312 } 313 if err != nil { 314 return node{}, err 315 } 316 n := node{} 317 _, err = Codec.Unmarshal(nodeBytes, &n) 318 if err == nil { 319 ldb.nodeCache.Put(keyStr, &n) 320 } 321 return n, err 322 } 323 324 func (ldb *linkedDB) putNode(key []byte, n node) error { 325 ldb.updatedNodes[string(key)] = &n 326 nodeBytes, err := Codec.Marshal(CodecVersion, n) 327 if err != nil { 328 return err 329 } 330 return ldb.batch.Put(nodeKey(key), nodeBytes) 331 } 332 333 func (ldb *linkedDB) deleteNode(key []byte) error { 334 ldb.updatedNodes[string(key)] = nil 335 return ldb.batch.Delete(nodeKey(key)) 336 } 337 338 func (ldb *linkedDB) resetBatch() { 339 ldb.headKeyIsUpdated = false 340 clear(ldb.updatedNodes) 341 ldb.batch.Reset() 342 } 343 344 func (ldb *linkedDB) writeBatch() error { 345 if err := ldb.batch.Write(); err != nil { 346 return err 347 } 348 if ldb.headKeyIsUpdated { 349 ldb.headKeyIsSynced = true 350 ldb.headKeyExists = ldb.updatedHeadKeyExists 351 ldb.headKey = ldb.updatedHeadKey 352 } 353 for key, n := range ldb.updatedNodes { 354 ldb.nodeCache.Put(key, n) 355 } 356 return nil 357 } 358 359 type iterator struct { 360 ldb *linkedDB 361 initialized, exhausted bool 362 key, value, nextKey []byte 363 err error 364 } 365 366 func (it *iterator) Next() bool { 367 // If the iterator has been exhausted, there is no next value. 368 if it.exhausted { 369 it.key = nil 370 it.value = nil 371 return false 372 } 373 374 it.ldb.lock.RLock() 375 defer it.ldb.lock.RUnlock() 376 377 // If the iterator was not yet initialized, do it now. 378 if !it.initialized { 379 it.initialized = true 380 headKey, err := it.ldb.getHeadKey() 381 if err == database.ErrNotFound { 382 it.exhausted = true 383 it.key = nil 384 it.value = nil 385 return false 386 } 387 if err != nil { 388 it.exhausted = true 389 it.key = nil 390 it.value = nil 391 it.err = err 392 return false 393 } 394 it.nextKey = headKey 395 } 396 397 nextNode, err := it.ldb.getNode(it.nextKey) 398 if err == database.ErrNotFound { 399 it.exhausted = true 400 it.key = nil 401 it.value = nil 402 return false 403 } 404 if err != nil { 405 it.exhausted = true 406 it.key = nil 407 it.value = nil 408 it.err = err 409 return false 410 } 411 it.key = it.nextKey 412 it.value = nextNode.Value 413 it.nextKey = nextNode.Next 414 it.exhausted = !nextNode.HasNext 415 return true 416 } 417 418 func (it *iterator) Error() error { 419 return it.err 420 } 421 422 func (it *iterator) Key() []byte { 423 return it.key 424 } 425 426 func (it *iterator) Value() []byte { 427 return it.value 428 } 429 430 func (*iterator) Release() {} 431 432 func nodeKey(key []byte) []byte { 433 newKey := make([]byte, len(key)+1) 434 copy(newKey[1:], key) 435 return newKey 436 }