Skip to content

Commit 761a1d3

Browse files
committed
spanset: assert read-only batches don't access store local and unreplicated RangeID local keys
1 parent 71bd03b commit 761a1d3

File tree

3 files changed

+70
-67
lines changed

3 files changed

+70
-67
lines changed

pkg/kv/kvserver/replica.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/logstore"
3737
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/rafttrace"
3838
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/rangefeed"
39+
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/spanset"
3940
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/split"
4041
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/tenantrate"
4142
"github.com/cockroachdb/cockroach/pkg/raft"
@@ -2984,3 +2985,66 @@ func (r *Replica) SendStreamStats(stats *rac2.RangeSendStreamStats) {
29842985
r.flowControlV2.SendStreamStats(stats)
29852986
}
29862987
}
2988+
2989+
// overlapsUnreplicatedRangeIDLocalKeys checks if the provided span overlaps
2990+
// with any unreplicated rangeID local keys.
2991+
// Note that we could receive the span with a nil startKey, which has a special
2992+
// meaning that the span represents: [endKey.Prev(), endKey).
2993+
func overlapsUnreplicatedRangeIDLocalKeys(span spanset.TrickySpan) error {
2994+
fullRangeIDLocalSpans := roachpb.Span{
2995+
Key: keys.LocalRangeIDPrefix.AsRawKey(),
2996+
EndKey: keys.LocalRangeIDPrefix.AsRawKey().PrefixEnd(),
2997+
}
2998+
2999+
// If the provided span is completely outside the rangeID local spans for any
3000+
// rangeID, then there is no overlap with any rangeID local keys.
3001+
if !spanset.Overlaps(fullRangeIDLocalSpans, span) {
3002+
return nil
3003+
}
3004+
3005+
// At this point, we know that we overlap with fullRangeIDLocalSpans. If we
3006+
// are not completely within fullRangeIDLocalSpans, return an error as we
3007+
// make an assumption that spans should respect the local RangeID tree
3008+
// structure, and that spans that partially overlaps with
3009+
// fullRangeIDLocalSpans don't make logical sense.
3010+
if !spanset.Contains(fullRangeIDLocalSpans, span) {
3011+
return errors.Errorf("overlapping an unreplicated rangeID key")
3012+
}
3013+
3014+
// If the span in inside fullRangeIDLocalSpans, we expect that both start and
3015+
// end keys should be in the same rangeID.
3016+
rangeIDKey := span.Key
3017+
if rangeIDKey == nil {
3018+
rangeIDKey = span.EndKey
3019+
}
3020+
rangeID, err := keys.DecodeRangeIDPrefix(rangeIDKey)
3021+
if err != nil {
3022+
return errors.NewAssertionErrorWithWrappedErrf(err,
3023+
"could not decode range ID for span: %s", span)
3024+
}
3025+
if spanset.Overlaps(roachpb.Span{
3026+
Key: keys.MakeRangeIDUnreplicatedPrefix(rangeID),
3027+
EndKey: keys.MakeRangeIDUnreplicatedPrefix(rangeID).PrefixEnd(),
3028+
}, span) {
3029+
return errors.Errorf("overlapping an unreplicated rangeID span")
3030+
}
3031+
3032+
return nil
3033+
}
3034+
3035+
// overlapsStoreLocalKeys returns an error if the provided span overlaps
3036+
// with any store local keys.
3037+
// Note that we could receive the span with a nil startKey, which has a special
3038+
// meaning that the span represents: [endKey.Prev(), endKey).
3039+
func overlapsStoreLocalKeys(span spanset.TrickySpan) error {
3040+
localStoreSpan := roachpb.Span{
3041+
Key: keys.LocalStorePrefix,
3042+
EndKey: keys.LocalStoreMax,
3043+
}
3044+
3045+
if spanset.Overlaps(localStoreSpan, span) {
3046+
return errors.Errorf("overlaps with store local keys")
3047+
}
3048+
3049+
return nil
3050+
}

