@@ -56,6 +56,12 @@ func delArgs(key roachpb.Key, seq enginepb.TxnSeq) *kvpb.DeleteRequest {
5656 }
5757}
5858
59+ func delRangeArgs (key , endKey roachpb.Key , seq enginepb.TxnSeq ) * kvpb.DeleteRangeRequest {
60+ return & kvpb.DeleteRangeRequest {
61+ RequestHeader : kvpb.RequestHeader {Key : key , EndKey : endKey , Sequence : seq },
62+ }
63+ }
64+
5965func makeBufferedWrite (key roachpb.Key , vals ... bufferedValue ) bufferedWrite {
6066 return bufferedWrite {key : key , vals : vals }
6167}
@@ -1495,3 +1501,116 @@ func TestTxnWriteBufferFlushesWhenOverBudget(t *testing.T) {
14951501 require .Len (t , br .Responses , 1 )
14961502 require .IsType (t , & kvpb.EndTxnResponse {}, br .Responses [0 ].GetInner ())
14971503}
1504+
1505+ // TestTxnWriteBufferDeleteRange ensures that the txnWriteBuffer correctly
1506+ // handles DeleteRange requests. In particular, whenever we see a batch with a
1507+ // DeleteRange request, the write buffer is flushed and write buffering is
1508+ // turned off for subsequent requests.
1509+ func TestTxnWriteBufferDeleteRange (t * testing.T ) {
1510+ defer leaktest .AfterTest (t )()
1511+ defer log .Scope (t ).Close (t )
1512+ ctx := context .Background ()
1513+ twb , mockSender := makeMockTxnWriteBuffer (cluster .MakeClusterSettings ())
1514+
1515+ txn := makeTxnProto ()
1516+ txn .Sequence = 10
1517+ keyA , keyB , keyC := roachpb .Key ("a" ), roachpb .Key ("b" ), roachpb .Key ("c" )
1518+ valA := "valA"
1519+
1520+ ba := & kvpb.BatchRequest {}
1521+ ba .Header = kvpb.Header {Txn : & txn }
1522+ putA := putArgs (keyA , valA , txn .Sequence )
1523+ delC := delArgs (keyC , txn .Sequence )
1524+ ba .Add (putA )
1525+ ba .Add (delC )
1526+
1527+ numCalled := mockSender .NumCalled ()
1528+ br , pErr := twb .SendLocked (ctx , ba )
1529+ require .Nil (t , pErr )
1530+ require .NotNil (t , br )
1531+ // All the requests should be buffered and not make it past the
1532+ // txnWriteBuffer. The response returned should be indistinguishable.
1533+ require .Equal (t , numCalled , mockSender .NumCalled ())
1534+ require .Len (t , br .Responses , 2 )
1535+ require .IsType (t , & kvpb.PutResponse {}, br .Responses [0 ].GetInner ())
1536+ // Verify the Put was buffered correctly.
1537+ expBufferedWrites := []bufferedWrite {
1538+ makeBufferedWrite (keyA , makeBufferedValue ("valA" , 10 )),
1539+ makeBufferedWrite (keyC , makeBufferedValue ("" , 10 )),
1540+ }
1541+ require .Equal (t , expBufferedWrites , twb .testingBufferedWritesAsSlice ())
1542+
1543+ // Send a DeleteRange request. This should result in the entire buffer
1544+ // being flushed. Note that we're flushing the delete to key C as well, even
1545+ // though it doesn't overlap with the DeleteRange request.
1546+ ba = & kvpb.BatchRequest {}
1547+ ba .Header = kvpb.Header {Txn : & txn }
1548+ delRange := delRangeArgs (keyA , keyB , txn .Sequence )
1549+ ba .Add (delRange )
1550+
1551+ mockSender .MockSend (func (ba * kvpb.BatchRequest ) (* kvpb.BatchResponse , * kvpb.Error ) {
1552+ require .Len (t , ba .Requests , 3 )
1553+
1554+ require .IsType (t , & kvpb.PutRequest {}, ba .Requests [0 ].GetInner ())
1555+ require .IsType (t , & kvpb.DeleteRequest {}, ba .Requests [1 ].GetInner ())
1556+ require .IsType (t , & kvpb.DeleteRangeRequest {}, ba .Requests [2 ].GetInner ())
1557+
1558+ br = ba .CreateReply ()
1559+ br .Txn = ba .Txn
1560+ return br , nil
1561+ })
1562+
1563+ br , pErr = twb .SendLocked (ctx , ba )
1564+ require .Nil (t , pErr )
1565+ require .NotNil (t , br )
1566+ // Even though we flushed some writes, it shouldn't make it back to the response.
1567+ require .Len (t , br .Responses , 1 )
1568+ require .IsType (t , & kvpb.DeleteRangeResponse {}, br .Responses [0 ].GetInner ())
1569+
1570+ // Ensure the buffer is empty at this point.
1571+ require .Equal (t , 0 , len (twb .testingBufferedWritesAsSlice ()))
1572+
1573+ // Subsequent batches should not be buffered.
1574+ ba = & kvpb.BatchRequest {}
1575+ ba .Header = kvpb.Header {Txn : & txn }
1576+ putC := putArgs (keyC , valA , txn .Sequence )
1577+ ba .Add (putC )
1578+
1579+ mockSender .MockSend (func (ba * kvpb.BatchRequest ) (* kvpb.BatchResponse , * kvpb.Error ) {
1580+ require .Len (t , ba .Requests , 1 )
1581+
1582+ require .IsType (t , & kvpb.PutRequest {}, ba .Requests [0 ].GetInner ())
1583+
1584+ br = ba .CreateReply ()
1585+ br .Txn = ba .Txn
1586+ return br , nil
1587+ })
1588+
1589+ br , pErr = twb .SendLocked (ctx , ba )
1590+ require .Nil (t , pErr )
1591+ require .NotNil (t , br )
1592+ require .Len (t , br .Responses , 1 )
1593+ require .IsType (t , & kvpb.PutResponse {}, br .Responses [0 ].GetInner ())
1594+
1595+ // Commit the transaction. We flushed the buffer already, and no subsequent
1596+ // writes were buffered, so the buffer should be empty. As such, no write
1597+ // requests should be added to the batch.
1598+ ba = & kvpb.BatchRequest {}
1599+ ba .Header = kvpb.Header {Txn : & txn }
1600+ ba .Add (& kvpb.EndTxnRequest {Commit : true })
1601+
1602+ mockSender .MockSend (func (ba * kvpb.BatchRequest ) (* kvpb.BatchResponse , * kvpb.Error ) {
1603+ require .Len (t , ba .Requests , 1 )
1604+ require .IsType (t , & kvpb.EndTxnRequest {}, ba .Requests [0 ].GetInner ())
1605+
1606+ br = ba .CreateReply ()
1607+ br .Txn = ba .Txn
1608+ return br , nil
1609+ })
1610+
1611+ br , pErr = twb .SendLocked (ctx , ba )
1612+ require .Nil (t , pErr )
1613+ require .NotNil (t , br )
1614+ require .Len (t , br .Responses , 1 )
1615+ require .IsType (t , & kvpb.EndTxnResponse {}, br .Responses [0 ].GetInner ())
1616+ }
0 commit comments