Skip to content

Commit 936b2f7

Browse files
pdrobnjakclaude
andcommitted
refactor(sei-db): encapsulate ReadOptions lifecycle in iterator constructors
Push ReadOptions creation into the iterator constructors so callers never handle ReadOptions directly: - newIterator() creates a versioned iterator with internally managed ReadOptions, used by Iterator() and ReverseIterator() - newRangeIterator() creates a timestamp-range iterator, used by RawIterate() - newTSReadOptions() now returns a cleanup closure alongside the ReadOptions, making it harder to forget Destroy() for non-iterator uses (getSlice) - The iterator stores a cleanup func() instead of *ReadOptions, called on Close() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8f3bc86 commit 936b2f7

File tree

2 files changed

+60
-46
lines changed

2 files changed

+60
-46
lines changed

sei-db/db_engine/rocksdb/mvcc/db.go

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ func OpenDB(dataDir string, config config.StateStoreConfig) (*Database, error) {
127127
}
128128

129129
func (db *Database) getSlice(storeKey string, version int64, key []byte) (*grocksdb.Slice, error) {
130-
readOpts := newTSReadOptions(version)
131-
defer readOpts.Destroy()
130+
readOpts, cleanup := newTSReadOptions(version)
131+
defer cleanup()
132132
return db.storage.GetCF(
133133
readOpts,
134134
db.cfHandle,
@@ -324,9 +324,7 @@ func (db *Database) Iterator(storeKey string, version int64, start, end []byte)
324324
prefix := storePrefix(storeKey)
325325
start, end = util.IterateWithPrefix(prefix, start, end)
326326

327-
readOpts := newTSReadOptions(version)
328-
itr := db.storage.NewIteratorCF(readOpts, db.cfHandle)
329-
return NewRocksDBIterator(itr, readOpts, prefix, start, end, version, db.earliestVersion, false), nil
327+
return newIterator(db.storage, db.cfHandle, prefix, start, end, version, db.earliestVersion, false), nil
330328
}
331329

332330
func (db *Database) ReverseIterator(storeKey string, version int64, start, end []byte) (types.DBIterator, error) {
@@ -341,9 +339,7 @@ func (db *Database) ReverseIterator(storeKey string, version int64, start, end [
341339
prefix := storePrefix(storeKey)
342340
start, end = util.IterateWithPrefix(prefix, start, end)
343341

344-
readOpts := newTSReadOptions(version)
345-
itr := db.storage.NewIteratorCF(readOpts, db.cfHandle)
346-
return NewRocksDBIterator(itr, readOpts, prefix, start, end, version, db.earliestVersion, true), nil
342+
return newIterator(db.storage, db.cfHandle, prefix, start, end, version, db.earliestVersion, true), nil
347343
}
348344

349345
// Import loads the initial version of the state in parallel with numWorkers goroutines
@@ -404,25 +400,13 @@ func (db *Database) RawIterate(storeKey string, fn func(key []byte, value []byte
404400
return false, err
405401
}
406402

407-
var startTs [TimestampSize]byte
408-
binary.LittleEndian.PutUint64(startTs[:], uint64(0))
409-
410-
var endTs [TimestampSize]byte
411-
binary.LittleEndian.PutUint64(endTs[:], uint64(latestVersion))
412-
413-
// Set timestamp lower and upper bound to iterate over all keys in db
414-
readOpts := grocksdb.NewDefaultReadOptions()
415-
readOpts.SetIterStartTimestamp(startTs[:])
416-
readOpts.SetTimestamp(endTs[:])
417-
418-
itr := db.storage.NewIteratorCF(readOpts, db.cfHandle)
419-
rocksItr := NewRocksDBIterator(itr, readOpts, prefix, start, end, latestVersion, 1, false)
403+
rocksItr := newRangeIterator(db.storage, db.cfHandle, prefix, start, end, 0, latestVersion, 1)
420404
defer func() { _ = rocksItr.Close() }()
421405

422406
for rocksItr.Valid() {
423407
key := rocksItr.Key()
424408
value := rocksItr.Value()
425-
version := int64(binary.LittleEndian.Uint64(itr.Timestamp().Data()))
409+
version := int64(binary.LittleEndian.Uint64(rocksItr.Timestamp().Data()))
426410

427411
// Call callback fn
428412
if fn(key, value, version) {
@@ -443,15 +427,17 @@ func (db *Database) GetLatestMigratedModule() (string, error) {
443427
panic("not implemented")
444428
}
445429

446-
// newTSReadOptions returns ReadOptions used in the RocksDB column family read.
447-
func newTSReadOptions(version int64) *grocksdb.ReadOptions {
430+
// newTSReadOptions returns ReadOptions used in the RocksDB column family read
431+
// and a cleanup function that destroys them. The caller must ensure cleanup is
432+
// called when the ReadOptions are no longer needed.
433+
func newTSReadOptions(version int64) (*grocksdb.ReadOptions, func()) {
448434
var ts [TimestampSize]byte
449435
binary.LittleEndian.PutUint64(ts[:], uint64(version))
450436

451437
readOpts := grocksdb.NewDefaultReadOptions()
452438
readOpts.SetTimestamp(ts[:])
453439

454-
return readOpts
440+
return readOpts, func() { readOpts.Destroy() }
455441
}
456442

457443
func storePrefix(storeKey string) []byte {

sei-db/db_engine/rocksdb/mvcc/iterator.go

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package mvcc
55

66
import (
77
"bytes"
8+
"encoding/binary"
89

910
"github.com/linxGnu/grocksdb"
1011
"github.com/sei-protocol/sei-chain/sei-db/state_db/ss/types"
@@ -14,25 +15,48 @@ var _ types.DBIterator = (*iterator)(nil)
1415

1516
type iterator struct {
1617
source *grocksdb.Iterator
17-
readOpts *grocksdb.ReadOptions
18+
cleanup func()
1819
prefix, start, end []byte
1920
version int64
2021
reverse bool
2122
invalid bool
2223
}
2324

24-
func NewRocksDBIterator(source *grocksdb.Iterator, readOpts *grocksdb.ReadOptions, prefix, start, end []byte, version int64, earliestVersion int64, reverse bool) *iterator {
25+
// newIterator creates a versioned iterator. ReadOptions are created and owned
26+
// internally — destroyed when Close() is called.
27+
func newIterator(storage *grocksdb.DB, cfHandle *grocksdb.ColumnFamilyHandle, prefix, start, end []byte, version int64, earliestVersion int64, reverse bool) *iterator {
28+
readOpts, cleanup := newTSReadOptions(version)
29+
source := storage.NewIteratorCF(readOpts, cfHandle)
30+
return buildIterator(source, cleanup, prefix, start, end, version, earliestVersion, reverse)
31+
}
32+
33+
// newRangeIterator creates an iterator over a version range. ReadOptions are
34+
// created and owned internally — destroyed when Close() is called.
35+
func newRangeIterator(storage *grocksdb.DB, cfHandle *grocksdb.ColumnFamilyHandle, prefix, start, end []byte, startVersion, endVersion int64, earliestVersion int64) *iterator {
36+
readOpts := grocksdb.NewDefaultReadOptions()
37+
var startTs [TimestampSize]byte
38+
binary.LittleEndian.PutUint64(startTs[:], uint64(startVersion))
39+
var endTs [TimestampSize]byte
40+
binary.LittleEndian.PutUint64(endTs[:], uint64(endVersion))
41+
readOpts.SetIterStartTimestamp(startTs[:])
42+
readOpts.SetTimestamp(endTs[:])
43+
44+
source := storage.NewIteratorCF(readOpts, cfHandle)
45+
return buildIterator(source, func() { readOpts.Destroy() }, prefix, start, end, endVersion, earliestVersion, false)
46+
}
47+
48+
func buildIterator(source *grocksdb.Iterator, cleanup func(), prefix, start, end []byte, version int64, earliestVersion int64, reverse bool) *iterator {
2549
// Return invalid iterator if requested iterator height is lower than earliest version after pruning
2650
if version < earliestVersion {
2751
return &iterator{
28-
source: source,
29-
readOpts: readOpts,
30-
prefix: prefix,
31-
start: start,
32-
end: end,
33-
version: version,
34-
reverse: reverse,
35-
invalid: true,
52+
source: source,
53+
cleanup: cleanup,
54+
prefix: prefix,
55+
start: start,
56+
end: end,
57+
version: version,
58+
reverse: reverse,
59+
invalid: true,
3660
}
3761
}
3862

@@ -60,14 +84,14 @@ func NewRocksDBIterator(source *grocksdb.Iterator, readOpts *grocksdb.ReadOption
6084
}
6185

6286
return &iterator{
63-
source: source,
64-
readOpts: readOpts,
65-
prefix: prefix,
66-
start: start,
67-
end: end,
68-
version: version,
69-
reverse: reverse,
70-
invalid: !source.Valid(),
87+
source: source,
88+
cleanup: cleanup,
89+
prefix: prefix,
90+
start: start,
91+
end: end,
92+
version: version,
93+
reverse: reverse,
94+
invalid: !source.Valid(),
7195
}
7296
}
7397

@@ -141,6 +165,10 @@ func (itr *iterator) Value() []byte {
141165
return copyAndFreeSlice(itr.source.Value())
142166
}
143167

168+
func (itr *iterator) Timestamp() *grocksdb.Slice {
169+
return itr.source.Timestamp()
170+
}
171+
144172
func (itr iterator) Next() {
145173
if itr.invalid {
146174
return
@@ -160,9 +188,9 @@ func (itr *iterator) Error() error {
160188
func (itr *iterator) Close() error {
161189
itr.source.Close()
162190
itr.source = nil
163-
if itr.readOpts != nil {
164-
itr.readOpts.Destroy()
165-
itr.readOpts = nil
191+
if itr.cleanup != nil {
192+
itr.cleanup()
193+
itr.cleanup = nil
166194
}
167195
itr.invalid = true
168196
return nil

0 commit comments

Comments
 (0)