core, ethdb, tests, trie: introduce database snapshot (#24486)
This commit is contained in:
@ -35,6 +35,10 @@ var (
|
||||
// errMemorydbNotFound is returned if a key is requested that is not found in
|
||||
// the provided memory database.
|
||||
errMemorydbNotFound = errors.New("not found")
|
||||
|
||||
// errSnapshotReleased is returned if callers want to retrieve data from a
|
||||
// released snapshot.
|
||||
errSnapshotReleased = errors.New("snapshot released")
|
||||
)
|
||||
|
||||
// Database is an ephemeral key-value store. Apart from basic data storage
|
||||
@ -53,7 +57,7 @@ func New() *Database {
|
||||
}
|
||||
}
|
||||
|
||||
// NewWithCap returns a wrapped map pre-allocated to the provided capcity with
|
||||
// NewWithCap returns a wrapped map pre-allocated to the provided capacity with
|
||||
// all the required database interface methods implemented.
|
||||
func NewWithCap(size int) *Database {
|
||||
return &Database{
|
||||
@ -170,6 +174,13 @@ func (db *Database) NewIterator(prefix []byte, start []byte) ethdb.Iterator {
|
||||
}
|
||||
}
|
||||
|
||||
// NewSnapshot creates a database snapshot based on the current state.
|
||||
// The created snapshot will not be affected by all following mutations
|
||||
// happened on the database.
|
||||
func (db *Database) NewSnapshot() (ethdb.Snapshot, error) {
|
||||
return newSnapshot(db), nil
|
||||
}
|
||||
|
||||
// Stat returns a particular internal stat of the database.
|
||||
func (db *Database) Stat(property string) (string, error) {
|
||||
return "", errors.New("unknown property")
|
||||
@ -320,3 +331,59 @@ func (it *iterator) Value() []byte {
|
||||
func (it *iterator) Release() {
|
||||
it.keys, it.values = nil, nil
|
||||
}
|
||||
|
||||
// snapshot wraps a batch of key-value entries deep copied from the in-memory
|
||||
// database for implementing the Snapshot interface.
|
||||
type snapshot struct {
|
||||
db map[string][]byte
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// newSnapshot initializes the snapshot with the given database instance.
|
||||
func newSnapshot(db *Database) *snapshot {
|
||||
db.lock.RLock()
|
||||
defer db.lock.RUnlock()
|
||||
|
||||
copied := make(map[string][]byte)
|
||||
for key, val := range db.db {
|
||||
copied[key] = common.CopyBytes(val)
|
||||
}
|
||||
return &snapshot{db: copied}
|
||||
}
|
||||
|
||||
// Has retrieves if a key is present in the snapshot backing by a key-value
|
||||
// data store.
|
||||
func (snap *snapshot) Has(key []byte) (bool, error) {
|
||||
snap.lock.RLock()
|
||||
defer snap.lock.RUnlock()
|
||||
|
||||
if snap.db == nil {
|
||||
return false, errSnapshotReleased
|
||||
}
|
||||
_, ok := snap.db[string(key)]
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
// Get retrieves the given key if it's present in the snapshot backing by
|
||||
// key-value data store.
|
||||
func (snap *snapshot) Get(key []byte) ([]byte, error) {
|
||||
snap.lock.RLock()
|
||||
defer snap.lock.RUnlock()
|
||||
|
||||
if snap.db == nil {
|
||||
return nil, errSnapshotReleased
|
||||
}
|
||||
if entry, ok := snap.db[string(key)]; ok {
|
||||
return common.CopyBytes(entry), nil
|
||||
}
|
||||
return nil, errMemorydbNotFound
|
||||
}
|
||||
|
||||
// Release releases associated resources. Release should always succeed and can
|
||||
// be called multiple times without causing error.
|
||||
func (snap *snapshot) Release() {
|
||||
snap.lock.Lock()
|
||||
defer snap.lock.Unlock()
|
||||
|
||||
snap.db = nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user