Skip to content

Commit 47aa793

Browse files
craig[bot]stevendanna
andcommitted
Merge #151916
151916: kvcoord: avoid piggybacking on any early-returning batch r=miraradeva a=stevendanna To reduce the number of batches, we'd _like_ to piggy-back our buffer flush on the user-provided batch. But, in some cases this is problematic as the user may have set options on the batch that can result in the batch being incompletely processed. Here, we check for those options and flush the buffer via a sanitized batch before processing the user's batch. In a follow-up, we may also _always_ use a separate batch for midTxn flushes to avoid nearly all read-write batches. Epic: None Release note: None Co-authored-by: Steven Danna <[email protected]>
2 parents f5c3ecd + ce6d504 commit 47aa793

File tree

4 files changed

+284
-48
lines changed

4 files changed

+284
-48
lines changed

pkg/kv/kvclient/kvcoord/dist_sender.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1897,8 +1897,7 @@ func (ds *DistSender) divideAndSendBatchToRanges(
18971897
}
18981898
}()
18991899

1900-
canParallelize := ba.Header.MaxSpanRequestKeys == 0 && ba.Header.TargetBytes == 0 &&
1901-
!ba.Header.ReturnElasticCPUResumeSpans
1900+
canParallelize := !ba.MightStopEarly()
19021901
if ba.IsSingleCheckConsistencyRequest() {
19031902
// Don't parallelize full checksum requests as they have to touch the
19041903
// entirety of each replica of each range they touch.
@@ -2002,9 +2001,8 @@ func (ds *DistSender) divideAndSendBatchToRanges(
20022001
ba.UpdateTxn(resp.reply.Txn)
20032002
}
20042003

2005-
mightStopEarly := ba.MaxSpanRequestKeys > 0 || ba.TargetBytes > 0 || ba.ReturnElasticCPUResumeSpans
20062004
// Check whether we've received enough responses to exit query loop.
2007-
if mightStopEarly {
2005+
if ba.MightStopEarly() {
20082006
var replyKeys int64
20092007
var replyBytes int64
20102008
for _, r := range resp.reply.Responses {

pkg/kv/kvclient/kvcoord/txn_interceptor_write_buffer.go

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,10 +1515,10 @@ func (rr requestRecord) toResp(
15151515
// a lock, we add it to the buffer since we may need to flush it as
15161516
// replicated lock.
15171517
if rr.transformed {
1518-
15191518
transformedGetResponse := br.GetInner().(*kvpb.GetResponse)
15201519
valueWasPresent := transformedGetResponse.Value.IsPresent()
1521-
lockShouldHaveBeenAcquired := valueWasPresent || req.LockNonExisting
1520+
lockShouldHaveBeenAcquired := (valueWasPresent || req.LockNonExisting) &&
1521+
transformedGetResponse.ResumeSpan == nil
15221522

15231523
if lockShouldHaveBeenAcquired {
15241524
dla := &bufferedDurableLockAcquisition{
@@ -1771,10 +1771,7 @@ func (twb *txnWriteBuffer) flushBufferAndSendBatch(
17711771
}
17721772

17731773
midTxnFlush := !hasEndTxn
1774-
1775-
// SkipLocked reads cannot be in a batch with basically anything else. If we
1776-
// encounter one, we need to flush our buffer in its own batch.
1777-
splitBatchRequired := ba.WaitPolicy == lock.WaitPolicy_SkipLocked
1774+
splitBatchRequired := separateBatchIsNeeded(ba)
17781775

17791776
// Flush all buffered writes by pre-pending them to the requests being sent
17801777
// in the batch.
@@ -1816,17 +1813,21 @@ func (twb *txnWriteBuffer) flushBufferAndSendBatch(
18161813
})
18171814

18181815
if splitBatchRequired {
1819-
log.VEventf(ctx, 2, "flushing buffer via separate batch")
18201816
flushBatch := ba.ShallowCopy()
1821-
flushBatch.WaitPolicy = 0
1817+
clearBatchRequestOptions(flushBatch)
18221818
flushBatch.Requests = reqs
1819+
log.VEventf(ctx, 2, "flushing %d buffered requests via separate batch", len(reqs))
1820+
18231821
br, pErr := twb.wrapped.SendLocked(ctx, flushBatch)
18241822
if pErr != nil {
18251823
pErr.Index = nil
18261824
return nil, pErr
18271825
}
1828-
1826+
if err := requireAllFlushedRequestsProcessed(br.Responses); err != nil {
1827+
return nil, kvpb.NewError(err)
1828+
}
18291829
ba.UpdateTxn(br.Txn)
1830+
18301831
return twb.wrapped.SendLocked(ctx, ba)
18311832
} else {
18321833
ba = ba.ShallowCopy()
@@ -1835,13 +1836,56 @@ func (twb *txnWriteBuffer) flushBufferAndSendBatch(
18351836
if pErr != nil {
18361837
return nil, twb.adjustErrorUponFlush(ctx, numRevisionsBuffered, pErr)
18371838
}
1838-
1839+
if err := requireAllFlushedRequestsProcessed(br.Responses[0:numRevisionsBuffered]); err != nil {
1840+
return nil, kvpb.NewError(err)
1841+
}
18391842
// Strip out responses for all the flushed buffered writes.
18401843
br.Responses = br.Responses[numRevisionsBuffered:]
18411844
return br, nil
18421845
}
18431846
}
18441847

1848+
func requireAllFlushedRequestsProcessed(responses []kvpb.ResponseUnion) error {
1849+
for _, resp := range responses {
1850+
if resp.GetInner().Header().ResumeSpan != nil {
1851+
return errors.AssertionFailedf("response from buffered request has non-nil resume span")
1852+
}
1853+
}
1854+
return nil
1855+
}
1856+
1857+
// separateBatchIsNeeded returns true if BatchRequest contains any options that
1858+
// require us to flush buffered requests using a separate batch.
1859+
//
1860+
// NB: If you are updating this function, you need to update
1861+
// clearBatchRequestOptions as well.
1862+
func separateBatchIsNeeded(ba *kvpb.BatchRequest) bool {
1863+
return ba.MightStopEarly() ||
1864+
ba.ReadConsistency != 0 ||
1865+
ba.WaitPolicy != 0 ||
1866+
ba.WriteOptions != nil && (*ba.WriteOptions != kvpb.WriteOptions{}) ||
1867+
ba.IsReverse
1868+
}
1869+
1870+
// clearBatchRequestOptions clears any options that should not be present on a
1871+
// batch used to send previously buffered requests.
1872+
//
1873+
// NB: If you are updating this function, you need to update
1874+
// separateBatchIsNeeded as well.
1875+
func clearBatchRequestOptions(ba *kvpb.BatchRequest) {
1876+
// If read consistency is set to anything but CONSISTENT, our flush will fail
1877+
// because we only allow inconsistent reads for read only requests.
1878+
ba.ReadConsistency = 0
1879+
// If WaitPolicy is set to SkipLocked, our request may fail validation.
1880+
ba.WaitPolicy = 0
1881+
// Reset options that could result in an early batch return.
1882+
ba.MaxSpanRequestKeys = 0
1883+
ba.TargetBytes = 0
1884+
ba.ReturnElasticCPUResumeSpans = false
1885+
ba.IsReverse = false
1886+
ba.WriteOptions = nil
1887+
}
1888+
18451889
// hasBufferedWrites returns whether the interceptor has buffered any writes
18461890
// locally.
18471891
func (twb *txnWriteBuffer) hasBufferedWrites() bool {

0 commit comments

Comments
 (0)