github.com/MetalBlockchain/metalgo@v1.11.9/database/versiondb/db.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package versiondb
     5  
     6  import (
     7  	"context"
     8  	"slices"
     9  	"strings"
    10  	"sync"
    11  
    12  	"github.com/MetalBlockchain/metalgo/database"
    13  	"github.com/MetalBlockchain/metalgo/database/memdb"
    14  )
    15  
    16  var (
    17  	_ database.Database = (*Database)(nil)
    18  	_ Commitable        = (*Database)(nil)
    19  	_ database.Batch    = (*batch)(nil)
    20  	_ database.Iterator = (*iterator)(nil)
    21  )
    22  
    23  // Commitable defines the interface that specifies that something may be
    24  // committed.
    25  type Commitable interface {
    26  	// Commit writes all the queued operations to the underlying data structure.
    27  	Commit() error
    28  }
    29  
    30  // Database implements the Database interface by living on top of another
    31  // database, writing changes to the underlying database only when commit is
    32  // called.
    33  type Database struct {
    34  	lock  sync.RWMutex
    35  	mem   map[string]valueDelete
    36  	db    database.Database
    37  	batch database.Batch
    38  }
    39  
    40  type valueDelete struct {
    41  	value  []byte
    42  	delete bool
    43  }
    44  
    45  // New returns a new versioned database
    46  func New(db database.Database) *Database {
    47  	return &Database{
    48  		mem:   make(map[string]valueDelete, memdb.DefaultSize),
    49  		db:    db,
    50  		batch: db.NewBatch(),
    51  	}
    52  }
    53  
    54  func (db *Database) Has(key []byte) (bool, error) {
    55  	db.lock.RLock()
    56  	defer db.lock.RUnlock()
    57  
    58  	if db.mem == nil {
    59  		return false, database.ErrClosed
    60  	}
    61  	if val, has := db.mem[string(key)]; has {
    62  		return !val.delete, nil
    63  	}
    64  	return db.db.Has(key)
    65  }
    66  
    67  func (db *Database) Get(key []byte) ([]byte, error) {
    68  	db.lock.RLock()
    69  	defer db.lock.RUnlock()
    70  
    71  	if db.mem == nil {
    72  		return nil, database.ErrClosed
    73  	}
    74  	if val, has := db.mem[string(key)]; has {
    75  		if val.delete {
    76  			return nil, database.ErrNotFound
    77  		}
    78  		return slices.Clone(val.value), nil
    79  	}
    80  	return db.db.Get(key)
    81  }
    82  
    83  func (db *Database) Put(key, value []byte) error {
    84  	db.lock.Lock()
    85  	defer db.lock.Unlock()
    86  
    87  	if db.mem == nil {
    88  		return database.ErrClosed
    89  	}
    90  	db.mem[string(key)] = valueDelete{value: slices.Clone(value)}
    91  	return nil
    92  }
    93  
    94  func (db *Database) Delete(key []byte) error {
    95  	db.lock.Lock()
    96  	defer db.lock.Unlock()
    97  
    98  	if db.mem == nil {
    99  		return database.ErrClosed
   100  	}
   101  	db.mem[string(key)] = valueDelete{delete: true}
   102  	return nil
   103  }
   104  
   105  func (db *Database) NewBatch() database.Batch {
   106  	return &batch{db: db}
   107  }
   108  
   109  func (db *Database) NewIterator() database.Iterator {
   110  	return db.NewIteratorWithStartAndPrefix(nil, nil)
   111  }
   112  
   113  func (db *Database) NewIteratorWithStart(start []byte) database.Iterator {
   114  	return db.NewIteratorWithStartAndPrefix(start, nil)
   115  }
   116  
   117  func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator {
   118  	return db.NewIteratorWithStartAndPrefix(nil, prefix)
   119  }
   120  
   121  func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator {
   122  	db.lock.RLock()
   123  	defer db.lock.RUnlock()
   124  
   125  	if db.mem == nil {
   126  		return &database.IteratorError{
   127  			Err: database.ErrClosed,
   128  		}
   129  	}
   130  
   131  	startString := string(start)
   132  	prefixString := string(prefix)
   133  	keys := make([]string, 0, len(db.mem))
   134  	for key := range db.mem {
   135  		if strings.HasPrefix(key, prefixString) && key >= startString {
   136  			keys = append(keys, key)
   137  		}
   138  	}
   139  	slices.Sort(keys) // Keys need to be in sorted order
   140  	values := make([]valueDelete, len(keys))
   141  	for i, key := range keys {
   142  		values[i] = db.mem[key]
   143  	}
   144  
   145  	return &iterator{
   146  		db:       db,
   147  		Iterator: db.db.NewIteratorWithStartAndPrefix(start, prefix),
   148  		keys:     keys,
   149  		values:   values,
   150  	}
   151  }
   152  
   153  func (db *Database) Compact(start, limit []byte) error {
   154  	db.lock.Lock()
   155  	defer db.lock.Unlock()
   156  
   157  	if db.mem == nil {
   158  		return database.ErrClosed
   159  	}
   160  	return db.db.Compact(start, limit)
   161  }
   162  
   163  // SetDatabase changes the underlying database to the specified database
   164  func (db *Database) SetDatabase(newDB database.Database) error {
   165  	db.lock.Lock()
   166  	defer db.lock.Unlock()
   167  
   168  	if db.mem == nil {
   169  		return database.ErrClosed
   170  	}
   171  
   172  	db.db = newDB
   173  	db.batch = newDB.NewBatch()
   174  	return nil
   175  }
   176  
   177  // GetDatabase returns the underlying database
   178  func (db *Database) GetDatabase() database.Database {
   179  	db.lock.RLock()
   180  	defer db.lock.RUnlock()
   181  
   182  	return db.db
   183  }
   184  
   185  // Commit writes all the operations of this database to the underlying database
   186  func (db *Database) Commit() error {
   187  	db.lock.Lock()
   188  	defer db.lock.Unlock()
   189  
   190  	batch, err := db.commitBatch()
   191  	if err != nil {
   192  		return err
   193  	}
   194  	if err := batch.Write(); err != nil {
   195  		return err
   196  	}
   197  	batch.Reset()
   198  	db.abort()
   199  	return nil
   200  }
   201  
   202  // Abort all changes to the underlying database
   203  func (db *Database) Abort() {
   204  	db.lock.Lock()
   205  	defer db.lock.Unlock()
   206  
   207  	db.abort()
   208  }
   209  
   210  func (db *Database) abort() {
   211  	clear(db.mem)
   212  }
   213  
   214  // CommitBatch returns a batch that contains all uncommitted puts/deletes.
   215  // Calling Write() on the returned batch causes the puts/deletes to be
   216  // written to the underlying database. The returned batch should be written before
   217  // future calls to this DB unless the batch will never be written.
   218  func (db *Database) CommitBatch() (database.Batch, error) {
   219  	db.lock.Lock()
   220  	defer db.lock.Unlock()
   221  
   222  	return db.commitBatch()
   223  }
   224  
   225  // Put all of the puts/deletes in memory into db.batch
   226  // and return the batch
   227  func (db *Database) commitBatch() (database.Batch, error) {
   228  	if db.mem == nil {
   229  		return nil, database.ErrClosed
   230  	}
   231  
   232  	db.batch.Reset()
   233  	for key, value := range db.mem {
   234  		if value.delete {
   235  			if err := db.batch.Delete([]byte(key)); err != nil {
   236  				return nil, err
   237  			}
   238  		} else if err := db.batch.Put([]byte(key), value.value); err != nil {
   239  			return nil, err
   240  		}
   241  	}
   242  
   243  	return db.batch, nil
   244  }
   245  
   246  func (db *Database) Close() error {
   247  	db.lock.Lock()
   248  	defer db.lock.Unlock()
   249  
   250  	if db.mem == nil {
   251  		return database.ErrClosed
   252  	}
   253  	db.batch = nil
   254  	db.mem = nil
   255  	db.db = nil
   256  	return nil
   257  }
   258  
   259  func (db *Database) isClosed() bool {
   260  	db.lock.RLock()
   261  	defer db.lock.RUnlock()
   262  
   263  	return db.db == nil
   264  }
   265  
   266  func (db *Database) HealthCheck(ctx context.Context) (interface{}, error) {
   267  	db.lock.RLock()
   268  	defer db.lock.RUnlock()
   269  
   270  	if db.mem == nil {
   271  		return nil, database.ErrClosed
   272  	}
   273  	return db.db.HealthCheck(ctx)
   274  }
   275  
   276  type batch struct {
   277  	database.BatchOps
   278  
   279  	db *Database
   280  }
   281  
   282  func (b *batch) Write() error {
   283  	b.db.lock.Lock()
   284  	defer b.db.lock.Unlock()
   285  
   286  	if b.db.mem == nil {
   287  		return database.ErrClosed
   288  	}
   289  
   290  	for _, op := range b.Ops {
   291  		b.db.mem[string(op.Key)] = valueDelete{
   292  			value:  op.Value,
   293  			delete: op.Delete,
   294  		}
   295  	}
   296  	return nil
   297  }
   298  
   299  func (b *batch) Inner() database.Batch {
   300  	return b
   301  }
   302  
   303  // iterator walks over both the in memory database and the underlying database
   304  // at the same time.
   305  type iterator struct {
   306  	db *Database
   307  	database.Iterator
   308  
   309  	key, value []byte
   310  	err        error
   311  
   312  	keys   []string
   313  	values []valueDelete
   314  
   315  	initialized, exhausted bool
   316  }
   317  
   318  // Next moves the iterator to the next key/value pair. It returns whether the
   319  // iterator is exhausted. We must pay careful attention to set the proper values
   320  // based on if the in memory db or the underlying db should be read next
   321  func (it *iterator) Next() bool {
   322  	// Short-circuit and set an error if the underlying database has been closed.
   323  	if it.db.isClosed() {
   324  		it.key = nil
   325  		it.value = nil
   326  		it.err = database.ErrClosed
   327  		return false
   328  	}
   329  
   330  	if !it.initialized {
   331  		it.exhausted = !it.Iterator.Next()
   332  		it.initialized = true
   333  	}
   334  
   335  	for {
   336  		switch {
   337  		case it.exhausted && len(it.keys) == 0:
   338  			it.key = nil
   339  			it.value = nil
   340  			return false
   341  		case it.exhausted:
   342  			nextKey := it.keys[0]
   343  			nextValue := it.values[0]
   344  
   345  			it.keys[0] = ""
   346  			it.keys = it.keys[1:]
   347  			it.values[0].value = nil
   348  			it.values = it.values[1:]
   349  
   350  			if !nextValue.delete {
   351  				it.key = []byte(nextKey)
   352  				it.value = nextValue.value
   353  				return true
   354  			}
   355  		case len(it.keys) == 0:
   356  			it.key = it.Iterator.Key()
   357  			it.value = it.Iterator.Value()
   358  			it.exhausted = !it.Iterator.Next()
   359  			return true
   360  		default:
   361  			memKey := it.keys[0]
   362  			memValue := it.values[0]
   363  
   364  			dbKey := it.Iterator.Key()
   365  
   366  			dbStringKey := string(dbKey)
   367  			switch {
   368  			case memKey < dbStringKey:
   369  				it.keys[0] = ""
   370  				it.keys = it.keys[1:]
   371  				it.values[0].value = nil
   372  				it.values = it.values[1:]
   373  
   374  				if !memValue.delete {
   375  					it.key = []byte(memKey)
   376  					it.value = memValue.value
   377  					return true
   378  				}
   379  			case dbStringKey < memKey:
   380  				it.key = dbKey
   381  				it.value = it.Iterator.Value()
   382  				it.exhausted = !it.Iterator.Next()
   383  				return true
   384  			default:
   385  				it.keys[0] = ""
   386  				it.keys = it.keys[1:]
   387  				it.values[0].value = nil
   388  				it.values = it.values[1:]
   389  				it.exhausted = !it.Iterator.Next()
   390  
   391  				if !memValue.delete {
   392  					it.key = []byte(memKey)
   393  					it.value = memValue.value
   394  					return true
   395  				}
   396  			}
   397  		}
   398  	}
   399  }
   400  
   401  func (it *iterator) Error() error {
   402  	if it.err != nil {
   403  		return it.err
   404  	}
   405  	return it.Iterator.Error()
   406  }
   407  
   408  func (it *iterator) Key() []byte {
   409  	return it.key
   410  }
   411  
   412  func (it *iterator) Value() []byte {
   413  	return it.value
   414  }
   415  
   416  func (it *iterator) Release() {
   417  	it.key = nil
   418  	it.value = nil
   419  	it.keys = nil
   420  	it.values = nil
   421  	it.Iterator.Release()
   422  }