Skip to content

Commit 2e3a978

Browse files
committed
kvcoord: fix bug in bufferSize accounting in txnWriteBuffer
For a lock that was never held, we were over-decrementing the buffer size on savepoint rollback. I've added a regression test, but this bug was also caught by KVNemesis. Epic: none Release note: None
1 parent fb045f3 commit 2e3a978

File tree

2 files changed

+43
-1
lines changed

2 files changed

+43
-1
lines changed

pkg/kv/kvclient/kvcoord/txn_interceptor_write_buffer.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,8 @@ func (twb *txnWriteBuffer) rollbackToSavepointLocked(ctx context.Context, s save
655655
toDelete := make([]*bufferedWrite, 0)
656656
it := twb.buffer.MakeIter()
657657
for it.First(); it.Valid(); it.Next() {
658-
if held := it.Cur().rollbackLockInfo(s.seqNum); !held {
658+
hadLockInfo := it.Cur().lki != nil
659+
if held := it.Cur().rollbackLockInfo(s.seqNum); hadLockInfo && !held {
659660
// If we aren't still held, update our buffer size.
660661
twb.bufferSize -= lockKeyInfoSize
661662
}

pkg/kv/kvclient/kvcoord/txn_interceptor_write_buffer_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1820,6 +1820,47 @@ func TestTxnWriteBufferRollbackToSavepoint(t *testing.T) {
18201820
require.IsType(t, &kvpb.EndTxnResponse{}, br.Responses[0].GetInner())
18211821
}
18221822

1823+
// TestRollbackNeverHeldLock is a regression test for a bug around incorrect
1824+
// accounting of the buffer size for completely unlocked writes that were rolled
1825+
// back.
1826+
func TestRollbackNeverHeldLock(t *testing.T) {
1827+
defer leaktest.AfterTest(t)()
1828+
defer log.Scope(t).Close(t)
1829+
ctx := context.Background()
1830+
twb, mockSender := makeMockTxnWriteBuffer(cluster.MakeClusterSettings())
1831+
1832+
txn := makeTxnProto()
1833+
txn.Sequence = 10
1834+
txn.Sequence++
1835+
sp := &savepoint{seqNum: txn.Sequence}
1836+
twb.createSavepointLocked(ctx, sp)
1837+
1838+
txn.Sequence++
1839+
ba := &kvpb.BatchRequest{Header: kvpb.Header{Txn: &txn}}
1840+
ba.Add(delArgs(roachpb.Key("a"), txn.Sequence))
1841+
1842+
br, pErr := twb.SendLocked(ctx, ba)
1843+
require.Nil(t, pErr)
1844+
require.NotNil(t, br)
1845+
1846+
twb.rollbackToSavepointLocked(ctx, *sp)
1847+
1848+
// Commit the transaction.
1849+
ba = &kvpb.BatchRequest{Header: kvpb.Header{Txn: &txn}}
1850+
ba.Add(&kvpb.EndTxnRequest{Commit: true})
1851+
1852+
mockSender.MockSend(func(ba *kvpb.BatchRequest) (*kvpb.BatchResponse, *kvpb.Error) {
1853+
require.Len(t, ba.Requests, 1)
1854+
br = ba.CreateReply()
1855+
br.Txn = ba.Txn
1856+
return br, nil
1857+
})
1858+
1859+
br, pErr = twb.SendLocked(ctx, ba)
1860+
require.Nil(t, pErr)
1861+
require.NotNil(t, br)
1862+
}
1863+
18231864
// TestTxnWriteBufferRollbackToSavepointMidTxn tests the savepoint rollback
18241865
// logic in the presence of explicit savepoints.
18251866
func TestTxnWriteBufferRollbackToSavepointMidTxn(t *testing.T) {

0 commit comments

Comments
 (0)