@@ -424,7 +424,9 @@ func (twb *txnWriteBuffer) adjustError(
424424 baIdx := int32 (0 )
425425 for i := range numOriginalRequests {
426426 if len (ts ) > 0 && ts [0 ].index == i {
427- if ts [0 ].stripped {
427+ curTs := ts [0 ]
428+ ts = ts [1 :]
429+ if curTs .stripped {
428430 numStripped ++
429431 } else {
430432 // This is a transformed request (for example a LockingGet that was
@@ -433,14 +435,19 @@ func (twb *txnWriteBuffer) adjustError(
433435 // exactly the request the user sent.
434436 //
435437 // For now, we handle this by logging and removing the error index.
436- if baIdx == pErr .Index .Index {
438+ //
439+ // [1] Get requests are always collected as transformations, but
440+ // they're never transformed. Attributing an error to them shouldn't
441+ // confuse the client.
442+ if baIdx == pErr .Index .Index && curTs .origRequest .Method () != kvpb .Get {
437443 log .Warningf (ctx , "error index %d is part of a transformed request" , pErr .Index .Index )
438444 pErr .Index = nil
439445 return pErr
440446 }
441447 }
442- ts = ts [1 :]
443- continue
448+ if curTs .origRequest .Method () != kvpb .Get {
449+ continue
450+ }
444451 }
445452 if baIdx == pErr .Index .Index {
446453 break
@@ -713,42 +720,43 @@ func (twb *txnWriteBuffer) applyTransformations(
713720
714721 case * kvpb.GetRequest :
715722 // If the key is in the buffer, we must serve the read from the buffer.
716- val , served := twb .maybeServeRead (t .Key , t .Sequence )
723+ // The actual serving of the read will happen on the response path though.
724+ stripped := false
725+ _ , served := twb .maybeServeRead (t .Key , t .Sequence )
717726 if served {
718- log .VEventf (ctx , 2 , "serving %s on key %s from the buffer" , t .Method (), t .Key )
719- var resp kvpb.ResponseUnion
720- getResp := & kvpb.GetResponse {}
721- if val .IsPresent () {
722- getResp .Value = val
723- }
724- resp .MustSetInner (getResp )
725-
726- stripped := true
727727 if t .KeyLockingStrength != lock .None {
728728 // Even though the Get request must be served from the buffer, as the
729729 // transaction performed a previous write to the key, we still need to
730730 // acquire a lock at the leaseholder. As a result, we can't strip the
731- // request from the batch.
731+ // request from the remote batch.
732732 //
733733 // TODO(arul): we could eschew sending this request if we knew there
734734 // was a sufficiently strong lock already present on the key.
735- stripped = false
735+ log . VEventf ( ctx , 2 , "locking %s on key %s must be sent to the server" , t . Method (), t . Key )
736736 baRemote .Requests = append (baRemote .Requests , ru )
737+ } else {
738+ // We'll synthesize the response from the buffer on the response path;
739+ // eschew sending the request to the KV layer as we don't need to
740+ // acquire a lock.
741+ stripped = true
742+ log .VEventf (
743+ ctx , 2 , "non-locking %s on key %s can be fully served by the client; not sending to KV" , t .Method (), t .Key ,
744+ )
737745 }
738-
739- ts = append (ts , transformation {
740- stripped : stripped ,
741- index : i ,
742- origRequest : req ,
743- resp : resp ,
744- })
745- // We've constructed a response that we'll stitch together with the
746- // result on the response path; eschew sending the request to the KV
747- // layer.
748- continue
746+ } else {
747+ // Wasn't served locally; send the request to the KV layer.
748+ baRemote .Requests = append (baRemote .Requests , ru )
749749 }
750- // Wasn't served locally; send the request to the KV layer.
751- baRemote .Requests = append (baRemote .Requests , ru )
750+ // Even if the request wasn't served from the buffer here, we still track
751+ // a transformation for it. That's because we haven't buffered any writes
752+ // from our current batch in the buffer yet, so checking the buffer above
753+ // isn't sufficient to determine whether the request needs to serve a read
754+ // from the buffer before returning a response or not.
755+ ts = append (ts , transformation {
756+ stripped : stripped ,
757+ index : i ,
758+ origRequest : req ,
759+ })
752760
753761 case * kvpb.ScanRequest :
754762 overlaps := twb .scanOverlaps (t .Key , t .EndKey )
@@ -1054,12 +1062,6 @@ type transformation struct {
10541062 index int
10551063 // origRequest is the original request that was transformed.
10561064 origRequest kvpb.Request
1057- // resp is locally produced response that needs to be merged with any
1058- // responses returned by the KV layer. This is set for requests that can be
1059- // evaluated locally (e.g. blind writes, reads that can be served entirely
1060- // from the buffer). Must be set if stripped is true, but the converse doesn't
1061- // hold.
1062- resp kvpb.ResponseUnion
10631065}
10641066
10651067// toResp returns the response that should be added to the batch response as
@@ -1142,13 +1144,20 @@ func (t transformation) toResp(
11421144 twb .addToBuffer (req .Key , roachpb.Value {}, req .Sequence )
11431145
11441146 case * kvpb.GetRequest :
1145- // Get requests must be served from the local buffer if a transaction
1146- // performed a previous write to the key being read. However, Get
1147- // requests must be sent to the KV layer (i.e. not be stripped) iff they
1148- // are locking in nature.
1149- assertTrue (t .stripped == (req .KeyLockingStrength == lock .None ),
1150- "Get requests should either be stripped or be locking" )
1151- ru = t .resp
1147+ val , served := twb .maybeServeRead (req .Key , req .Sequence )
1148+ if served {
1149+ getResp := & kvpb.GetResponse {}
1150+ if val .IsPresent () {
1151+ getResp .Value = val
1152+ }
1153+ ru .MustSetInner (getResp )
1154+ log .VEventf (ctx , 2 , "serving %s on key %s from the buffer" , req .Method (), req .Key )
1155+ } else {
1156+ // The request wasn't served from the buffer; return the response from the
1157+ // KV layer.
1158+ assertTrue (! t .stripped , "we shouldn't be stripping requests that aren't served from the buffer" )
1159+ ru = br
1160+ }
11521161
11531162 case * kvpb.ScanRequest :
11541163 scanResp , err := twb .mergeWithScanResp (
0 commit comments