pkg/kv/kvserver/replica_read.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
"github.com/cockroachdb/cockroach/pkg/settings"
2525
"github.com/cockroachdb/cockroach/pkg/storage"
2626
"github.com/cockroachdb/cockroach/pkg/storage/fs"
27-
"github.com/cockroachdb/cockroach/pkg/util"
27+
"github.com/cockroachdb/cockroach/pkg/util/buildutil"
2828
"github.com/cockroachdb/cockroach/pkg/util/hlc"
2929
"github.com/cockroachdb/cockroach/pkg/util/log"
3030
"github.com/cockroachdb/cockroach/pkg/util/mon"
@@ -92,8 +92,11 @@ func (r *Replica) executeReadOnlyBatch(
9292
if err := rw.PinEngineStateForIterators(readCategory); err != nil {
9393
return nil, g, nil, kvpb.NewError(err)
9494
}
95-
if util.RaceEnabled {
96-
rw = spanset.NewReadWriterAt(rw, g.LatchSpans(), ba.Timestamp)
95+
if buildutil.CrdbTestBuild {
96+
spans := g.LatchSpans()
97+
spans.AddForbiddenMatcher(overlapsUnreplicatedRangeIDLocalKeys)
98+
spans.AddForbiddenMatcher(overlapsStoreLocalKeys)
99+
rw = spanset.NewReadWriterAt(rw, spans, ba.Timestamp)
97100
}
98101
defer rw.Close()
99102

pkg/kv/kvserver/replica_write.go

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"sync"
1111
"time"
1212

13-
"github.com/cockroachdb/cockroach/pkg/keys"
1413
"github.com/cockroachdb/cockroach/pkg/kv"
1514
"github.com/cockroachdb/cockroach/pkg/kv/kvpb"
1615
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval"
@@ -889,66 +888,3 @@ func releaseMVCCStats(ms *enginepb.MVCCStats) {
889888
ms.Reset()
890889
mvccStatsPool.Put(ms)
891890
}
892-
893-
// overlapsUnreplicatedRangeIDLocalKeys checks if the provided span overlaps
894-
// with any unreplicated rangeID local keys.
895-
// Note that we could receive the span with a nil startKey, which has a special
896-
// meaning that the span represents: [endKey.Prev(), endKey).
897-
func overlapsUnreplicatedRangeIDLocalKeys(span spanset.TrickySpan) error {
898-
fullRangeIDLocalSpans := roachpb.Span{
899-
Key: keys.LocalRangeIDPrefix.AsRawKey(),
900-
EndKey: keys.LocalRangeIDPrefix.AsRawKey().PrefixEnd(),
901-
}
902-
903-
// If the provided span is completely outside the rangeID local spans for any
904-
// rangeID, then there is no overlap with any rangeID local keys.
905-
if !spanset.Overlaps(fullRangeIDLocalSpans, span) {
906-
return nil
907-
}
908-
909-
// At this point, we know that we overlap with fullRangeIDLocalSpans. If we
910-
// are not completely within fullRangeIDLocalSpans, return an error as we
911-
// make an assumption that spans should respect the local RangeID tree
912-
// structure, and that spans that partially overlaps with
913-
// fullRangeIDLocalSpans don't make logical sense.
914-
if !spanset.Contains(fullRangeIDLocalSpans, span) {
915-
return errors.Errorf("overlapping an unreplicated rangeID key")
916-
}
917-
918-
// If the span in inside fullRangeIDLocalSpans, we expect that both start and
919-
// end keys should be in the same rangeID.
920-
rangeIDKey := span.Key
921-
if rangeIDKey == nil {
922-
rangeIDKey = span.EndKey
923-
}
924-
rangeID, err := keys.DecodeRangeIDPrefix(rangeIDKey)
925-
if err != nil {
926-
return errors.NewAssertionErrorWithWrappedErrf(err,
927-
"could not decode range ID for span: %s", span)
928-
}
929-
if spanset.Overlaps(roachpb.Span{
930-
Key: keys.MakeRangeIDUnreplicatedPrefix(rangeID),
931-
EndKey: keys.MakeRangeIDUnreplicatedPrefix(rangeID).PrefixEnd(),
932-
}, span) {
933-
return errors.Errorf("overlapping an unreplicated rangeID span")
934-
}
935-
936-
return nil
937-
}
938-
939-
// overlapsStoreLocalKeys returns an error if the provided span overlaps
940-
// with any store local keys.
941-
// Note that we could receive the span with a nil startKey, which has a special
942-
// meaning that the span represents: [endKey.Prev(), endKey).
943-
func overlapsStoreLocalKeys(span spanset.TrickySpan) error {
944-
localStoreSpan := roachpb.Span{
945-
Key: keys.LocalStorePrefix,
946-
EndKey: keys.LocalStoreMax,
947-
}
948-
949-
if spanset.Overlaps(localStoreSpan, span) {
950-
return errors.Errorf("overlaps with store local keys")
951-
}
952-
953-
return nil
954-
}

0 commit comments

Comments
 (0)