Skip to content

Commit 6283391

Browse files
authored
core/rawdb: improve table database (#20703)
This PR fixes issues in TableDatabase. TableDatabase is a wrapper of underlying ethdb.Database with an additional prefix. The prefix is applied to all entries it maintains. However when we try to retrieve entries from it we don't handle the key properly. In theory the prefix should be truncated and only user key is returned. But we don't do it in some cases, e.g. the iterator and batch replayer created from it. So this PR is the fix to these issues.
1 parent 5dd0cd1 commit 6283391

File tree

2 files changed

+216
-3
lines changed

2 files changed

+216
-3
lines changed

core/rawdb/table.go

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,21 @@ func (t *table) NewIterator() ethdb.Iterator {
113113
// database content starting at a particular initial key (or after, if it does
114114
// not exist).
115115
func (t *table) NewIteratorWithStart(start []byte) ethdb.Iterator {
116-
return t.db.NewIteratorWithStart(start)
116+
iter := t.db.NewIteratorWithStart(append([]byte(t.prefix), start...))
117+
return &tableIterator{
118+
iter: iter,
119+
prefix: t.prefix,
120+
}
117121
}
118122

119123
// NewIteratorWithPrefix creates a binary-alphabetical iterator over a subset
120124
// of database content with a particular key prefix.
121125
func (t *table) NewIteratorWithPrefix(prefix []byte) ethdb.Iterator {
122-
return t.db.NewIteratorWithPrefix(append([]byte(t.prefix), prefix...))
126+
iter := t.db.NewIteratorWithPrefix(append([]byte(t.prefix), prefix...))
127+
return &tableIterator{
128+
iter: iter,
129+
prefix: t.prefix,
130+
}
123131
}
124132

125133
// Stat returns a particular internal stat of the database.
@@ -198,7 +206,69 @@ func (b *tableBatch) Reset() {
198206
b.batch.Reset()
199207
}
200208

209+
// tableReplayer is a wrapper around a batch replayer which truncates
210+
// the added prefix.
211+
type tableReplayer struct {
212+
w ethdb.KeyValueWriter
213+
prefix string
214+
}
215+
216+
// Put implements the interface KeyValueWriter.
217+
func (r *tableReplayer) Put(key []byte, value []byte) error {
218+
trimmed := key[len(r.prefix):]
219+
return r.w.Put(trimmed, value)
220+
}
221+
222+
// Delete implements the interface KeyValueWriter.
223+
func (r *tableReplayer) Delete(key []byte) error {
224+
trimmed := key[len(r.prefix):]
225+
return r.w.Delete(trimmed)
226+
}
227+
201228
// Replay replays the batch contents.
202229
func (b *tableBatch) Replay(w ethdb.KeyValueWriter) error {
203-
return b.batch.Replay(w)
230+
return b.batch.Replay(&tableReplayer{w: w, prefix: b.prefix})
231+
}
232+
233+
// tableIterator is a wrapper around a database iterator that prefixes each key access
234+
// with a pre-configured string.
235+
type tableIterator struct {
236+
iter ethdb.Iterator
237+
prefix string
238+
}
239+
240+
// Next moves the iterator to the next key/value pair. It returns whether the
241+
// iterator is exhausted.
242+
func (iter *tableIterator) Next() bool {
243+
return iter.iter.Next()
244+
}
245+
246+
// Error returns any accumulated error. Exhausting all the key/value pairs
247+
// is not considered to be an error.
248+
func (iter *tableIterator) Error() error {
249+
return iter.iter.Error()
250+
}
251+
252+
// Key returns the key of the current key/value pair, or nil if done. The caller
253+
// should not modify the contents of the returned slice, and its contents may
254+
// change on the next call to Next.
255+
func (iter *tableIterator) Key() []byte {
256+
key := iter.iter.Key()
257+
if key == nil {
258+
return nil
259+
}
260+
return key[len(iter.prefix):]
261+
}
262+
263+
// Value returns the value of the current key/value pair, or nil if done. The
264+
// caller should not modify the contents of the returned slice, and its contents
265+
// may change on the next call to Next.
266+
func (iter *tableIterator) Value() []byte {
267+
return iter.iter.Value()
268+
}
269+
270+
// Release releases associated resources. Release should always succeed and can
271+
// be called multiple times without causing error.
272+
func (iter *tableIterator) Release() {
273+
iter.iter.Release()
204274
}

core/rawdb/table_test.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// Copyright 2020 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package rawdb
18+
19+
import (
20+
"bytes"
21+
"testing"
22+
)
23+
24+
func TestTableDatabase(t *testing.T) { testTableDatabase(t, "prefix") }
25+
func TestEmptyPrefixTableDatabase(t *testing.T) { testTableDatabase(t, "") }
26+
27+
type testReplayer struct {
28+
puts [][]byte
29+
dels [][]byte
30+
}
31+
32+
func (r *testReplayer) Put(key []byte, value []byte) error {
33+
r.puts = append(r.puts, key)
34+
return nil
35+
}
36+
37+
func (r *testReplayer) Delete(key []byte) error {
38+
r.dels = append(r.dels, key)
39+
return nil
40+
}
41+
42+
func testTableDatabase(t *testing.T, prefix string) {
43+
db := NewTable(NewMemoryDatabase(), prefix)
44+
45+
var entries = []struct {
46+
key []byte
47+
value []byte
48+
}{
49+
{[]byte{0x01, 0x02}, []byte{0x0a, 0x0b}},
50+
{[]byte{0x03, 0x04}, []byte{0x0c, 0x0d}},
51+
{[]byte{0x05, 0x06}, []byte{0x0e, 0x0f}},
52+
53+
{[]byte{0xff, 0xff, 0x01}, []byte{0x1a, 0x1b}},
54+
{[]byte{0xff, 0xff, 0x02}, []byte{0x1c, 0x1d}},
55+
{[]byte{0xff, 0xff, 0x03}, []byte{0x1e, 0x1f}},
56+
}
57+
58+
// Test Put/Get operation
59+
for _, entry := range entries {
60+
db.Put(entry.key, entry.value)
61+
}
62+
for _, entry := range entries {
63+
got, err := db.Get(entry.key)
64+
if err != nil {
65+
t.Fatalf("Failed to get value: %v", err)
66+
}
67+
if !bytes.Equal(got, entry.value) {
68+
t.Fatalf("Value mismatch: want=%v, got=%v", entry.value, got)
69+
}
70+
}
71+
72+
// Test batch operation
73+
db = NewTable(NewMemoryDatabase(), prefix)
74+
batch := db.NewBatch()
75+
for _, entry := range entries {
76+
batch.Put(entry.key, entry.value)
77+
}
78+
batch.Write()
79+
for _, entry := range entries {
80+
got, err := db.Get(entry.key)
81+
if err != nil {
82+
t.Fatalf("Failed to get value: %v", err)
83+
}
84+
if !bytes.Equal(got, entry.value) {
85+
t.Fatalf("Value mismatch: want=%v, got=%v", entry.value, got)
86+
}
87+
}
88+
89+
// Test batch replayer
90+
r := &testReplayer{}
91+
batch.Replay(r)
92+
for index, entry := range entries {
93+
got := r.puts[index]
94+
if !bytes.Equal(got, entry.key) {
95+
t.Fatalf("Key mismatch: want=%v, got=%v", entry.key, got)
96+
}
97+
}
98+
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)
106+
}
107+
if !bytes.Equal(value, entries[index].value) {
108+
t.Fatalf("Value mismatch: want=%v, got=%v", entries[index].value, value)
109+
}
110+
index += 1
111+
}
112+
iter.Release()
113+
114+
// 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+
129+
// 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()
143+
}

0 commit comments

Comments
 (0)