Skip to content

Commit 6402c42

Browse files
authored
all: simplify and fix database iteration with prefix/start (#20808)
* core/state/snapshot: start fixing disk iterator seek * ethdb, rawdb, leveldb, memorydb: implement iterators with prefix and start * les, core/state/snapshot: iterator fixes * all: remove two iterator methods * all: rename Iteratee.NewIteratorWith -> NewIterator * ethdb: fix review concerns
1 parent 00064dd commit 6402c42

24 files changed

+246
-185
lines changed

cmd/utils/cmd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ func ExportPreimages(db ethdb.Database, fn string) error {
301301
defer writer.(*gzip.Writer).Close()
302302
}
303303
// Iterate over the preimages and export them
304-
it := db.NewIteratorWithPrefix([]byte("secure-key-"))
304+
it := db.NewIterator([]byte("secure-key-"), nil)
305305
defer it.Release()
306306

307307
for it.Next() {

common/bytes.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,14 @@ func TrimLeftZeroes(s []byte) []byte {
145145
}
146146
return s[idx:]
147147
}
148+
149+
// TrimRightZeroes returns a subslice of s without trailing zeroes
150+
func TrimRightZeroes(s []byte) []byte {
151+
idx := len(s)
152+
for ; idx > 0; idx-- {
153+
if s[idx-1] != 0 {
154+
break
155+
}
156+
}
157+
return s[:idx]
158+
}

common/bytes_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,22 @@ func TestNoPrefixShortHexOddLength(t *testing.T) {
105105
t.Errorf("Expected %x got %x", expected, result)
106106
}
107107
}
108+
109+
func TestTrimRightZeroes(t *testing.T) {
110+
tests := []struct {
111+
arr []byte
112+
exp []byte
113+
}{
114+
{FromHex("0x00ffff00ff0000"), FromHex("0x00ffff00ff")},
115+
{FromHex("0x00000000000000"), []byte{}},
116+
{FromHex("0xff"), FromHex("0xff")},
117+
{[]byte{}, []byte{}},
118+
{FromHex("0x00ffffffffffff"), FromHex("0x00ffffffffffff")},
119+
}
120+
for i, test := range tests {
121+
got := TrimRightZeroes(test.arr)
122+
if !bytes.Equal(got, test.exp) {
123+
t.Errorf("test %d, got %x exp %x", i, got, test.exp)
124+
}
125+
}
126+
}

