Skip to content

ethdb: Implement DeleteRange in batch #31947

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

Merged
merged 6 commits into from
Jun 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions core/rawdb/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ func (t *table) Delete(key []byte) error {
// DeleteRange deletes all of the keys (and values) in the range [start,end)
// (inclusive on start, exclusive on end).
func (t *table) DeleteRange(start, end []byte) error {
// The nilness will be lost by adding the prefix, explicitly converting it
// to a special flag representing the end of key range.
if end == nil {
end = ethdb.MaximumKey
}
return t.db.DeleteRange(append([]byte(t.prefix), start...), append([]byte(t.prefix), end...))
}

Expand Down Expand Up @@ -223,6 +228,16 @@ func (b *tableBatch) Delete(key []byte) error {
return b.batch.Delete(append([]byte(b.prefix), key...))
}

// DeleteRange removes all keys in the range [start, end) from the batch for later committing.
func (b *tableBatch) DeleteRange(start, end []byte) error {
// The nilness will be lost by adding the prefix, explicitly converting it
// to a special flag representing the end of key range.
if end == nil {
end = ethdb.MaximumKey
}
return b.batch.DeleteRange(append([]byte(b.prefix), start...), append([]byte(b.prefix), end...))
}

// ValueSize retrieves the amount of data queued up for writing.
func (b *tableBatch) ValueSize() int {
return b.batch.ValueSize()
Expand Down
24 changes: 24 additions & 0 deletions core/rawdb/table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,28 @@ func testTableDatabase(t *testing.T, prefix string) {
// Test iterators with prefix and start point
check(db.NewIterator([]byte{0xee}, nil), 0, 0)
check(db.NewIterator(nil, []byte{0x00}), 6, 0)

// Test range deletion
db.DeleteRange(nil, nil)
for _, entry := range entries {
_, err := db.Get(entry.key)
if err == nil {
t.Fatal("Unexpected item after deletion")
}
}
// Test range deletion by batch
batch = db.NewBatch()
for _, entry := range entries {
batch.Put(entry.key, entry.value)
}
batch.Write()
batch.Reset()
batch.DeleteRange(nil, nil)
batch.Write()
for _, entry := range entries {
_, err := db.Get(entry.key)
if err == nil {
t.Fatal("Unexpected item after deletion")
}
}
}
14 changes: 12 additions & 2 deletions ethdb/batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const IdealBatchSize = 100 * 1024
// when Write is called. A batch cannot be used concurrently.
type Batch interface {
KeyValueWriter
KeyValueRangeDeleter

// ValueSize retrieves the amount of data queued up for writing.
ValueSize() int
Expand Down Expand Up @@ -53,8 +54,9 @@ type Batcher interface {
type HookedBatch struct {
Batch

OnPut func(key []byte, value []byte) // Callback if a key is inserted
OnDelete func(key []byte) // Callback if a key is deleted
OnPut func(key []byte, value []byte) // Callback if a key is inserted
OnDelete func(key []byte) // Callback if a key is deleted
OnDeleteRange func(start, end []byte) // Callback if a range of keys is deleted
}

// Put inserts the given value into the key-value data store.
Expand All @@ -72,3 +74,11 @@ func (b HookedBatch) Delete(key []byte) error {
}
return b.Batch.Delete(key)
}

// DeleteRange removes all keys in the range [start, end) from the key-value data store.
func (b HookedBatch) DeleteRange(start, end []byte) error {
if b.OnDeleteRange != nil {
b.OnDeleteRange(start, end)
}
return b.Batch.DeleteRange(start, end)
}
18 changes: 18 additions & 0 deletions ethdb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,23 @@
package ethdb

import (
"bytes"
"errors"
"io"
)

var (
// MaximumKey is a special marker representing the largest possible key
// in the database.
//
// All prefixed database entries will be smaller than this marker.
// For trie nodes in hash mode, we use a 32-byte slice filled with 0xFF
// because there may be shared prefixes starting with multiple 0xFF bytes.
// Using 32 bytes ensures that only a hash collision could potentially
// match or exceed it.
MaximumKey = bytes.Repeat([]byte{0xff}, 32)
)

// KeyValueReader wraps the Has and Get method of a backing data store.
type KeyValueReader interface {
// Has retrieves if a key is present in the key-value data store.
Expand All @@ -46,6 +59,11 @@ var ErrTooManyKeys = errors.New("too many keys in deleted range")
type KeyValueRangeDeleter interface {
// DeleteRange deletes all of the keys (and values) in the range [start,end)
// (inclusive on start, exclusive on end).
//
// A nil start is treated as a key before all keys in the data store; a nil
// end is treated as a key after all keys in the data store. If both is nil
// then the entire data store will be purged.
//
// Some implementations of DeleteRange may return ErrTooManyKeys after
// partially deleting entries in the given range.
DeleteRange(start, end []byte) error
Expand Down
Loading