Skip to content

Commit 238f0e3

Browse files
committed
kvclient: fix handling of ResumeSpans in txnWriteBuffer
Previously, we incorrectly handled Scans and ReverseScans when they had ResumeSpan set. In particular, we would include buffered writes that overlapped with the key span until the "full end", ignoring where the ResumeSpan started. This meant that we could include the same buffered write multiple times into the merged response. This is now fixed by overlapping the btree only with the key span that was scanned on the current "page". Release note: None
1 parent 094779f commit 238f0e3

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

pkg/kv/kvclient/kvcoord/txn_interceptor_write_buffer.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,11 @@ type respIter struct {
14771477
// BATCH_RESPONSE are supported right now.
14781478
scanFormat kvpb.ScanFormat
14791479

1480+
// resumeSpan, if set, is the ResumeSpan of the response. When non-nil, it
1481+
// means that the response is being paginated, so we need to overlap the
1482+
// buffer only with the part of the original span that was actually scanned.
1483+
resumeSpan *roachpb.Span
1484+
14801485
// rows is the Rows field of the corresponding response.
14811486
//
14821487
// Only set with KEY_VALUES scan format.
@@ -1514,6 +1519,7 @@ func newScanRespIter(req *kvpb.ScanRequest, resp *kvpb.ScanResponse) *respIter {
15141519
return &respIter{
15151520
scanReq: req,
15161521
scanFormat: req.ScanFormat,
1522+
resumeSpan: resp.ResumeSpan,
15171523
rows: resp.Rows,
15181524
batchResponses: resp.BatchResponses,
15191525
}
@@ -1530,6 +1536,7 @@ func newReverseScanRespIter(
15301536
return &respIter{
15311537
reverseScanReq: req,
15321538
scanFormat: req.ScanFormat,
1539+
resumeSpan: resp.ResumeSpan,
15331540
rows: resp.Rows,
15341541
batchResponses: resp.BatchResponses,
15351542
}
@@ -1583,13 +1590,27 @@ func (s *respIter) startKey() roachpb.Key {
15831590
if s.scanReq != nil {
15841591
return s.scanReq.Key
15851592
}
1593+
// For ReverseScans, the EndKey of the ResumeSpan is updated to indicate the
1594+
// start key for the "next" page, which is exactly the last key that was
1595+
// reverse-scanned for the current response.
1596+
// TODO(yuzefovich): we should have some unit tests that exercise the
1597+
// ResumeSpan case.
1598+
if s.resumeSpan != nil {
1599+
return s.resumeSpan.EndKey
1600+
}
15861601
return s.reverseScanReq.Key
15871602
}
15881603

15891604
// endKey returns the end key of the request in response to which the iterator
15901605
// was created.
15911606
func (s *respIter) endKey() roachpb.Key {
15921607
if s.scanReq != nil {
1608+
// For Scans, the Key of the ResumeSpan is updated to indicate the start
1609+
// key for the "next" page, which is exactly the last key that was
1610+
// scanned for the current response.
1611+
if s.resumeSpan != nil {
1612+
return s.resumeSpan.Key
1613+
}
15931614
return s.scanReq.EndKey
15941615
}
15951616
return s.reverseScanReq.EndKey

pkg/sql/logictest/testdata/logic_test/buffered_writes

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,3 +439,43 @@ query I
439439
SELECT count(*) FROM uvw
440440
----
441441
64
442+
443+
# Create some large blobs that will require the scans to be paginated (in 10MiB
444+
# chunks).
445+
statement ok
446+
CREATE TABLE large (k PRIMARY KEY, blob) AS SELECT i * 100, repeat('a', 11 * 1024 * 1024) FROM generate_series(1, 4) AS g(i);
447+
448+
statement ok
449+
BEGIN;
450+
451+
statement ok
452+
INSERT INTO large SELECT 11 + i * 100, 'b' FROM generate_series(1, 4) AS g(i);
453+
454+
# Forward scan.
455+
query I
456+
SELECT k FROM large ORDER BY k;
457+
----
458+
100
459+
111
460+
200
461+
211
462+
300
463+
311
464+
400
465+
411
466+
467+
# Reverse scan.
468+
query I
469+
SELECT k FROM large ORDER BY k DESC;
470+
----
471+
411
472+
400
473+
311
474+
300
475+
211
476+
200
477+
111
478+
100
479+
480+
statement ok
481+
COMMIT;

0 commit comments

Comments
 (0)