Skip to content

Commit b1ad1e9

Browse files
committed
Add panics if an unclosed iterator is found. Or, if multiple iterators are run in one transaction.
1 parent 3340933 commit b1ad1e9

File tree

7 files changed

+35
-3
lines changed

7 files changed

+35
-3
lines changed

backup.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ func (db *DB) Backup(w io.Writer, since uint64) (uint64, error) {
5353
opts := DefaultIteratorOptions
5454
opts.AllVersions = true
5555
it := txn.NewIterator(opts)
56+
defer it.Close()
57+
5658
for it.Rewind(); it.Valid(); it.Next() {
5759
item := it.Item()
5860
if item.Version() < since {

backup_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ func TestDumpLoad(t *testing.T) {
9191
opts := DefaultIteratorOptions
9292
opts.AllVersions = true
9393
it := txn.NewIterator(opts)
94+
defer it.Close()
9495
var count int
9596
for it.Rewind(); it.Valid(); it.Next() {
9697
item := it.Item()

db.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,8 @@ func (op *MergeOperator) iterateAndMerge(txn *Txn) (val []byte, err error) {
11211121
opt := DefaultIteratorOptions
11221122
opt.AllVersions = true
11231123
it := txn.NewIterator(opt)
1124+
defer it.Close()
1125+
11241126
var numVersions int
11251127
for it.Rewind(); it.ValidForPrefix(op.key); it.Next() {
11261128
item := it.Item()

db_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,7 @@ func TestDiscardVersionsBelow(t *testing.T) {
862862
// Verify that there are 4 versions, and record 3rd version (2nd from top in iteration)
863863
db.View(func(txn *Txn) error {
864864
it := txn.NewIterator(opts)
865+
defer it.Close()
865866
var count int
866867
for it.Rewind(); it.Valid(); it.Next() {
867868
count++
@@ -885,6 +886,7 @@ func TestDiscardVersionsBelow(t *testing.T) {
885886
// below ts have been deleted.
886887
db.View(func(txn *Txn) error {
887888
it := txn.NewIterator(opts)
889+
defer it.Close()
888890
var count int
889891
for it.Rewind(); it.Valid(); it.Next() {
890892
count++
@@ -931,6 +933,7 @@ func TestExpiry(t *testing.T) {
931933
opts.PrefetchValues = false
932934
err = db.View(func(txn *Txn) error {
933935
it := txn.NewIterator(opts)
936+
defer it.Close()
934937
var count int
935938
for it.Rewind(); it.Valid(); it.Next() {
936939
count++
@@ -1099,6 +1102,7 @@ func TestWriteDeadlock(t *testing.T) {
10991102
opt := DefaultIteratorOptions
11001103
opt.PrefetchValues = false
11011104
it := txn.NewIterator(opt)
1105+
defer it.Close()
11021106
for it.Rewind(); it.Valid(); it.Next() {
11031107
item := it.Item()
11041108

@@ -1558,6 +1562,7 @@ func ExampleTxn_NewIterator() {
15581562
var count int
15591563
err = db.View(func(txn *Txn) error {
15601564
it := txn.NewIterator(opt)
1565+
defer it.Close()
15611566
for it.Rewind(); it.Valid(); it.Next() {
15621567
count++
15631568
}

iterator.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"bytes"
2121
"fmt"
2222
"sync"
23+
"sync/atomic"
2324
"time"
2425

2526
"github.com/dgraph-io/badger/options"
@@ -316,6 +317,10 @@ type Iterator struct {
316317
// Using prefetch is highly recommended if you're doing a long running iteration.
317318
// Avoid long running iterations in update transactions.
318319
func (txn *Txn) NewIterator(opt IteratorOptions) *Iterator {
320+
if atomic.AddInt32(&txn.numIterators, 1) > 1 {
321+
panic("Only one iterator can be active at one time.")
322+
}
323+
319324
tables, decr := txn.db.getMemTables()
320325
defer decr()
321326
txn.db.vlog.incrIteratorCount()
@@ -382,6 +387,7 @@ func (it *Iterator) Close() {
382387

383388
// TODO: We could handle this error.
384389
_ = it.txn.db.vlog.decrIteratorCount()
390+
atomic.AddInt32(&it.txn.numIterators, -1)
385391
}
386392

387393
// Next would advance the iterator by one. Always check it.Valid() after a Next()

transaction.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,9 @@ type Txn struct {
149149
callbacks []func()
150150
discarded bool
151151

152-
size int64
153-
count int64
152+
size int64
153+
count int64
154+
numIterators int32
154155
}
155156

156157
type pendingWritesIterator struct {
@@ -423,10 +424,12 @@ func (txn *Txn) Discard() {
423424
if txn.discarded { // Avoid a re-run.
424425
return
425426
}
427+
if atomic.LoadInt32(&txn.numIterators) > 0 {
428+
panic("Unclosed iterator at time of Txn.Discard.")
429+
}
426430
txn.discarded = true
427431
txn.db.orc.readMark.Done(txn.readTs)
428432
txn.runCallbacks()
429-
430433
if txn.update {
431434
txn.db.orc.decrRef()
432435
}

transaction_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ func TestTxnVersions(t *testing.T) {
156156
}
157157

158158
checkIterator := func(itr *Iterator, i int) {
159+
defer itr.Close()
159160
count := 0
160161
for itr.Rewind(); itr.Valid(); itr.Next() {
161162
item := itr.Item()
@@ -225,12 +226,14 @@ func TestTxnVersions(t *testing.T) {
225226
opt.AllVersions = true
226227
itr = txn.NewIterator(opt)
227228
checkAllVersions(itr, i)
229+
itr.Close()
228230

229231
opt = DefaultIteratorOptions
230232
opt.AllVersions = true
231233
opt.Reverse = true
232234
itr = txn.NewIterator(opt)
233235
checkAllVersions(itr, i)
236+
itr.Close()
234237

235238
txn.Discard()
236239
}
@@ -352,6 +355,7 @@ func TestTxnIterationEdgeCase(t *testing.T) {
352355
require.Equal(t, uint64(4), db.orc.readTs())
353356

354357
checkIterator := func(itr *Iterator, expected []string) {
358+
defer itr.Close()
355359
var i int
356360
for itr.Rewind(); itr.Valid(); itr.Next() {
357361
item := itr.Item()
@@ -435,6 +439,7 @@ func TestTxnIterationEdgeCase2(t *testing.T) {
435439
require.Equal(t, uint64(4), db.orc.readTs())
436440

437441
checkIterator := func(itr *Iterator, expected []string) {
442+
defer itr.Close()
438443
var i int
439444
for itr.Rewind(); itr.Valid(); itr.Next() {
440445
item := itr.Item()
@@ -463,6 +468,7 @@ func TestTxnIterationEdgeCase2(t *testing.T) {
463468
itr.Seek(kc)
464469
require.True(t, itr.Valid())
465470
require.Equal(t, itr.item.Key(), kc)
471+
itr.Close()
466472

467473
itr = txn.NewIterator(rev)
468474
itr.Seek(ka)
@@ -471,6 +477,7 @@ func TestTxnIterationEdgeCase2(t *testing.T) {
471477
itr.Seek(kc)
472478
require.True(t, itr.Valid())
473479
require.Equal(t, itr.item.Key(), kc)
480+
itr.Close()
474481

475482
txn.readTs = 3
476483
itr = txn.NewIterator(DefaultIteratorOptions)
@@ -537,6 +544,7 @@ func TestTxnIterationEdgeCase3(t *testing.T) {
537544
itr.Seek([]byte("ac"))
538545
require.True(t, itr.Valid())
539546
require.Equal(t, itr.item.Key(), kc)
547+
itr.Close()
540548

541549
// Keys: "abc", "ade"
542550
// Read pending writes.
@@ -558,6 +566,7 @@ func TestTxnIterationEdgeCase3(t *testing.T) {
558566
itr.Seek([]byte("ad"))
559567
require.True(t, itr.Valid())
560568
require.Equal(t, itr.item.Key(), kd)
569+
itr.Close()
561570

562571
itr = txn.NewIterator(rev)
563572
itr.Seek([]byte("ac"))
@@ -576,6 +585,7 @@ func TestTxnIterationEdgeCase3(t *testing.T) {
576585
itr.Seek([]byte("ad"))
577586
require.True(t, itr.Valid())
578587
require.Equal(t, itr.item.Key(), kc)
588+
itr.Close()
579589

580590
// Keys: "abc", "ade"
581591
itr = txn2.NewIterator(rev)
@@ -595,6 +605,7 @@ func TestTxnIterationEdgeCase3(t *testing.T) {
595605
itr.Seek([]byte("ac"))
596606
require.True(t, itr.Valid())
597607
require.Equal(t, itr.item.Key(), kb)
608+
itr.Close()
598609
})
599610
}
600611

@@ -630,6 +641,7 @@ func TestIteratorAllVersionsWithDeleted(t *testing.T) {
630641
// Verify that deleted shows up when AllVersions is set.
631642
err = db.View(func(txn *Txn) error {
632643
it := txn.NewIterator(opts)
644+
defer it.Close()
633645
var count int
634646
for it.Rewind(); it.Valid(); it.Next() {
635647
count++
@@ -670,6 +682,7 @@ func TestIteratorAllVersionsWithDeleted2(t *testing.T) {
670682
// Verify that deleted shows up when AllVersions is set.
671683
err := db.View(func(txn *Txn) error {
672684
it := txn.NewIterator(opts)
685+
defer it.Close()
673686
var count int
674687
for it.Rewind(); it.Valid(); it.Next() {
675688
item := it.Item()

0 commit comments

Comments
 (0)