Skip to content

Commit c3b2059

Browse files
craig[bot]miraradeva
andcommitted
Merge #156088
156088: kvnemesis: address fault mode todos r=stevendanna a=miraradeva This commit addresses TODOs left after introducing network partitions and node restarts to kvnemesis: - All fault mode tests run with 4 nodes to ensure that liveness tests can always maintain a majority quorum of replicas on nodes 1 and 2. If the tests run with 5 nodes, we need to take extra care to ensure 3 replicas are healthy for all RF=5 ranges (e.g. the systems ranges). - Splits are now allowed in both partition and restart liveness modes. Previously, if r3 was unavailable, the range ID allocator would get stuck retrying the increment operation that generates a new range ID. This commit ensures all systems ranges are available in liveness mode. - Lease transafers are now allowed in partition liveness mode. It's not clear what changed but the failure doesn't repro anymore. Will investigate more if it fails in CI. Part of: #64828 Part of: #114814 Release note: None Co-authored-by: Mira Radeva <[email protected]>
2 parents 7487733 + ba76a26 commit c3b2059

File tree

5 files changed

+38
-51
lines changed

5 files changed

+38
-51
lines changed

pkg/kv/kvnemesis/kvnemesis_test.go

Lines changed: 16 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ func TestKVNemesisMultiNode_Partition_Safety(t *testing.T) {
470470
defer log.Scope(t).Close(t)
471471

472472
testKVNemesisImpl(t, kvnemesisTestCfg{
473-
numNodes: 5,
473+
numNodes: 4,
474474
numSteps: defaultNumSteps,
475475
concurrency: 5,
476476
seedOverride: 0,
@@ -481,16 +481,6 @@ func TestKVNemesisMultiNode_Partition_Safety(t *testing.T) {
481481
testGeneratorConfig: func(cfg *GeneratorConfig) {
482482
cfg.Ops.Fault.AddNetworkPartition = 1
483483
cfg.Ops.Fault.RemoveNetworkPartition = 1
484-
// TODO(mira): DeleteRangeUsingTombstone and AddSSTable are always
485-
// non-transactional, and as such are susceptible to double-application.
486-
// The cluster setting kvcoord.NonTransactionalWritesNotIdempotent is
487-
// enabled for this test to protect against double-application, but these
488-
// requests don't propagate the flag AmbiguousReplayProtection to ensure
489-
// the second application fails. We should fix this.
490-
cfg.Ops.DB.DeleteRangeUsingTombstone = 0
491-
cfg.Ops.DB.AddSSTable = 0
492-
// The same issue above occurs for non-transactional DeleteRange requests.
493-
cfg.Ops.DB.DeleteRange = 0
494484
},
495485
})
496486
}
@@ -500,7 +490,7 @@ func TestKVNemesisMultiNode_Partition_Liveness(t *testing.T) {
500490
defer log.Scope(t).Close(t)
501491

502492
testKVNemesisImpl(t, kvnemesisTestCfg{
503-
numNodes: 5,
493+
numNodes: 4,
504494
numSteps: defaultNumSteps,
505495
concurrency: 5,
506496
seedOverride: 0,
@@ -516,20 +506,9 @@ func TestKVNemesisMultiNode_Partition_Liveness(t *testing.T) {
516506
// constraints (at least one replica on nodes 1 and 2).
517507
cfg.Ops.ChangeReplicas = ChangeReplicasConfig{}
518508
// Epoch leases can experience indefinite unavailability in the case of a
519-
// leader-leaseholder split and a network partition, so only leader leases
520-
// are allowed.
509+
// leader-leaseholder split and a network partition. This test starts off
510+
// with leader leases, and lease type changes are disallowed.
521511
cfg.Ops.ChangeSetting = ChangeSettingConfig{}
522-
// TODO(mira): Transfers can result in RUEs because in the intermediate
523-
// expiration-lease state, a request can get stuck holding latches until
524-
// the replica circuit breaker trips and poisons the latches. This results
525-
// in RUEs returned to the client. The behavior is expected; we can enable
526-
// this setting if we allow the test to tolerate these RUEs.
527-
cfg.Ops.ChangeLease = ChangeLeaseConfig{}
528-
// TODO(mira): We should investigate splits more. So far I've seen then
529-
// fail for two reasons: (1) r1 can become uvavailable (we can fix this by
530-
// setting the right zone configs), and (2) if a partition races with the
531-
// split, the range ID allocator can get stuck waiting for a response.
532-
cfg.Ops.Split = SplitConfig{}
533512
},
534513
})
535514
}
@@ -559,9 +538,6 @@ func TestKVNemesisMultiNode_Restart_Liveness(t *testing.T) {
559538
// Disallow replica changes because they interfere with the zone config
560539
// constraints (at least one replica on nodes 1 and 2).
561540
cfg.Ops.ChangeReplicas = ChangeReplicasConfig{}
562-
// TODO(mira): Similar issue to Partition_Liveness, except the failure
563-
// mode (2) here looks like "could not allocate ID; system is draining".
564-
cfg.Ops.Split = SplitConfig{}
565541
},
566542
})
567543
}
@@ -817,22 +793,17 @@ func setAndVerifyZoneConfigs(
817793
voter_constraints = '{"+node=n1": 1, "+node=n2": 1}'`,
818794
)
819795

820-
// Ensure the liveness and meta ranges are also constrained appropriately.
821-
sqlRunner.Exec(
822-
t, `ALTER RANGE meta CONFIGURE ZONE USING
823-
num_replicas = 3,
824-
num_voters = 3,
825-
constraints = '{"+node=n1": 1, "+node=n2": 1}',
826-
voter_constraints = '{"+node=n1": 1, "+node=n2": 1}'`,
827-
)
828-
829-
sqlRunner.Exec(
830-
t, `ALTER RANGE liveness CONFIGURE ZONE USING
831-
num_replicas = 3,
832-
num_voters = 3,
833-
constraints = '{"+node=n1": 1, "+node=n2": 1}',
834-
voter_constraints = '{"+node=n1": 1, "+node=n2": 1}'`,
835-
)
796+
// Ensure the liveness, meta and system ranges are also constrained.
797+
systemRanges := []string{"meta", "liveness", "system"}
798+
for _, r := range systemRanges {
799+
sqlRunner.Exec(
800+
t, fmt.Sprintf(`ALTER RANGE %s CONFIGURE ZONE USING
801+
num_replicas = 3,
802+
num_voters = 3,
803+
constraints = '{"+node=n1": 1, "+node=n2": 1}',
804+
voter_constraints = '{"+node=n1": 1, "+node=n2": 1}'`, r),
805+
)
806+
}
836807

837808
// Wait for zone configs to propagate to all span config subscribers.
838809
require.NoError(t, tc.WaitForZoneConfigPropagation())
@@ -853,7 +824,7 @@ func setAndVerifyZoneConfigs(
853824
Key: desc.StartKey.AsRawKey(),
854825
EndKey: desc.EndKey.AsRawKey(),
855826
}
856-
if replicaSpan.Overlaps(dataSpan) || desc.RangeID <= 2 {
827+
if replicaSpan.Overlaps(dataSpan) || desc.RangeID <= 3 {
857828
overlappingReplicas = append(overlappingReplicas, replica)
858829
}
859830
return true // continue

pkg/kv/kvnemesis/validator.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -489,11 +489,13 @@ func (v *validator) processOp(op Operation) {
489489
break
490490
}
491491
readObservation := &observedRead{Key: t.Key}
492+
var shouldObserveRead bool
492493
writeObservation := &observedWrite{
493494
Key: t.Key,
494495
Seq: t.Seq,
495496
Value: roachpb.MakeValueFromString(t.Value()),
496497
}
498+
var shouldObserveWrite bool
497499
// Consider two cases based on whether the CPut hit a ConditionFailedError.
498500
err := errorFromResult(t.Result)
499501
if e := (*kvpb.ConditionFailedError)(nil); errors.As(err, &e) {
@@ -504,7 +506,7 @@ func (v *validator) processOp(op Operation) {
504506
observedVal.RawBytes = e.ActualValue.RawBytes
505507
}
506508
readObservation.Value = observedVal
507-
v.curObservations = append(v.curObservations, readObservation)
509+
shouldObserveRead = true
508510
} else {
509511
// If the CPut succeeded, the expected value is observed, and the CPut's
510512
// write is also observed.
@@ -517,11 +519,21 @@ func (v *validator) processOp(op Operation) {
517519
observedVal = roachpb.MakeValueFromBytes(t.ExpVal)
518520
}
519521
readObservation.Value = observedVal
520-
v.curObservations = append(v.curObservations, readObservation)
522+
shouldObserveRead = true
521523
}
522524
if sv, ok := v.tryConsumeWrite(t.Key, t.Seq); ok {
523525
writeObservation.Timestamp = sv.Timestamp
524526
}
527+
shouldObserveWrite = true
528+
}
529+
// The read observation should be added before the write observation, since
530+
// that's the order in which the CPut executed. Moreover, the CPut read is
531+
// always non-locking, so if the observation filter is observeLocking, we
532+
// won't be adding it.
533+
if shouldObserveRead && v.observationFilter != observeLocking {
534+
v.curObservations = append(v.curObservations, readObservation)
535+
}
536+
if shouldObserveWrite {
525537
v.curObservations = append(v.curObservations, writeObservation)
526538
}
527539
if v.buffering == bufferingSingle {

pkg/kv/kvserver/idalloc/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ go_library(
88
deps = [
99
"//pkg/base",
1010
"//pkg/kv",
11+
"//pkg/kv/kvpb",
1112
"//pkg/roachpb",
1213
"//pkg/util/log",
1314
"//pkg/util/retry",
@@ -28,6 +29,7 @@ go_test(
2829
deps = [
2930
"//pkg/keys",
3031
"//pkg/kv/kvclient/kvcoord",
32+
"//pkg/kv/kvpb",
3133
"//pkg/roachpb",
3234
"//pkg/security/securityassets",
3335
"//pkg/security/securitytest",

pkg/kv/kvserver/idalloc/id_alloc.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/cockroachdb/cockroach/pkg/base"
1313
"github.com/cockroachdb/cockroach/pkg/kv"
14+
"github.com/cockroachdb/cockroach/pkg/kv/kvpb"
1415
"github.com/cockroachdb/cockroach/pkg/roachpb"
1516
"github.com/cockroachdb/cockroach/pkg/util/log"
1617
"github.com/cockroachdb/cockroach/pkg/util/retry"
@@ -84,7 +85,7 @@ func (ia *Allocator) Allocate(ctx context.Context) (int64, error) {
8485
case id := <-ia.ids:
8586
// when the channel is closed, the zero value is returned.
8687
if id == 0 {
87-
return id, errors.Errorf("could not allocate ID; system is draining")
88+
return id, &kvpb.NodeUnavailableError{}
8889
}
8990
return id, nil
9091
case <-ctx.Done():

pkg/kv/kvserver/idalloc/id_alloc_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
"github.com/cockroachdb/cockroach/pkg/keys"
1616
"github.com/cockroachdb/cockroach/pkg/kv/kvclient/kvcoord"
17+
"github.com/cockroachdb/cockroach/pkg/kv/kvpb"
1718
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/idalloc"
1819
"github.com/cockroachdb/cockroach/pkg/roachpb"
1920
// Import to set ZoneConfigHook.
@@ -232,9 +233,9 @@ func TestAllocateWithStopper(t *testing.T) {
232233
s, idAlloc := newTestAllocator(t)
233234
s.Stop() // not deferred.
234235

235-
if _, err := idAlloc.Allocate(context.Background()); !testutils.IsError(err, "system is draining") {
236-
t.Errorf("unexpected error: %+v", err)
237-
}
236+
_, err := idAlloc.Allocate(context.Background())
237+
require.Error(t, err)
238+
require.ErrorIs(t, err, &kvpb.NodeUnavailableError{})
238239
}
239240

240241
// TestLostWriteAssertion makes sure that the Allocator performs a best-effort

0 commit comments

Comments
 (0)