@@ -326,7 +326,15 @@ func (twb *txnWriteBuffer) validateRequests(ba *kvpb.BatchRequest) error {
326
326
if t .OriginTimestamp .IsSet () {
327
327
return unsupportedOptionError (t .Method (), "OriginTimestamp" )
328
328
}
329
+ assertTrue (ba .MaxSpanRequestKeys == 0 && ba .TargetBytes == 0 , "unexpectedly found CPut in a BatchRequest with a limit" )
329
330
case * kvpb.PutRequest :
331
+ // TODO(yuzefovich): the DistSender allows Puts to be in batches
332
+ // with limits, which can happen when we're forced to flush the
333
+ // buffered Puts, and the batch we piggy-back on has a limit set.
334
+ // However, SQL never constructs such a batch on its own, so we're
335
+ // asserting the expectations from SQL. Figure out how to reconcile
336
+ // this with more permissive DistSender-level checks.
337
+ assertTrue (ba .MaxSpanRequestKeys == 0 && ba .TargetBytes == 0 , "unexpectedly found Put in a BatchRequest with a limit" )
330
338
case * kvpb.DeleteRequest :
331
339
case * kvpb.GetRequest :
332
340
// ReturnRawMVCCValues is unsupported because we don't know how to serve
@@ -1058,6 +1066,11 @@ func (rr requestRecord) toResp(
1058
1066
// We only use the response from KV if there wasn't already a
1059
1067
// buffered value for this key that our transaction wrote
1060
1068
// previously.
1069
+ // TODO(yuzefovich): for completeness, we should check whether
1070
+ // ResumeSpan is non-nil, in which case the response from KV is
1071
+ // incomplete. This can happen when MaxSpanRequestKeys and/or
1072
+ // TargetBytes limits are set on the batch, and SQL currently
1073
+ // doesn't do that for batches with CPuts.
1061
1074
val = br .GetInner ().(* kvpb.GetResponse ).Value
1062
1075
}
1063
1076
@@ -1078,17 +1091,22 @@ func (rr requestRecord) toResp(
1078
1091
twb .addToBuffer (req .Key , req .Value , req .Sequence , req .KVNemesisSeq )
1079
1092
1080
1093
case * kvpb.PutRequest :
1094
+ // TODO(yuzefovich): for completeness, we should check whether
1095
+ // ResumeSpan is non-nil if we transformed the request, in which case
1096
+ // the response from KV is incomplete. This can happen when
1097
+ // MaxSpanRequestKeys and/or TargetBytes limits are set on the batch,
1098
+ // and SQL currently doesn't do that for batches with Puts.
1081
1099
ru .MustSetInner (& kvpb.PutResponse {})
1082
1100
twb .addToBuffer (req .Key , req .Value , req .Sequence , req .KVNemesisSeq )
1083
1101
1084
1102
case * kvpb.DeleteRequest :
1085
1103
// To correctly populate FoundKey in the response, we must prefer any
1086
1104
// buffered values (if they exist).
1087
- var foundKey bool
1105
+ var resp kvpb. DeleteResponse
1088
1106
val , served := twb .maybeServeRead (req .Key , req .Sequence )
1089
1107
if served {
1090
1108
log .VEventf (ctx , 2 , "serving read portion of %s on key %s from the buffer" , req .Method (), req .Key )
1091
- foundKey = val .IsPresent ()
1109
+ resp . FoundKey = val .IsPresent ()
1092
1110
} else if req .MustAcquireExclusiveLock {
1093
1111
// We sent a GetRequest to the KV layer to acquire an exclusive lock
1094
1112
// on the key, regardless of whether the key already exists or not.
@@ -1097,7 +1115,8 @@ func (rr requestRecord) toResp(
1097
1115
if log .ExpensiveLogEnabled (ctx , 2 ) {
1098
1116
log .Eventf (ctx , "synthesizing DeleteResponse from GetResponse: %#v" , getResp )
1099
1117
}
1100
- foundKey = getResp .Value .IsPresent ()
1118
+ resp .FoundKey = getResp .Value .IsPresent ()
1119
+ resp .ResumeSpan = getResp .ResumeSpan
1101
1120
} else {
1102
1121
// NB: If MustAcquireExclusiveLock wasn't set by the client then we
1103
1122
// eschew sending a Get request to the KV layer just to populate
@@ -1109,16 +1128,26 @@ func (rr requestRecord) toResp(
1109
1128
// TODO(arul): improve the FoundKey semantics to have callers opt
1110
1129
// into whether the care about the key being found. Alternatively,
1111
1130
// clarify the behaviour on DeleteRequest.
1112
- foundKey = false
1131
+ resp . FoundKey = false
1113
1132
}
1114
- ru .MustSetInner (& kvpb.DeleteResponse {
1115
- FoundKey : foundKey ,
1116
- })
1133
+
1134
+ ru .MustSetInner (& resp )
1135
+ if resp .ResumeSpan != nil {
1136
+ // When the Get was incomplete, we haven't actually processed this
1137
+ // Del, so we cannot buffer the write.
1138
+ break
1139
+ }
1140
+
1117
1141
twb .addToBuffer (req .Key , roachpb.Value {}, req .Sequence , req .KVNemesisSeq )
1118
1142
1119
1143
case * kvpb.GetRequest :
1120
1144
val , served := twb .maybeServeRead (req .Key , req .Sequence )
1121
1145
if served {
1146
+ // TODO(yuzefovich): we're effectively ignoring the limits of
1147
+ // BatchRequest when serving the Get from the buffer. We should
1148
+ // consider setting the ResumeSpan if a limit has already been
1149
+ // reached by this point. This will force us to set ResumeSpan on
1150
+ // all remaining requests in the batch.
1122
1151
getResp := & kvpb.GetResponse {}
1123
1152
if val .IsPresent () {
1124
1153
getResp .Value = val
@@ -1612,8 +1641,6 @@ func (s *respIter) startKey() roachpb.Key {
1612
1641
// For ReverseScans, the EndKey of the ResumeSpan is updated to indicate the
1613
1642
// start key for the "next" page, which is exactly the last key that was
1614
1643
// reverse-scanned for the current response.
1615
- // TODO(yuzefovich): we should have some unit tests that exercise the
1616
- // ResumeSpan case.
1617
1644
if s .resumeSpan != nil {
1618
1645
return s .resumeSpan .EndKey
1619
1646
}
@@ -1684,6 +1711,11 @@ func makeRespSizeHelper(it *respIter) respSizeHelper {
1684
1711
}
1685
1712
1686
1713
func (h * respSizeHelper ) acceptBuffer (key roachpb.Key , value * roachpb.Value ) {
1714
+ // TODO(yuzefovich): we're effectively ignoring the limits of BatchRequest
1715
+ // when serving the reads from the buffer. We should consider checking how
1716
+ // many keys and bytes have already been included to see whether we've
1717
+ // reached a limit, and set the ResumeSpan if so (which can result in some
1718
+ // wasted work by the server).
1687
1719
h .numKeys ++
1688
1720
lenKV , _ := encKVLength (key , value )
1689
1721
h .numBytes += int64 (lenKV )
0 commit comments