Skip to content

Commit 8f3bc86

Browse files
pdrobnjakclaude
andcommitted
fix(sei-db): retain ReadOptions lifetime for RocksDB iterators
The ReadOptions (and its timestamp slice) created by newTSReadOptions() was not stored after being passed to NewIteratorCF(), making it eligible for GC while the C++ iterator still held a dangling Slice pointer to the timestamp data. Under parallel access this caused iterators to read with corrupted timestamps, returning values from wrong versions. Store ReadOptions in the iterator struct and Destroy() it on Close(). Also fix the same leak in getSlice() used by Get/Has. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2a1ba1f commit 8f3bc86

2 files changed

Lines changed: 32 additions & 22 deletions

File tree

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,10 @@ 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()
130132
return db.storage.GetCF(
131-
newTSReadOptions(version),
133+
readOpts,
132134
db.cfHandle,
133135
prependStoreKey(storeKey, key),
134136
)
@@ -322,8 +324,9 @@ func (db *Database) Iterator(storeKey string, version int64, start, end []byte)
322324
prefix := storePrefix(storeKey)
323325
start, end = util.IterateWithPrefix(prefix, start, end)
324326

325-
itr := db.storage.NewIteratorCF(newTSReadOptions(version), db.cfHandle)
326-
return NewRocksDBIterator(itr, prefix, start, end, version, db.earliestVersion, false), nil
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
327330
}
328331

329332
func (db *Database) ReverseIterator(storeKey string, version int64, start, end []byte) (types.DBIterator, error) {
@@ -338,8 +341,9 @@ func (db *Database) ReverseIterator(storeKey string, version int64, start, end [
338341
prefix := storePrefix(storeKey)
339342
start, end = util.IterateWithPrefix(prefix, start, end)
340343

341-
itr := db.storage.NewIteratorCF(newTSReadOptions(version), db.cfHandle)
342-
return NewRocksDBIterator(itr, prefix, start, end, version, db.earliestVersion, true), nil
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
343347
}
344348

345349
// Import loads the initial version of the state in parallel with numWorkers goroutines
@@ -410,10 +414,9 @@ func (db *Database) RawIterate(storeKey string, fn func(key []byte, value []byte
410414
readOpts := grocksdb.NewDefaultReadOptions()
411415
readOpts.SetIterStartTimestamp(startTs[:])
412416
readOpts.SetTimestamp(endTs[:])
413-
defer readOpts.Destroy()
414417

415418
itr := db.storage.NewIteratorCF(readOpts, db.cfHandle)
416-
rocksItr := NewRocksDBIterator(itr, prefix, start, end, latestVersion, 1, false)
419+
rocksItr := NewRocksDBIterator(itr, readOpts, prefix, start, end, latestVersion, 1, false)
417420
defer func() { _ = rocksItr.Close() }()
418421

419422
for rocksItr.Valid() {

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

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,25 @@ var _ types.DBIterator = (*iterator)(nil)
1414

1515
type iterator struct {
1616
source *grocksdb.Iterator
17+
readOpts *grocksdb.ReadOptions
1718
prefix, start, end []byte
1819
version int64
1920
reverse bool
2021
invalid bool
2122
}
2223

23-
func NewRocksDBIterator(source *grocksdb.Iterator, prefix, start, end []byte, version int64, earliestVersion int64, reverse bool) *iterator {
24+
func NewRocksDBIterator(source *grocksdb.Iterator, readOpts *grocksdb.ReadOptions, prefix, start, end []byte, version int64, earliestVersion int64, reverse bool) *iterator {
2425
// Return invalid iterator if requested iterator height is lower than earliest version after pruning
2526
if version < earliestVersion {
2627
return &iterator{
27-
source: source,
28-
prefix: prefix,
29-
start: start,
30-
end: end,
31-
version: version,
32-
reverse: reverse,
33-
invalid: true,
28+
source: source,
29+
readOpts: readOpts,
30+
prefix: prefix,
31+
start: start,
32+
end: end,
33+
version: version,
34+
reverse: reverse,
35+
invalid: true,
3436
}
3537
}
3638

@@ -58,13 +60,14 @@ func NewRocksDBIterator(source *grocksdb.Iterator, prefix, start, end []byte, ve
5860
}
5961

6062
return &iterator{
61-
source: source,
62-
prefix: prefix,
63-
start: start,
64-
end: end,
65-
version: version,
66-
reverse: reverse,
67-
invalid: !source.Valid(),
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(),
6871
}
6972
}
7073

@@ -157,6 +160,10 @@ func (itr *iterator) Error() error {
157160
func (itr *iterator) Close() error {
158161
itr.source.Close()
159162
itr.source = nil
163+
if itr.readOpts != nil {
164+
itr.readOpts.Destroy()
165+
itr.readOpts = nil
166+
}
160167
itr.invalid = true
161168
return nil
162169
}

0 commit comments

Comments
 (0)