Skip to content

Commit 9dbc353

Browse files
craig[bot]stevendanna
craig[bot]
andcommitted
Merge #142999
142999: kvserver: actually test lock replication on merge r=yuzefovich a=stevendanna We modified this test during review and I made a mistake that meant that it always passed because they key being locked wasn't actually in the range being merged. Here, I fixed that and along they way also make sure that we run the consistency checker on the range after merging. Epic: none Release note: None Co-authored-by: Steven Danna <[email protected]>
2 parents 8ef4468 + 13d75f5 commit 9dbc353

File tree

6 files changed

+120
-57
lines changed

6 files changed

+120
-57
lines changed

pkg/kv/kvnemesis/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ go_library(
3030
"//pkg/kv/kvserver/concurrency/isolation",
3131
"//pkg/kv/kvserver/concurrency/lock",
3232
"//pkg/kv/kvserver/liveness",
33+
"//pkg/kv/kvtestutils",
3334
"//pkg/roachpb",
3435
"//pkg/settings/cluster",
3536
"//pkg/sql/catalog/bootstrap",

pkg/kv/kvnemesis/env.go

+2-49
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ import (
99
"context"
1010
gosql "database/sql"
1111
"fmt"
12-
"regexp"
1312
"time"
1413

1514
"github.com/cockroachdb/cockroach-go/v2/crdb"
1615
"github.com/cockroachdb/cockroach/pkg/config/zonepb"
17-
kvpb "github.com/cockroachdb/cockroach/pkg/kv/kvpb"
16+
"github.com/cockroachdb/cockroach/pkg/kv/kvtestutils"
1817
"github.com/cockroachdb/cockroach/pkg/roachpb"
1918
"github.com/cockroachdb/cockroach/pkg/util/protoutil"
2019
"github.com/cockroachdb/errors"
@@ -46,53 +45,7 @@ func (e *Env) anyNode() *gosql.DB {
4645
// as a list of errors. RANGE_CONSISTENT_STATS_ESTIMATED is considered a
4746
// success, since stats estimates are fine (if unfortunate).
4847
func (e *Env) CheckConsistency(ctx context.Context, span roachpb.Span) []error {
49-
rows, err := e.anyNode().QueryContext(ctx, fmt.Sprintf(`
50-
SELECT range_id, start_key_pretty, status, detail
51-
FROM crdb_internal.check_consistency(false, b'\x%x', b'\x%x')
52-
ORDER BY range_id ASC`,
53-
span.Key, span.EndKey,
54-
))
55-
if err != nil {
56-
return []error{err}
57-
}
58-
defer rows.Close()
59-
60-
var failures []error
61-
for rows.Next() {
62-
var rangeID int
63-
var key, status, detail string
64-
if err := rows.Scan(&rangeID, &key, &status, &detail); err != nil {
65-
return []error{err}
66-
}
67-
// NB: There's a known issue that can result in a 10-byte discrepancy in
68-
// SysBytes. See:
69-
// https://github.com/cockroachdb/cockroach/issues/93896
70-
//
71-
// This isn't critical, so we ignore such discrepancies.
72-
if status == kvpb.CheckConsistencyResponse_RANGE_CONSISTENT_STATS_INCORRECT.String() {
73-
m := regexp.MustCompile(`.*\ndelta \(stats-computed\): \{(.*)\}`).FindStringSubmatch(detail)
74-
if len(m) > 1 {
75-
delta := m[1]
76-
// Strip out LastUpdateNanos and all zero-valued fields.
77-
delta = regexp.MustCompile(`LastUpdateNanos:\d+`).ReplaceAllString(delta, "")
78-
delta = regexp.MustCompile(`\S+:0\b`).ReplaceAllString(delta, "")
79-
if regexp.MustCompile(`^\s*SysBytes:-?10\s*$`).MatchString(delta) {
80-
continue
81-
}
82-
}
83-
}
84-
switch status {
85-
case kvpb.CheckConsistencyResponse_RANGE_INDETERMINATE.String():
86-
// Can't do anything, so let it slide.
87-
case kvpb.CheckConsistencyResponse_RANGE_CONSISTENT.String():
88-
// Good.
89-
case kvpb.CheckConsistencyResponse_RANGE_CONSISTENT_STATS_ESTIMATED.String():
90-
// Ok.
91-
default:
92-
failures = append(failures, errors.Errorf("range %d (%s) %s:\n%s", rangeID, key, status, detail))
93-
}
94-
}
95-
return failures
48+
return kvtestutils.CheckConsistency(ctx, e.anyNode(), span)
9649
}
9750

9851
// SetClosedTimestampInterval sets the kv.closed_timestamp.target_duration

pkg/kv/kvserver/client_replica_test.go

+19-3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/protectedts/ptutil"
4040
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/raftutil"
4141
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/stateloader"
42+
"github.com/cockroachdb/cockroach/pkg/kv/kvtestutils"
4243
"github.com/cockroachdb/cockroach/pkg/raft"
4344
"github.com/cockroachdb/cockroach/pkg/raft/raftpb"
4445
"github.com/cockroachdb/cockroach/pkg/roachpb"
@@ -5889,20 +5890,28 @@ func TestMergeReplicatesLocks(t *testing.T) {
58895890

58905891
for _, b := range []bool{true, false} {
58915892
name := "lhs-lock"
5892-
lockKey := lhsKey
5893+
lockKeySuffix := lhsKey
58935894
if b {
58945895
name = "rhs-lock"
5895-
lockKey = rhsKey
5896+
lockKeySuffix = rhsKey
58965897
}
58975898
t.Run(name, func(t *testing.T) {
58985899
tc := testcluster.StartTestCluster(t, 3, base.TestClusterArgs{
58995900
ServerArgs: base.TestServerArgs{
59005901
Settings: st,
59015902
},
59025903
})
5904+
5905+
sql := tc.ServerConn(0)
59035906
defer tc.Stopper().Stop(ctx)
59045907
scratch := tc.ScratchRange(t)
5905-
splitKey := append(scratch[:len(scratch):len(scratch)], splitPoint...)
5908+
mkKey := func(s string) roachpb.Key {
5909+
prefix := scratch.Clone()
5910+
return append(prefix[:len(prefix):len(prefix)], s...)
5911+
}
5912+
5913+
lockKey := mkKey(lockKeySuffix)
5914+
splitKey := mkKey(splitPoint)
59065915
tc.SplitRangeOrFatal(t, splitKey)
59075916
// Write a value for the key because at the moment we don't create locks for
59085917
// non-existent keys.
@@ -5961,6 +5970,13 @@ func TestMergeReplicatesLocks(t *testing.T) {
59615970
t.Log("cancelling txn2")
59625971
txn2Cancel()
59635972
require.NoError(t, g.Wait())
5973+
failures := kvtestutils.CheckConsistency(ctx, sql, roachpb.Span{
5974+
Key: keys.ScratchRangeMin,
5975+
EndKey: keys.ScratchRangeMax,
5976+
})
5977+
for _, err := range failures {
5978+
t.Errorf("consistency failure: %s", err.Error())
5979+
}
59645980
})
59655981
}
59665982
}

pkg/kv/kvtestutils/BUILD.bazel

+11-3
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,22 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
22

33
go_library(
44
name = "kvtestutils",
5-
testonly = 1,
6-
srcs = ["test_utils.go"],
5+
# This package can become testonly when kvnemesis tests are moved
6+
# into a test package.
7+
# testonly = 1,
8+
srcs = [
9+
"consistency.go",
10+
"test_utils.go",
11+
],
712
importpath = "github.com/cockroachdb/cockroach/pkg/kv/kvtestutils",
813
visibility = ["//visibility:public"],
914
deps = [
1015
"//pkg/kv/kvbase",
11-
"//pkg/testutils",
16+
"//pkg/kv/kvpb",
17+
"//pkg/roachpb",
18+
"//pkg/sql/pgwire/pgerror",
1219
"//pkg/util/tracing",
1320
"//pkg/util/tracing/tracingpb",
21+
"@com_github_cockroachdb_errors//:errors",
1422
],
1523
)

pkg/kv/kvtestutils/consistency.go

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2025 The Cockroach Authors.
2+
//
3+
// Use of this software is governed by the CockroachDB Software License
4+
// included in the /LICENSE file.
5+
//
6+
7+
package kvtestutils
8+
9+
import (
10+
"context"
11+
gosql "database/sql"
12+
"fmt"
13+
"regexp"
14+
15+
"github.com/cockroachdb/cockroach/pkg/kv/kvpb"
16+
"github.com/cockroachdb/cockroach/pkg/roachpb"
17+
"github.com/cockroachdb/errors"
18+
)
19+
20+
type querier interface {
21+
QueryContext(ctx context.Context, query string, args ...interface{}) (*gosql.Rows, error)
22+
}
23+
24+
// CheckConsistency runs a consistency check on all ranges in the given span,
25+
// primarily to verify that MVCC stats are accurate. Any failures are returned
26+
// as a list of errors. RANGE_CONSISTENT_STATS_ESTIMATED is considered a
27+
// success, since stats estimates are fine (if unfortunate).
28+
func CheckConsistency(ctx context.Context, db querier, span roachpb.Span) []error {
29+
rows, err := db.QueryContext(ctx, fmt.Sprintf(`
30+
SELECT range_id, start_key_pretty, status, detail
31+
FROM crdb_internal.check_consistency(false, b'\x%x', b'\x%x')
32+
ORDER BY range_id ASC`,
33+
span.Key, span.EndKey,
34+
))
35+
if err != nil {
36+
return []error{err}
37+
}
38+
defer rows.Close()
39+
40+
var failures []error
41+
for rows.Next() {
42+
var rangeID int
43+
var key, status, detail string
44+
if err := rows.Scan(&rangeID, &key, &status, &detail); err != nil {
45+
return []error{err}
46+
}
47+
// NB: There's a known issue that can result in a 10-byte discrepancy in
48+
// SysBytes. See:
49+
// https://github.com/cockroachdb/cockroach/issues/93896
50+
//
51+
// This isn't critical, so we ignore such discrepancies.
52+
if status == kvpb.CheckConsistencyResponse_RANGE_CONSISTENT_STATS_INCORRECT.String() {
53+
m := regexp.MustCompile(`.*\ndelta \(stats-computed\): \{(.*)\}`).FindStringSubmatch(detail)
54+
if len(m) > 1 {
55+
delta := m[1]
56+
// Strip out LastUpdateNanos and all zero-valued fields.
57+
delta = regexp.MustCompile(`LastUpdateNanos:\d+`).ReplaceAllString(delta, "")
58+
delta = regexp.MustCompile(`\S+:0\b`).ReplaceAllString(delta, "")
59+
if regexp.MustCompile(`^\s*SysBytes:-?10\s*$`).MatchString(delta) {
60+
continue
61+
}
62+
}
63+
}
64+
switch status {
65+
case kvpb.CheckConsistencyResponse_RANGE_INDETERMINATE.String():
66+
// Can't do anything, so let it slide.
67+
case kvpb.CheckConsistencyResponse_RANGE_CONSISTENT.String():
68+
// Good.
69+
case kvpb.CheckConsistencyResponse_RANGE_CONSISTENT_STATS_ESTIMATED.String():
70+
// Ok.
71+
default:
72+
failures = append(failures, errors.Errorf("range %d (%s) %s:\n%s", rangeID, key, status, detail))
73+
}
74+
}
75+
return failures
76+
}

pkg/kv/kvtestutils/test_utils.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
package kvtestutils
77

88
import (
9+
"regexp"
910
"strings"
1011

1112
"github.com/cockroachdb/cockroach/pkg/kv/kvbase"
12-
"github.com/cockroachdb/cockroach/pkg/testutils"
13+
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
1314
"github.com/cockroachdb/cockroach/pkg/util/tracing"
1415
"github.com/cockroachdb/cockroach/pkg/util/tracing/tracingpb"
1516
)
@@ -54,6 +55,10 @@ func OnlyFollowerReads(rec tracingpb.Recording) bool {
5455
// kvserver.Is{Retryable,Illegal}ReplicationChangeError,
5556
// which avoids string matching.
5657
func IsExpectedRelocateError(err error) bool {
58+
if err == nil {
59+
return false
60+
}
61+
5762
allowlist := []string{
5863
"descriptor changed",
5964
"unable to remove replica .* which is not present",
@@ -74,5 +79,9 @@ func IsExpectedRelocateError(err error) bool {
7479
"cannot remove learner while snapshot is in flight", // https://github.com/cockroachdb/cockroach/issues/79887
7580
}
7681
pattern := "(" + strings.Join(allowlist, "|") + ")"
77-
return testutils.IsError(err, pattern)
82+
matched, merr := regexp.MatchString(pattern, pgerror.FullError(err))
83+
if merr != nil {
84+
return false
85+
}
86+
return matched
7887
}

0 commit comments

Comments
 (0)