Skip to content

Commit e640a28

Browse files
committed
db: fix unpropagated error in Iterator.findPrevEntry
Previously, when iterating backwards within findPrevEntry, it was possible for an error to stop backwards iteration without the error propagating up: If the iterator state was already IterValid (eg, because we'd already observed a valid KV), the error was ignored, and the most recent entry was returned. This could cause a reader to observe an older version of the key than what was committed.
1 parent cd7c3b1 commit e640a28

File tree

4 files changed

+73
-5
lines changed

4 files changed

+73
-5
lines changed

data_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,7 @@ func printIterState(
337337
validityStateStr = " at-limit"
338338
}
339339
}
340-
if err := iter.Error(); err != nil {
341-
fmt.Fprintf(b, "err=%v\n", err)
342-
} else if validity == IterValid {
340+
if validity == IterValid {
343341
switch {
344342
case iter.opts.pointKeys():
345343
hasPoint, hasRange := iter.HasPointAndRange()
@@ -378,7 +376,11 @@ func printIterState(
378376
}
379377
fmt.Fprintln(b)
380378
} else {
381-
fmt.Fprintf(b, ".%s\n", validityStateStr)
379+
if err := iter.Error(); err != nil {
380+
fmt.Fprintf(b, "err=%v\n", err)
381+
} else {
382+
fmt.Fprintf(b, ".%s\n", validityStateStr)
383+
}
382384
}
383385
}
384386

iterator.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1088,8 +1088,14 @@ func (i *Iterator) findPrevEntry(limit []byte) {
10881088
return
10891089
}
10901090
}
1091-
10921091
// i.iterKey == nil, so broke out of the preceding loop.
1092+
1093+
// Is iterKey nil due to an error?
1094+
if i.err = i.iter.Error(); i.err != nil {
1095+
i.iterValidityState = IterExhausted
1096+
return
1097+
}
1098+
10931099
if i.iterValidityState == IterValid {
10941100
i.pos = iterPosPrev
10951101
if valueMerger != nil {

iterator_histories_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,20 @@ func TestIterHistories(t *testing.T) {
6868
err = firstError(err, iter.Close())
6969
delete(iters, key)
7070
}
71+
7172
if d != nil {
73+
// Close all open snapshots.
74+
d.mu.Lock()
75+
var ss []*Snapshot
76+
l := &d.mu.snapshots
77+
for i := l.root.next; i != &l.root; i = i.next {
78+
ss = append(ss, i)
79+
}
80+
d.mu.Unlock()
81+
for i := range ss {
82+
err = firstError(err, ss[i].Close())
83+
}
84+
7285
err = firstError(err, d.Close())
7386
d = nil
7487
}

testdata/iter_histories/errors

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,50 @@ first
9797
----
9898
err=injected error
9999
a: (a, .)
100+
101+
# Test a scenario where an error occurs while the top-level Iterator is
102+
# Prev()-ing backwards through many versions of the same user key. In the below
103+
# test, reading the first block of the sstable (containing c.SET.13) fails. The
104+
# iterator must surface the error. Previously a bug existed that would allow the
105+
# iterator to mistakenly surface c.SET.12.
106+
107+
define auto-compactions=off block-size=1 snapshots=(10,11,12,13)
108+
L1
109+
c.SET.13:c13
110+
c.SET.12:c12
111+
c.SET.11:c11
112+
c.SET.10:c10
113+
c.SET.9:c9
114+
c.SET.8:c8
115+
d.SET.9:d9
116+
e.SET.9:e9
117+
----
118+
1:
119+
000004:[c#13,SET-e#9,SET]
120+
121+
layout filename=000004.sst
122+
----
123+
0 data (23)
124+
28 data (23)
125+
56 data (23)
126+
84 data (23)
127+
112 data (22)
128+
139 data (22)
129+
166 data (22)
130+
193 index (113)
131+
311 properties (643)
132+
959 meta-index (33)
133+
997 footer (53)
134+
1050 EOF
135+
136+
reopen auto-compactions=off enable-table-stats=false inject-errors=((ErrInjected (And (PathMatch "000004.sst") (OpFileReadAt 0))))
137+
----
138+
139+
combined-iter
140+
last
141+
prev
142+
prev
143+
----
144+
e: (e9, .)
145+
d: (d9, .)
146+
err=injected error

0 commit comments

Comments
 (0)