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  }