Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merkle db iterator #1533

Merged
merged 49 commits into from
Jun 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
45f2286
initial commit
tonyqzhang Apr 27, 2023
60c22c9
1> init commit db_test.go 2> do HealthCheck before call real command …
tonyqzhang May 1, 2023
f845769
use contex.TODO as a placeholder for HealthCheck argument
tonyqzhang May 1, 2023
19dfdf7
func New: the 5th arg is unused, use _
tonyqzhang May 2, 2023
7de7660
gofmt issue
tonyqzhang May 2, 2023
293382d
gofmt issue
tonyqzhang May 2, 2023
0177901
gofumpt issue
tonyqzhang May 2, 2023
6cf5633
init pebble/metrics.go
tonyqzhang May 3, 2023
1cc4137
format issus
tonyqzhang May 3, 2023
923a411
more metrics items for compaction
tonyqzhang May 3, 2023
ee953ba
add more metrics items
tonyqzhang May 4, 2023
97163ef
Merge branch 'dev' into tony_avalanchego
tonyqzhang May 4, 2023
27f0e35
add more metrics items
tonyqzhang May 4, 2023
74f3b45
Add Iterator
dboehm-avalabs May 6, 2023
e4c6e6f
Merge branch 'MerkleDBIterator' of github.com:ava-labs/avalanchego in…
tonyqzhang May 7, 2023
5de1187
define trieview.getKeyValues by viewIterator
tonyqzhang May 10, 2023
4afa929
Merge branch 'dev' into MerkleDBIterator
tonyqzhang May 10, 2023
c0d7763
trieview getKeyValues: need check maxLength
tonyqzhang May 10, 2023
f0e19f9
define a new test Test_View_Iteration2
tonyqzhang May 12, 2023
aa07b3b
func (t *trieView) getKeyValues: needs take care corner cases of inpu…
tonyqzhang May 12, 2023
dacfe3c
fix
dboehm-avalabs May 19, 2023
2cd9799
Update trie_test.go
dboehm-avalabs May 19, 2023
3d5646d
delete pebble
dboehm-avalabs May 19, 2023
e9d0efe
Update go.mod
dboehm-avalabs May 19, 2023
69bd089
Update go.sum
dboehm-avalabs May 19, 2023
70a15f4
Update trie_test.go
dboehm-avalabs May 19, 2023
f9a5d23
Merge branch 'dev' into MerkleDBIterator
dboehm-avalabs May 23, 2023
2b6bd07
Update mock_db.go
dboehm-avalabs May 23, 2023
7cdb4f5
Merge branch 'dev' into MerkleDBIterator
May 24, 2023
e1c7247
Update x/merkledb/view_iterator.go
dboehm-avalabs May 26, 2023
7782ebe
Update view_iterator.go
dboehm-avalabs May 26, 2023
ed37096
Merge branch 'MerkleDBIterator' of https://github.com/ava-labs/avalan…
dboehm-avalabs May 26, 2023
a250047
Update view_iterator.go
dboehm-avalabs May 26, 2023
204380b
update
dboehm-avalabs May 26, 2023
cf93b3c
Update view_iterator.go
dboehm-avalabs May 26, 2023
a0e2778
Merge branch 'dev' into MerkleDBIterator
Jun 7, 2023
4bc9c94
nits
Jun 7, 2023
cb281db
nits
Jun 7, 2023
82743ab
nits; add test
Jun 7, 2023
9d8bda2
appease linter; add to test
Jun 7, 2023
d0a2633
appease linter
Jun 7, 2023
b242237
appease linter
Jun 8, 2023
8acbf5e
Merge branch 'dev' into MerkleDBIterator
Jun 8, 2023
e9428af
Merge branch 'dev' into MerkleDBIterator
Jun 8, 2023
10397c5
nits
Jun 8, 2023
7d21195
Merge branch 'dev' into MerkleDBIterator
Jun 8, 2023
1089bed
Merge branch 'dev' into MerkleDBIterator
Jun 8, 2023
ad6c4ea
Merge branch 'dev' into MerkleDBIterator
Jun 8, 2023
5bd5c0b
Merge branch 'dev' into MerkleDBIterator
Jun 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 1 addition & 50 deletions x/merkledb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -1162,7 +1162,7 @@ func (db *merkleDB) getHistoricalViewForRange(
return newTrieViewWithChanges(db, db, changeHistory, len(changeHistory.nodes))
}