core/rawdb/accessors_chain.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func ReadAllHashes(db ethdb.Iteratee, number uint64) []common.Hash {
6969
prefix := headerKeyPrefix(number)
7070

7171
hashes := make([]common.Hash, 0, 1)
72-
it := db.NewIteratorWithPrefix(prefix)
72+
it := db.NewIterator(prefix, nil)
7373
defer it.Release()
7474

7575
for it.Next() {

core/rawdb/accessors_snapshot.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func DeleteStorageSnapshot(db ethdb.KeyValueWriter, accountHash, storageHash com
9393
// IterateStorageSnapshots returns an iterator for walking the entire storage
9494
// space of a specific account.
9595
func IterateStorageSnapshots(db ethdb.Iteratee, accountHash common.Hash) ethdb.Iterator {
96-
return db.NewIteratorWithPrefix(storageSnapshotsKey(accountHash))
96+
return db.NewIterator(storageSnapshotsKey(accountHash), nil)
9797
}
9898

9999
// ReadSnapshotJournal retrieves the serialized in-memory diff layers saved at

core/rawdb/database.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ func NewLevelDBDatabaseWithFreezer(file string, cache int, handles int, freezer
221221
// InspectDatabase traverses the entire database and checks the size
222222
// of all different categories of data.
223223
func InspectDatabase(db ethdb.Database) error {
224-
it := db.NewIterator()
224+
it := db.NewIterator(nil, nil)
225225
defer it.Release()
226226

227227
var (

core/rawdb/table.go

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -103,27 +103,12 @@ func (t *table) Delete(key []byte) error {
103103
return t.db.Delete(append([]byte(t.prefix), key...))
104104
}
105105

106-
// NewIterator creates a binary-alphabetical iterator over the entire keyspace
107-
// contained within the database.
108-
func (t *table) NewIterator() ethdb.Iterator {
109-
return t.NewIteratorWithPrefix(nil)
110-
}
111-
112-
// NewIteratorWithStart creates a binary-alphabetical iterator over a subset of
113-
// database content starting at a particular initial key (or after, if it does
114-
// not exist).
115-
func (t *table) NewIteratorWithStart(start []byte) ethdb.Iterator {
116-
iter := t.db.NewIteratorWithStart(append([]byte(t.prefix), start...))
117-
return &tableIterator{
118-
iter: iter,
119-
prefix: t.prefix,
120-
}
121-
}
122-
123-
// NewIteratorWithPrefix creates a binary-alphabetical iterator over a subset
124-
// of database content with a particular key prefix.
125-
func (t *table) NewIteratorWithPrefix(prefix []byte) ethdb.Iterator {
126-
iter := t.db.NewIteratorWithPrefix(append([]byte(t.prefix), prefix...))
106+
// NewIterator creates a binary-alphabetical iterator over a subset
107+
// of database content with a particular key prefix, starting at a particular
108+
// initial key (or after, if it does not exist).
109+
func (t *table) NewIterator(prefix []byte, start []byte) ethdb.Iterator {
110+
innerPrefix := append([]byte(t.prefix), prefix...)
111+
iter := t.db.NewIterator(innerPrefix, start)
127112
return &tableIterator{
128113
iter: iter,
129114
prefix: t.prefix,

core/rawdb/table_test.go

Lines changed: 24 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package rawdb
1919
import (
2020
"bytes"
2121
"testing"
22+
23+
"github.com/ethereum/go-ethereum/ethdb"
2224
)
2325

2426
func TestTableDatabase(t *testing.T) { testTableDatabase(t, "prefix") }
@@ -96,48 +98,31 @@ func testTableDatabase(t *testing.T, prefix string) {
9698
}
9799
}
98100

99-
// Test iterators
100-
iter := db.NewIterator()
101-
var index int
102-
for iter.Next() {
103-
key, value := iter.Key(), iter.Value()
104-
if !bytes.Equal(key, entries[index].key) {
105-
t.Fatalf("Key mismatch: want=%v, got=%v", entries[index].key, key)
101+
check := func(iter ethdb.Iterator, expCount, index int) {
102+
count := 0
103+
for iter.Next() {
104+
key, value := iter.Key(), iter.Value()
105+
if !bytes.Equal(key, entries[index].key) {
106+
t.Fatalf("Key mismatch: want=%v, got=%v", entries[index].key, key)
107+
}
108+
if !bytes.Equal(value, entries[index].value) {
109+
t.Fatalf("Value mismatch: want=%v, got=%v", entries[index].value, value)
110+
}
111+
index += 1
112+
count++
106113
}
107-
if !bytes.Equal(value, entries[index].value) {
108-
t.Fatalf("Value mismatch: want=%v, got=%v", entries[index].value, value)
114+
if count != expCount {
115+
t.Fatalf("Wrong number of elems, exp %d got %d", expCount, count)
109116
}
110-
index += 1
117+
iter.Release()
111118
}
112-
iter.Release()
113-
119+
// Test iterators
120+
check(db.NewIterator(nil, nil), 6, 0)
114121
// Test iterators with prefix
115-
iter = db.NewIteratorWithPrefix([]byte{0xff, 0xff})
116-
index = 3
117-
for iter.Next() {
118-
key, value := iter.Key(), iter.Value()
119-
if !bytes.Equal(key, entries[index].key) {
120-
t.Fatalf("Key mismatch: want=%v, got=%v", entries[index].key, key)
121-
}
122-
if !bytes.Equal(value, entries[index].value) {
123-
t.Fatalf("Value mismatch: want=%v, got=%v", entries[index].value, value)
124-
}
125-
index += 1
126-
}
127-
iter.Release()
128-
122+
check(db.NewIterator([]byte{0xff, 0xff}, nil), 3, 3)
129123
// Test iterators with start point
130-
iter = db.NewIteratorWithStart([]byte{0xff, 0xff, 0x02})
131-
index = 4
132-
for iter.Next() {
133-
key, value := iter.Key(), iter.Value()
134-
if !bytes.Equal(key, entries[index].key) {
135-
t.Fatalf("Key mismatch: want=%v, got=%v", entries[index].key, key)
136-
}
137-
if !bytes.Equal(value, entries[index].value) {
138-
t.Fatalf("Value mismatch: want=%v, got=%v", entries[index].value, value)
139-
}
140-
index += 1
141-
}
142-
iter.Release()
124+
check(db.NewIterator(nil, []byte{0xff, 0xff, 0x02}), 2, 4)
125+
// Test iterators with prefix and start point
126+
check(db.NewIterator([]byte{0xee}, nil), 0, 0)
127+
check(db.NewIterator(nil, []byte{0x00}), 6, 0)
143128
}

core/state/iterator_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func TestNodeIteratorCoverage(t *testing.T) {
5151
t.Errorf("state entry not reported %x", hash)
5252
}
5353
}
54-
it := db.TrieDB().DiskDB().(ethdb.Database).NewIterator()
54+
it := db.TrieDB().DiskDB().(ethdb.Database).NewIterator(nil, nil)
5555
for it.Next() {
5656
key := it.Key()
5757
if bytes.HasPrefix(key, []byte("secure-key-")) {

core/state/snapshot/disklayer_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@ package snapshot
1818

1919
import (
2020
"bytes"
21+
"io/ioutil"
22+
"os"
2123
"testing"
2224

2325
"github.com/VictoriaMetrics/fastcache"
2426
"github.com/ethereum/go-ethereum/common"
2527
"github.com/ethereum/go-ethereum/core/rawdb"
28+
"github.com/ethereum/go-ethereum/ethdb"
29+
"github.com/ethereum/go-ethereum/ethdb/leveldb"
2630
"github.com/ethereum/go-ethereum/ethdb/memorydb"
2731
)
2832

@@ -432,4 +436,76 @@ func TestDiskPartialMerge(t *testing.T) {
432436
// This test case is a tiny specialized case of TestDiskPartialMerge, which tests
433437
// some very specific cornercases that random tests won't ever trigger.
434438
func TestDiskMidAccountPartialMerge(t *testing.T) {
439+
// TODO(@karalabe) ?
440+
}
441+
442+
// TestDiskSeek tests that seek-operations work on the disk layer
443+
func TestDiskSeek(t *testing.T) {
444+
// Create some accounts in the disk layer
445+
var db ethdb.Database
446+
447+
if dir, err := ioutil.TempDir("", "disklayer-test"); err != nil {
448+
t.Fatal(err)
449+
} else {
450+
defer os.RemoveAll(dir)
451+
diskdb, err := leveldb.New(dir, 256, 0, "")
452+
if err != nil {
453+
t.Fatal(err)
454+
}
455+
db = rawdb.NewDatabase(diskdb)
456+
}
457+
// Fill even keys [0,2,4...]
458+
for i := 0; i < 0xff; i += 2 {
459+
acc := common.Hash{byte(i)}
460+
rawdb.WriteAccountSnapshot(db, acc, acc[:])
461+
}
462+
// Add an 'higher' key, with incorrect (higher) prefix
463+
highKey := []byte{rawdb.SnapshotAccountPrefix[0] + 1}
464+
db.Put(highKey, []byte{0xff, 0xff})
465+
466+
baseRoot := randomHash()
467+
rawdb.WriteSnapshotRoot(db, baseRoot)
468+
469+
snaps := &Tree{
470+
layers: map[common.Hash]snapshot{
471+
baseRoot: &diskLayer{
472+
diskdb: db,
473+
cache: fastcache.New(500 * 1024),
474+
root: baseRoot,
475+
},
476+
},
477+
}
478+
// Test some different seek positions
479+
type testcase struct {
480+
pos byte
481+
expkey byte
482+
}
483+
var cases = []testcase{
484+
{0xff, 0x55}, // this should exit immediately without checking key
485+
{0x01, 0x02},
486+
{0xfe, 0xfe},
487+
{0xfd, 0xfe},
488+
{0x00, 0x00},
489+
}
490+
for i, tc := range cases {
491+
it, err := snaps.AccountIterator(baseRoot, common.Hash{tc.pos})
492+
if err != nil {
493+
t.Fatalf("case %d, error: %v", i, err)
494+
}
495+
count := 0
496+
for it.Next() {
497+
k, v, err := it.Hash()[0], it.Account()[0], it.Error()
498+
if err != nil {
499+
t.Fatalf("test %d, item %d, error: %v", i, count, err)
500+
}
501+
// First item in iterator should have the expected key
502+
if count == 0 && k != tc.expkey {
503+
t.Fatalf("test %d, item %d, got %v exp %v", i, count, k, tc.expkey)
504+
}
505+
count++
506+
if v != k {
507+
t.Fatalf("test %d, item %d, value wrong, got %v exp %v", i, count, v, k)
508+
}
509+
}
510+
}
435511
}

0 commit comments

Comments
 (0)