Skip to content

Commit d913fee

Browse files
committed
kvclient: DeleteRequest's reads from write buffer
In order to ensure that a DELETE returns the correct number of deleted rows, it must read from the write buffer so that it observes any previous deletes or writes. Note that all DELETE statements are not yet handled since we don't yet have DeleteRange support. Epic: none Release note: None
1 parent 3afe231 commit d913fee

File tree

4 files changed

+97
-12
lines changed

4 files changed

+97
-12
lines changed

pkg/kv/kvclient/kvcoord/txn_interceptor_write_buffer.go

+33-8
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,15 @@ func (twb *txnWriteBuffer) applyTransformations(
396396
twb.addToBuffer(t.Key, t.Value, t.Sequence)
397397

398398
case *kvpb.DeleteRequest:
399+
// To correctly populate FoundKey in the response, we need to look in our
400+
// write buffer to see if there is a tombstone.
401+
var foundKey bool
402+
val, served := twb.maybeServeRead(t.Key, t.Sequence)
403+
if served {
404+
log.VEventf(ctx, 2, "serving read portion of %s on key %s from the buffer", t.Method(), t.Key)
405+
foundKey = val.IsPresent()
406+
}
407+
399408
// If MustAcquireExclusiveLock flag is set on the DeleteRequest, then we
400409
// need to add a locking Get to the BatchRequest, including if the key
401410
// doesn't exist.
@@ -414,10 +423,20 @@ func (twb *txnWriteBuffer) applyTransformations(
414423
baRemote.Requests = append(baRemote.Requests, getReqU)
415424
}
416425

426+
// If we found a key in our write buffer we use that
427+
// result regardless of what the GetResponse that we
428+
// might have sent says.
429+
//
430+
// NOTE(ssd): We are assuming that callers who care
431+
// about an accurate value of FoundKey also set
432+
// MustAcquireExclusiveLock.
417433
var ru kvpb.ResponseUnion
418-
ru.MustSetInner(&kvpb.DeleteResponse{
419-
FoundKey: false,
420-
})
434+
if served || !t.MustAcquireExclusiveLock {
435+
ru.MustSetInner(&kvpb.DeleteResponse{
436+
FoundKey: foundKey,
437+
})
438+
}
439+
421440
ts = append(ts, transformation{
422441
stripped: !t.MustAcquireExclusiveLock,
423442
index: i,
@@ -812,13 +831,19 @@ func (t transformation) toResp(
812831
ru = t.resp
813832

814833
case *kvpb.DeleteRequest:
815-
getResp := br.GetInner().(*kvpb.GetResponse)
816834
ru = t.resp
817-
if log.ExpensiveLogEnabled(ctx, 2) {
818-
log.Eventf(ctx, "synthesizing DeleteResponse from GetResponse: %#v", getResp)
835+
// If the deletion response is already set, it means we served response from
836+
// the write buffer. We can still be here because we happened to need to
837+
// send a GetRequest solely for the locking behaviour.
838+
if ru.GetDelete() == nil {
839+
getResp := br.GetInner().(*kvpb.GetResponse)
840+
if log.ExpensiveLogEnabled(ctx, 2) {
841+
log.Eventf(ctx, "synthesizing DeleteResponse from GetResponse: %#v", getResp)
842+
}
843+
ru.MustSetInner(&kvpb.DeleteResponse{
844+
FoundKey: getResp.Value.IsPresent(),
845+
})
819846
}
820-
ru.GetDelete().FoundKey = getResp.Value.IsPresent()
821-
822847
case *kvpb.GetRequest:
823848
// Get requests must be served from the local buffer if a transaction
824849
// performed a previous write to the key being read. However, Get requests

pkg/kv/kvpb/api.proto

+5-1
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,11 @@ message DeleteRequest {
408408
RequestHeader header = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true];
409409
// MustAcquireExclusiveLock, if set, indicates that a lock with the strength
410410
// no lower than "exclusive" needs to be acquired on the key, even if it
411-
// doesn't exist.
411+
// doesn't exist. Note that when MustAcquireExclusiveLock is false, the
412+
// FoundKey field in the DeleteResponse may be incorrect when the kvclient has
413+
// been configured to buffer writes.
414+
//
415+
// TODO(ssd): Separate the behaviour of FoundKey to not depend on this flag.
412416
bool must_acquire_exclusive_lock = 2;
413417
}
414418

pkg/sql/logictest/logic.go

+17
Original file line numberDiff line numberDiff line change
@@ -2086,6 +2086,21 @@ func (c clusterOptIgnoreStrictGCForTenants) apply(args *base.TestServerArgs) {
20862086
args.Knobs.Store.(*kvserver.StoreTestingKnobs).IgnoreStrictGCEnforcement = true
20872087
}
20882088

2089+
// clusterOptDisableUseMVCCRangeTombstonesForPointDeletes corresponds
2090+
// to the disable-mvcc-range-tombstones-for-point-deletes directive.
2091+
type clusterOptDisableUseMVCCRangeTombstonesForPointDeletes struct{}
2092+
2093+
var _ clusterOpt = clusterOptDisableUseMVCCRangeTombstonesForPointDeletes{}
2094+
2095+
// apply implements the clusterOpt interface.
2096+
func (c clusterOptDisableUseMVCCRangeTombstonesForPointDeletes) apply(args *base.TestServerArgs) {
2097+
_, ok := args.Knobs.Store.(*kvserver.StoreTestingKnobs)
2098+
if !ok {
2099+
args.Knobs.Store = &kvserver.StoreTestingKnobs{}
2100+
}
2101+
args.Knobs.Store.(*kvserver.StoreTestingKnobs).EvalKnobs.UseRangeTombstonesForPointDeletes = false
2102+
}
2103+
20892104
// knobOptDisableCorpusGeneration disables corpus generation for declarative
20902105
// schema changer.
20912106
type knobOptDisableCorpusGeneration struct{}
@@ -2230,6 +2245,8 @@ func readClusterOptions(t *testing.T, path string) []clusterOpt {
22302245
res = append(res, clusterOptTracingOff{})
22312246
case "ignore-tenant-strict-gc-enforcement":
22322247
res = append(res, clusterOptIgnoreStrictGCForTenants{})
2248+
case "disable-mvcc-range-tombstones-for-point-deletes":
2249+
res = append(res, clusterOptDisableUseMVCCRangeTombstonesForPointDeletes{})
22332250
default:
22342251
t.Fatalf("unrecognized cluster option: %s", opt)
22352252
}

pkg/sql/logictest/testdata/logic_test/buffered_writes

+42-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
2-
subtest point_delete
1+
# cluster-opt: disable-mvcc-range-tombstones-for-point-deletes
32

43
statement ok
54
SET kv_transaction_buffered_writes_enabled=true
65

76
statement ok
8-
CREATE TABLE t1 (pk int primary key, v int)
7+
CREATE TABLE t1 (pk int primary key, v int, FAMILY (pk, v))
8+
9+
subtest point_delete
910

1011
statement ok
1112
INSERT INTO t1 VALUES (1,1)
@@ -21,3 +22,41 @@ DELETE FROM t1 WHERE pk = 3
2122

2223
statement ok
2324
COMMIT
25+
26+
subtest repeated_point_delete
27+
28+
statement ok
29+
INSERT INTO t1 VALUES (1,1)
30+
31+
statement ok
32+
BEGIN
33+
34+
statement count 1
35+
DELETE FROM t1 WHERE pk = 1
36+
37+
# The second delete should be served from the write buffer and observe
38+
# the buffered tombstone.
39+
statement count 0
40+
DELETE FROM t1 WHERE pk = 1
41+
42+
statement ok
43+
COMMIT
44+
45+
subtest point_delete_after_write
46+
47+
statement ok
48+
BEGIN
49+
50+
statement ok
51+
INSERT INTO t1 VALUES (1,1)
52+
53+
statement count 1
54+
DELETE FROM t1 WHERE pk = 1
55+
56+
# The second delete should be served from the write buffer and observe
57+
# the buffered tombstone.
58+
statement count 0
59+
DELETE FROM t1 WHERE pk = 1
60+
61+
statement ok
62+
COMMIT

0 commit comments

Comments
 (0)