// Returns all of the keys in range [start, end] that aren't in [keySet].
// Returns all keys in range [start, end] that aren't in [keySet].
// If [start] is nil, then the range has no lower bound.
// If [end] is nil, then the range has no upper bound.
func (db *merkleDB) getKeysNotInSet(start, end []byte, keySet set.Set[string]) ([][]byte, error) {
Expand Down Expand Up @@ -1242,55 +1242,6 @@ func (db *merkleDB) getNode(key path) (*node, error) {
return node, err
}

// If [lock], grabs [db.lock]'s read lock.
// Otherwise assumes [db.lock] is already read locked.
func (db *merkleDB) getKeyValues(
start []byte,
end []byte,
maxLength int,
keysToIgnore set.Set[string],
lock bool,
) ([]KeyValue, error) {
if lock {
db.lock.RLock()
defer db.lock.RUnlock()
}

if db.closed {
return nil, database.ErrClosed
}

if maxLength <= 0 {
return nil, fmt.Errorf("%w but was %d", ErrInvalidMaxLength, maxLength)
}

it := db.NewIteratorWithStart(start)
defer it.Release()

remainingLength := maxLength
result := make([]KeyValue, 0, maxLength)
// Keep adding key/value pairs until one of the following:
// * We hit a key that is lexicographically larger than the end key.
// * [maxLength] elements are in [result].
// * There are no more values to add.
for remainingLength > 0 && it.Next() {
key := it.Key()
if len(end) != 0 && bytes.Compare(it.Key(), end) > 0 {
break
}
if keysToIgnore.Contains(string(key)) {
continue
}
result = append(result, KeyValue{
Key: key,
Value: it.Value(),
})
remainingLength--
}

return result, it.Error()
}

// Returns a new view atop [db] with the changes in [ops] applied to it.
// Assumes [db.commitLock] is read locked.
func (db *merkleDB) prepareBatchView(ops []database.BatchOp) (*trieView, error) {
Expand Down
16 changes: 0 additions & 16 deletions x/merkledb/mock_db.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 2 additions & 8 deletions x/merkledb/trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"context"
"errors"

"github.com/ava-labs/avalanchego/database"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/set"
)

var errNoNewRoot = errors.New("there was no updated root in change list")
Expand Down Expand Up @@ -46,13 +46,7 @@ type ReadOnlyTrie interface {
// GetRangeProof generates a proof of up to maxLength smallest key/values with keys between start and end
GetRangeProof(ctx context.Context, start, end []byte, maxLength int) (*RangeProof, error)

getKeyValues(
start []byte,
end []byte,
maxLength int,
keysToIgnore set.Set[string],
lock bool,
) ([]KeyValue, error)
database.Iteratee
}

type Trie interface {
Expand Down
169 changes: 16 additions & 153 deletions x/merkledb/trieview.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import (

"github.com/ava-labs/avalanchego/database"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/set"
)

const defaultPreallocationSize = 100
const (
initKeyValuesSize = 256
defaultPreallocationSize = 100
)

var (
_ TrieView = (*trieView)(nil)
Expand Down Expand Up @@ -418,25 +420,20 @@ func (t *trieView) GetRangeProof(
t.lock.RLock()
}

var (
result RangeProof
err error
)
var result RangeProof

result.KeyValues, err = t.getKeyValues(
start,
end,
maxLength,
set.Set[string]{},
false, /*lock*/
)
if err != nil {
return nil, err
result.KeyValues = make([]KeyValue, 0, initKeyValuesSize)
it := t.NewIteratorWithStart(start)
for it.Next() && len(result.KeyValues) < maxLength && (len(end) == 0 || bytes.Compare(it.Key(), end) <= 0) {
// clone the value to prevent editing of the values stored within the trie
result.KeyValues = append(result.KeyValues, KeyValue{
Key: it.Key(),
Value: slices.Clone(it.Value()),
})
}

// copy values, so edits won't affect the underlying arrays
for i, kv := range result.KeyValues {
result.KeyValues[i] = KeyValue{Key: kv.Key, Value: slices.Clone(kv.Value)}
it.Release()
darioush marked this conversation as resolved.
Show resolved Hide resolved
if err := it.Error(); err != nil {
return nil, err
}

// This proof may not contain all key-value pairs in [start, end] due to size limitations.
Expand Down Expand Up @@ -719,140 +716,6 @@ func (t *trieView) getMerkleRoot(ctx context.Context) (ids.ID, error) {
return t.root.id, nil
}

// Returns up to [maxLength] key/values from keys in closed range [start, end].
// Acts similarly to the merge step of a merge sort to combine state from the view
// with state from the parent trie.
// If [lock], grabs [t.lock]'s read lock.
// Otherwise assumes [t.lock]'s read lock is held.
func (t *trieView) getKeyValues(
start []byte,
end []byte,
maxLength int,
keysToIgnore set.Set[string],
lock bool,
) ([]KeyValue, error) {
if lock {
t.lock.RLock()
defer t.lock.RUnlock()
}

if maxLength <= 0 {
return nil, fmt.Errorf("%w but was %d", ErrInvalidMaxLength, maxLength)
}

if t.isInvalid() {
return nil, ErrInvalid
}

// collect all values that have changed or been deleted
changes := make([]KeyValue, 0, len(t.changes.values))
for key, change := range t.changes.values {
if change.after.IsNothing() {
// This was deleted
keysToIgnore.Add(string(key.Serialize().Value))
} else {
changes = append(changes, KeyValue{
Key: key.Serialize().Value,
Value: change.after.value,
})
}
}
// sort [changes] so they can be merged with the parent trie's state
slices.SortFunc(changes, func(a, b KeyValue) bool {
return bytes.Compare(a.Key, b.Key) == -1
})

baseKeyValues, err := t.getParentTrie().getKeyValues(
start,
end,
maxLength,
keysToIgnore,
true, /*lock*/
)
if err != nil {
return nil, err
}

var (
// True if there are more key/value pairs from [baseKeyValues] to add to result
baseKeyValuesFinished = false
// True if there are more key/value pairs from [changes] to add to result
changesFinished = false
// The index of the next key/value pair to add from [baseKeyValues].
baseKeyValuesIndex = 0
// The index of the next key/value pair to add from [changes].
changesIndex = 0
remainingLength = maxLength
hasUpperBound = len(end) > 0
result = make([]KeyValue, 0, len(baseKeyValues))
)

// keep adding key/value pairs until one of the following:
// * a key that is lexicographically larger than the end key is hit
// * the maxLength is hit
// * no more values are available to add
for remainingLength > 0 {
// the baseKeyValues iterator is finished when we have run out of keys or hit a key greater than the end key
baseKeyValuesFinished = baseKeyValuesFinished ||
(baseKeyValuesIndex >= len(baseKeyValues) || (hasUpperBound && bytes.Compare(baseKeyValues[baseKeyValuesIndex].Key, end) == 1))

// the changes iterator is finished when we have run out of keys or hit a key greater than the end key
changesFinished = changesFinished ||
(changesIndex >= len(changes) || (hasUpperBound && bytes.Compare(changes[changesIndex].Key, end) == 1))

// if both the base state and changes are finished, return the result of the merge
if baseKeyValuesFinished && changesFinished {
return result, nil
}

// one or both iterators still have values, so one will be added to the result
remainingLength--

// both still have key/values available, so add the smallest key
if !changesFinished && !baseKeyValuesFinished {
currentChangeState := changes[changesIndex]
currentKeyValues := baseKeyValues[baseKeyValuesIndex]

switch bytes.Compare(currentChangeState.Key, currentKeyValues.Key) {
case -1:
result = append(result, currentChangeState)
changesIndex++
case 0:
// the keys are the same, so override the base value with the changed value
result = append(result, currentChangeState)
changesIndex++
baseKeyValuesIndex++
case 1:
result = append(result, currentKeyValues)
baseKeyValuesIndex++
}
continue
}

// the base state is not finished, but the changes is finished.
// add the next base state value.
if !baseKeyValuesFinished {
currentBaseState := baseKeyValues[baseKeyValuesIndex]
result = append(result, currentBaseState)
baseKeyValuesIndex++
continue
}

// the base state is finished, but the changes is not finished.
// add the next changes value.
currentChangeState := changes[changesIndex]
result = append(result, currentChangeState)
changesIndex++
}

// ensure no ancestor changes occurred during execution
if t.isInvalid() {
return nil, ErrInvalid
}

return result, nil
}

func (t *trieView) GetValues(_ context.Context, keys [][]byte) ([][]byte, []error) {
t.lock.RLock()
defer t.lock.RUnlock()
Expand Down
Loading