@@ -1808,6 +1808,133 @@ func TestTxnWriteBufferRollbackToSavepoint(t *testing.T) {
1808
1808
require .IsType (t , & kvpb.EndTxnResponse {}, br .Responses [0 ].GetInner ())
1809
1809
}
1810
1810
1811
+ // TestTxnWriteBufferRollbackToSavepointMidTxn tests the savepoint rollback
1812
+ // logic in the presence of explicit savepoints.
1813
+ func TestTxnWriteBufferRollbackToSavepointMidTxn (t * testing.T ) {
1814
+ defer leaktest .AfterTest (t )()
1815
+ defer log .Scope (t ).Close (t )
1816
+ ctx := context .Background ()
1817
+
1818
+ sendPut := func (t * testing.T , twb * txnWriteBuffer , mockSender * mockLockedSender , txn * roachpb.Transaction ) {
1819
+ txn .Sequence ++
1820
+ keyA := roachpb .Key ("a" )
1821
+ valA := fmt .Sprintf ("valA@%d" , txn .Sequence )
1822
+ putA := putArgs (keyA , valA , txn .Sequence )
1823
+
1824
+ ba := & kvpb.BatchRequest {}
1825
+ ba .Header = kvpb.Header {Txn : txn }
1826
+ ba .Add (putA )
1827
+
1828
+ mockSender .MockSend (func (ba * kvpb.BatchRequest ) (* kvpb.BatchResponse , * kvpb.Error ) {
1829
+ br := ba .CreateReply ()
1830
+ br .Txn = ba .Txn
1831
+ return br , nil
1832
+ })
1833
+
1834
+ numCalled := mockSender .NumCalled ()
1835
+ br , pErr := twb .SendLocked (ctx , ba )
1836
+ require .Nil (t , pErr )
1837
+ require .NotNil (t , br )
1838
+ require .Equal (t , numCalled , mockSender .NumCalled ())
1839
+ }
1840
+
1841
+ delRangeBatch := func (txn * roachpb.Transaction ) * kvpb.BatchRequest {
1842
+ txn .Sequence ++
1843
+ keyB := roachpb .Key ("b" )
1844
+ keyC := roachpb .Key ("c" )
1845
+ delRangeReq := delRangeArgs (keyB , keyC , txn .Sequence )
1846
+
1847
+ ba := & kvpb.BatchRequest {}
1848
+ ba .Header = kvpb.Header {Txn : txn }
1849
+ ba .Add (delRangeReq )
1850
+ return ba
1851
+ }
1852
+
1853
+ savepoint := func (twb * txnWriteBuffer , txn * roachpb.Transaction ) * savepoint {
1854
+ txn .Sequence ++
1855
+ savepoint := & savepoint {seqNum : txn .Sequence }
1856
+ twb .createSavepointLocked (ctx , savepoint )
1857
+ return savepoint
1858
+ }
1859
+
1860
+ t .Run ("flush with no savepoint sends latest" , func (t * testing.T ) {
1861
+ twb , mockSender := makeMockTxnWriteBuffer (cluster .MakeClusterSettings ())
1862
+ txn := makeTxnProto ()
1863
+ // Send 4 requests to the buffer
1864
+ sendPut (t , & twb , mockSender , & txn )
1865
+ sendPut (t , & twb , mockSender , & txn )
1866
+ sendPut (t , & twb , mockSender , & txn )
1867
+ sendPut (t , & twb , mockSender , & txn )
1868
+ ba := delRangeBatch (& txn )
1869
+
1870
+ // Expect 1 Put and 1 DelRange.
1871
+ mockSender .MockSend (func (ba * kvpb.BatchRequest ) (* kvpb.BatchResponse , * kvpb.Error ) {
1872
+ require .Len (t , ba .Requests , 2 )
1873
+ require .IsType (t , & kvpb.PutRequest {}, ba .Requests [0 ].GetInner ())
1874
+ require .IsType (t , & kvpb.DeleteRangeRequest {}, ba .Requests [1 ].GetInner ())
1875
+
1876
+ br := ba .CreateReply ()
1877
+ br .Txn = ba .Txn
1878
+ return br , nil
1879
+ })
1880
+ br , pErr := twb .SendLocked (ctx , ba )
1881
+ require .Nil (t , pErr )
1882
+ require .NotNil (t , br )
1883
+ })
1884
+
1885
+ t .Run ("flush with savepoint still elides unnecessary writes under savepoint" , func (t * testing.T ) {
1886
+ twb , mockSender := makeMockTxnWriteBuffer (cluster .MakeClusterSettings ())
1887
+ txn := makeTxnProto ()
1888
+ sendPut (t , & twb , mockSender , & txn ) // should be elided
1889
+ sendPut (t , & twb , mockSender , & txn )
1890
+ _ = savepoint (& twb , & txn )
1891
+ sendPut (t , & twb , mockSender , & txn )
1892
+ sendPut (t , & twb , mockSender , & txn )
1893
+ ba := delRangeBatch (& txn )
1894
+
1895
+ // Expect 3 Put and 1 DelRange.
1896
+ mockSender .MockSend (func (ba * kvpb.BatchRequest ) (* kvpb.BatchResponse , * kvpb.Error ) {
1897
+ require .Len (t , ba .Requests , 4 )
1898
+ require .IsType (t , & kvpb.PutRequest {}, ba .Requests [0 ].GetInner ())
1899
+ require .IsType (t , & kvpb.PutRequest {}, ba .Requests [1 ].GetInner ())
1900
+ require .IsType (t , & kvpb.PutRequest {}, ba .Requests [2 ].GetInner ())
1901
+ require .IsType (t , & kvpb.DeleteRangeRequest {}, ba .Requests [3 ].GetInner ())
1902
+
1903
+ br := ba .CreateReply ()
1904
+ br .Txn = ba .Txn
1905
+ return br , nil
1906
+ })
1907
+ br , pErr := twb .SendLocked (ctx , ba )
1908
+ require .Nil (t , pErr )
1909
+ require .NotNil (t , br )
1910
+ })
1911
+
1912
+ t .Run ("flush after release of earliest savepoint only sends latest" , func (t * testing.T ) {
1913
+ twb , mockSender := makeMockTxnWriteBuffer (cluster .MakeClusterSettings ())
1914
+ txn := makeTxnProto ()
1915
+ sendPut (t , & twb , mockSender , & txn )
1916
+ sendPut (t , & twb , mockSender , & txn )
1917
+ sendPut (t , & twb , mockSender , & txn )
1918
+ sp := savepoint (& twb , & txn )
1919
+ sendPut (t , & twb , mockSender , & txn )
1920
+ twb .releaseSavepointLocked (ctx , sp )
1921
+
1922
+ ba := delRangeBatch (& txn )
1923
+ mockSender .MockSend (func (ba * kvpb.BatchRequest ) (* kvpb.BatchResponse , * kvpb.Error ) {
1924
+ require .Len (t , ba .Requests , 2 )
1925
+ require .IsType (t , & kvpb.PutRequest {}, ba .Requests [0 ].GetInner ())
1926
+ require .IsType (t , & kvpb.DeleteRangeRequest {}, ba .Requests [1 ].GetInner ())
1927
+
1928
+ br := ba .CreateReply ()
1929
+ br .Txn = ba .Txn
1930
+ return br , nil
1931
+ })
1932
+ br , pErr := twb .SendLocked (ctx , ba )
1933
+ require .Nil (t , pErr )
1934
+ require .NotNil (t , br )
1935
+ })
1936
+ }
1937
+
1811
1938
// TestTxnWriteBufferFlushesAfterDisabling verifies that the txnWriteBuffer
1812
1939
// flushes on the next batch after it is disabled if it buffered any writes.
1813
1940
func TestTxnWriteBufferFlushesAfterDisabling (t * testing.T ) {
0 commit comments