Skip to content

Commit

Permalink
asim: enable random zone config event generation
Browse files Browse the repository at this point in the history
Previously, zone config event generation used hardcoded span configurations.
This limits our ability to test the allocator more thoroughly.

To improve this, this patch enables random span configs to be generated and
applied as part of the simulation. These configurations are generated by
randomly selecting the primary region, region V.S. zone survival goal, and
leaseholder preference.

```
The following command is now supported:
"rand_events" [cycle_via_random_survival_goals]
```

Part of: #106192
Release Note: none
Epic: none
  • Loading branch information
wenyihu6 authored and kvoli committed Sep 25, 2023
1 parent 9f1ee63 commit 91adebd
Show file tree
Hide file tree
Showing 7 changed files with 291 additions and 3 deletions.
5 changes: 5 additions & 0 deletions pkg/kv/kvserver/asim/tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ go_library(
"output.go",
"rand_framework.go",
"rand_gen.go",
"rand_util.go",
],
importpath = "github.com/cockroachdb/cockroach/pkg/kv/kvserver/asim/tests",
visibility = ["//visibility:public"],
Expand All @@ -20,6 +21,10 @@ go_library(
"//pkg/kv/kvserver/asim/scheduled",
"//pkg/kv/kvserver/asim/state",
"//pkg/roachpb",
"//pkg/sql",
"//pkg/sql/catalog/catpb",
"//pkg/sql/catalog/descpb",
"//pkg/sql/catalog/multiregion",
],
)

Expand Down
4 changes: 3 additions & 1 deletion pkg/kv/kvserver/asim/tests/rand_framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,9 @@ func (f randTestingFramework) randomEventSeriesGen(
) gen.StaticEvents {
switch eventsType := f.s.eventGen.eventsType; eventsType {
case cycleViaHardcodedSurvivalGoals:
return generateSurvivalGoalsEvents(cluster.Regions(), settings.Settings.StartTime, f.s.eventGen.durationToAssertOnEvent)
return generateHardcodedSurvivalGoalsEvents(cluster.Regions(), settings.Settings.StartTime, f.s.eventGen.durationToAssertOnEvent)
case cycleViaRandomSurvivalGoals:
return generateRandomSurvivalGoalsEvents(cluster.Regions(), settings.Settings.StartTime, f.s.eventGen.durationToAssertOnEvent, f.s.duration, f.s.randSource)
default:
panic("unknown event series type")
}
Expand Down
103 changes: 102 additions & 1 deletion pkg/kv/kvserver/asim/tests/rand_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import (
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/asim/gen"
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/asim/state"
"github.com/cockroachdb/cockroach/pkg/roachpb"
"github.com/cockroachdb/cockroach/pkg/sql"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/catpb"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/multiregion"
)

// randomClusterInfoGen returns a randomly picked predefined configuration.
Expand Down Expand Up @@ -272,6 +276,9 @@ const (
// Cycle through predefined region and zone survival configurations. Note
// that only cluster_gen_type=multi_region can execute this event.
cycleViaHardcodedSurvivalGoals eventSeriesType = iota
// Cycle through randomly generated region and zone survival configurations.
// Note that only cluster_gen_type=multi_region can execute this event.
cycleViaRandomSurvivalGoals
)

type eventGenSettings struct {
Expand All @@ -288,6 +295,8 @@ func (e eventSeriesType) String() string {
switch e {
case cycleViaHardcodedSurvivalGoals:
return "cycle_via_hardcoded_survival_goals"
case cycleViaRandomSurvivalGoals:
return "cycle_via_random_survival_goals"
default:
panic("unknown event series type")
}
Expand All @@ -297,6 +306,8 @@ func getEventSeriesType(s string) eventSeriesType {
switch s {
case "cycle_via_hardcoded_survival_goals":
return cycleViaHardcodedSurvivalGoals
case "cycle_via_random_survival_goals":
return cycleViaRandomSurvivalGoals
default:
panic(fmt.Sprintf("unknown event series type: %s", s))
}
Expand Down Expand Up @@ -327,7 +338,12 @@ func constructSetZoneConfigEventWithConformanceAssertion(
}
}

func generateSurvivalGoalsEvents(
// generateHardcodedSurvivalGoalsEvents sets up two MutationWithAssertionEvents.
// The first mutation event starts at the beginning, followed by an assertion
// event after some time (durationToAssert). Right after that, the second
// MutationWithAssertionEvent happens. Both of these mutation events are
// SetSpanConfig events which use two hardcoded zone configurations.
func generateHardcodedSurvivalGoalsEvents(
regions []state.Region, startTime time.Time, durationToAssert time.Duration,
) gen.StaticEvents {
if len(regions) < 3 {
Expand All @@ -354,3 +370,88 @@ func generateSurvivalGoalsEvents(
}
return eventGen
}

// getRegionNames takes a list of regions and returns the extracted region names
// in the catpb.RegionNames format.
func getRegionNames(regions []state.Region) (regionNames catpb.RegionNames) {
for _, r := range regions {
regionNames = append(regionNames, catpb.RegionName(r.Name))
}
return regionNames
}

// randomlySelectSurvivalGoal randomly selects between SurvivalGoal_ZONE_FAILURE
// and SurvivalGoal_REGION_FAILURE.
func randomlySelectSurvivalGoal(randSource *rand.Rand) descpb.SurvivalGoal {
if randBool(randSource) {
return descpb.SurvivalGoal_ZONE_FAILURE
} else {
return descpb.SurvivalGoal_REGION_FAILURE
}
}

// randomlySelectDataPlacement randomly selects between DataPlacement_DEFAULT
// and DataPlacement_RESTRICTED.
func randomlySelectDataPlacement(randSource *rand.Rand) descpb.DataPlacement {
if randBool(randSource) {
return descpb.DataPlacement_DEFAULT
} else {
return descpb.DataPlacement_RESTRICTED
}
}

// generateRandomSurvivalGoalsEvents generates SetSpanConfig events spaced at
// intervals defined by durationToAssert from the start time. These events apply
// a randomly generated zone configuration followed by an assertion event. Note
// that these random configurations might be unsatisfiable under the cluster
// setup.
func generateRandomSurvivalGoalsEvents(
regions []state.Region,
startTime time.Time,
durationToAssert time.Duration,
duration time.Duration,
randSource *rand.Rand,
) gen.StaticEvents {
if len(regions) < 3 {
panic("iterate all zone configs is only possible for clusters with > 3 regions")
}
eventGen := gen.NewStaticEventsWithNoEvents()
delay := time.Duration(0)

span := roachpb.Span{
Key: state.MinKey.ToRKey().AsRawKey(),
EndKey: state.MaxKey.ToRKey().AsRawKey(),
}

regionNames := getRegionNames(regions)
for delay < duration {
randomRegionIndex := randIndex(randSource, len(regions))
randomPrimaryRegion := regions[randomRegionIndex].Name
randomSurvivalGoal := randomlySelectSurvivalGoal(randSource)
randomDataPlacement := randomlySelectDataPlacement(randSource)
rc := multiregion.MakeRegionConfig(
regionNames,
catpb.RegionName(randomPrimaryRegion),
randomSurvivalGoal,
descpb.InvalidID,
randomDataPlacement,
nil,
descpb.ZoneConfigExtensions{},
)

zoneConfig, convertErr := sql.TestingConvertRegionToZoneConfig(rc)
if convertErr != nil {
panic(fmt.Sprintf("failed to convert region to zone config %s", convertErr.Error()))
}
if validateErr := zoneConfig.Validate(); validateErr != nil {
panic(fmt.Sprintf("zone config generated is invalid %s", validateErr.Error()))
}
if validateErr := zoneConfig.EnsureFullyHydrated(); validateErr != nil {
panic(fmt.Sprintf("zone config generated is not fully hydrated %s", validateErr.Error()))
}
eventGen.ScheduleMutationWithAssertionEvent(startTime, delay,
constructSetZoneConfigEventWithConformanceAssertion(span, zoneConfig, durationToAssert))
delay += durationToAssert
}
return eventGen
}
3 changes: 2 additions & 1 deletion pkg/kv/kvserver/asim/tests/rand_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ const (
// - height (default value is 15): height of the plot
// - width (default value is 80): width of the plot

// 5. "rand_events" [type=<string>{cycle_via_hardcoded_survival_goals}]
// 5. "rand_events" [type=<string>{cycle_via_hardcoded_survival_goals,
// cycle_via_random_survival_goals}]
// [duration_to_assert_on_event=<time.Duration>]
// e.g. rand_events type=cycle_via_hardcoded_survival_goals duration=5m
// - rand_events: generates interesting event series to be scheduled in the
Expand Down
29 changes: 29 additions & 0 deletions pkg/kv/kvserver/asim/tests/rand_util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2023 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package tests

import "math/rand"

// randBool randomly picks between true and false.
func randBool(randSource *rand.Rand) bool {
return randSource.Intn(2) == 0
}

// randBetweenMinMaxExclusive randomly selects a number∈[min, max).
func randBetweenMinMaxExclusive(randSource *rand.Rand, min int, max int) int {
return randSource.Intn(max-min) + min
}

// randIndex randomly selects an index given the length of an array ∈[0,
// lenOfArr).
func randIndex(randSource *rand.Rand, lenOfArr int) int {
return randBetweenMinMaxExclusive(randSource, 0, lenOfArr)
}
131 changes: 131 additions & 0 deletions pkg/kv/kvserver/asim/tests/testdata/rand/rand_event
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
rand_cluster cluster_gen_type=multi_region
----

change_static_option ranges=1
----

rand_events type=cycle_via_random_survival_goals duration_to_assert_on_event=5m
----

eval duration=60m num_iterations=1 verbose=(all)
----
test settings
num_iterations=1 duration=1h0m0s
----------------------------------
generating cluster configurations using randomized option
cluster_gen_type=multi_region
generating ranges configurations using static option
placement_type=even, ranges=1, key_space=200000, replication_factor=3, bytes=0
generating load configurations using static option
rw_ratio=0.00, rate=0.00, min_block=1, max_block=1, min_key=1, max_key=200000, skewed_access=false
generating events configurations using randomized option
duration_to_assert_on_event=5m0s, type=cycle_via_random_survival_goals
generating settings configurations using static option
----------------------------------
sample1: start running
configurations generated using seed 7894140303635748408
loaded cluster with
region:US_East [zone=US_East_1(nodes=1,stores=0), zone=US_East_2(nodes=2,stores=0), zone=US_East_3(nodes=3,stores=0), zone=US_East_3(nodes=10,stores=0)]
region:US_West [zone=US_West_1(nodes=2,stores=0)]
region:EU [zone=EU_1(nodes=3,stores=0), zone=EU_2(nodes=3,stores=0), zone=EU_3(nodes=4,stores=0)]
basic ranges with placement_type=even, ranges=1, key_space=200000, replication_factor=3, bytes=0
basic load with rw_ratio=0.00, rate=0.00, skewed_access=false, min_block_size=1, max_block_size=1, min_key=1, max_key=200000
number of mutation events=12, number of assertion events=12
initial state at 2022-03-21 11:00:00:
stores(28)=[s1n1=(replicas(0)),s2n2=(replicas(0)),s3n3=(replicas(0)),s4n4=(replicas(1)),s5n5=(replicas(0)),s6n6=(replicas(0)),s7n7=(replicas(0)),s8n8=(replicas(0)),s9n9=(replicas(0)),s10n10=(replicas(0)),s11n11=(replicas(0)),s12n12=(replicas(0)),s13n13=(replicas(0)),s14n14=(replicas(0)),s15n15=(replicas(0)),s16n16=(replicas(0)),s17n17=(replicas(1)),s18n18=(replicas(0)),s19n19=(replicas(0)),s20n20=(replicas(0)),s21n21=(replicas(0)),s22n22=(replicas(1)),s23n23=(replicas(0)),s24n24=(replicas(0)),s25n25=(replicas(1)),s26n26=(replicas(1)),s27n27=(replicas(0)),s28n28=(replicas(0))]
topology:
EU
EU_1
│ └── [19 20 21]
EU_2
│ └── [22 23 24]
EU_3
│ └── [25 26 27 28]
US_East
US_East_1
│ └── [1]
US_East_2
│ └── [2 3]
US_East_3
│ └── [4 5 6 7 8 9 10 11 12 13 14 15 16]
US_West
US_West_1
└── [17 18]
24 events executed:
executed at: 2022-03-21 11:00:00
event: set span config event with span={0000000000-9999999999}, config=range_min_bytes:134217728 range_max_bytes:536870912 gc_policy:<ttl_seconds:14400 > num_replicas:5 num_voters:3 constraints:<num_replicas:1 constraints:<key:"region" value:"US_East" > > constraints:<num_replicas:1 constraints:<key:"region" value:"US_West" > > constraints:<num_replicas:1 constraints:<key:"region" value:"EU" > > voter_constraints:<constraints:<key:"region" value:"EU" > > lease_preferences:<constraints:<key:"region" value:"EU" > >
executed at: 2022-03-21 11:05:00
event: assertion checking event
1. assertion=conformance unavailable=0 under=0 over=0 violating=0
passed
executed at: 2022-03-21 11:05:00
event: set span config event with span={0000000000-9999999999}, config=range_min_bytes:134217728 range_max_bytes:536870912 gc_policy:<ttl_seconds:14400 > num_replicas:5 num_voters:5 voter_constraints:<num_replicas:2 constraints:<key:"region" value:"US_West" > > lease_preferences:<constraints:<key:"region" value:"US_West" > >
executed at: 2022-03-21 11:10:00
event: assertion checking event
1. assertion=conformance unavailable=0 under=0 over=0 violating=0
passed
executed at: 2022-03-21 11:10:00
event: set span config event with span={0000000000-9999999999}, config=range_min_bytes:134217728 range_max_bytes:536870912 gc_policy:<ttl_seconds:14400 > num_replicas:3 num_voters:3 voter_constraints:<constraints:<key:"region" value:"EU" > > lease_preferences:<constraints:<key:"region" value:"EU" > >
executed at: 2022-03-21 11:15:00
event: assertion checking event
1. assertion=conformance unavailable=0 under=0 over=0 violating=0
passed
executed at: 2022-03-21 11:15:00
event: set span config event with span={0000000000-9999999999}, config=range_min_bytes:134217728 range_max_bytes:536870912 gc_policy:<ttl_seconds:14400 > num_replicas:5 num_voters:5 voter_constraints:<num_replicas:2 constraints:<key:"region" value:"US_West" > > lease_preferences:<constraints:<key:"region" value:"US_West" > >
executed at: 2022-03-21 11:20:00
event: assertion checking event
1. assertion=conformance unavailable=0 under=0 over=0 violating=0
passed
executed at: 2022-03-21 11:20:00
event: set span config event with span={0000000000-9999999999}, config=range_min_bytes:134217728 range_max_bytes:536870912 gc_policy:<ttl_seconds:14400 > num_replicas:3 num_voters:3 voter_constraints:<constraints:<key:"region" value:"US_West" > > lease_preferences:<constraints:<key:"region" value:"US_West" > >
executed at: 2022-03-21 11:25:00
event: assertion checking event
1. assertion=conformance unavailable=0 under=0 over=0 violating=0
failed: conformance unavailable=0 under=0 over=0 violating=0
actual unavailable=0 under=0, over=0 violating=1
violating constraints:
r1:{0000000000-9999999999} [(n17,s17):14, (n18,s18):13, (n22,s22):7] applying num_voters=3 voter_constraints=[+region=US_West] lease_preferences=[+region=US_West]
executed at: 2022-03-21 11:25:00
event: set span config event with span={0000000000-9999999999}, config=range_min_bytes:134217728 range_max_bytes:536870912 gc_policy:<ttl_seconds:14400 > num_replicas:5 num_voters:5 voter_constraints:<num_replicas:2 constraints:<key:"region" value:"EU" > > lease_preferences:<constraints:<key:"region" value:"EU" > >
executed at: 2022-03-21 11:30:00
event: assertion checking event
1. assertion=conformance unavailable=0 under=0 over=0 violating=0
passed
executed at: 2022-03-21 11:30:00
event: set span config event with span={0000000000-9999999999}, config=range_min_bytes:134217728 range_max_bytes:536870912 gc_policy:<ttl_seconds:14400 > num_replicas:5 num_voters:3 constraints:<num_replicas:1 constraints:<key:"region" value:"US_East" > > constraints:<num_replicas:1 constraints:<key:"region" value:"US_West" > > constraints:<num_replicas:1 constraints:<key:"region" value:"EU" > > voter_constraints:<constraints:<key:"region" value:"EU" > > lease_preferences:<constraints:<key:"region" value:"EU" > >
executed at: 2022-03-21 11:35:00
event: assertion checking event
1. assertion=conformance unavailable=0 under=0 over=0 violating=0
passed
executed at: 2022-03-21 11:35:00
event: set span config event with span={0000000000-9999999999}, config=range_min_bytes:134217728 range_max_bytes:536870912 gc_policy:<ttl_seconds:14400 > num_replicas:5 num_voters:5 constraints:<num_replicas:1 constraints:<key:"region" value:"US_East" > > constraints:<num_replicas:1 constraints:<key:"region" value:"US_West" > > constraints:<num_replicas:1 constraints:<key:"region" value:"EU" > > voter_constraints:<num_replicas:2 constraints:<key:"region" value:"US_West" > > lease_preferences:<constraints:<key:"region" value:"US_West" > >
executed at: 2022-03-21 11:40:00
event: assertion checking event
1. assertion=conformance unavailable=0 under=0 over=0 violating=0
passed
executed at: 2022-03-21 11:40:00
event: set span config event with span={0000000000-9999999999}, config=range_min_bytes:134217728 range_max_bytes:536870912 gc_policy:<ttl_seconds:14400 > num_replicas:3 num_voters:3 voter_constraints:<constraints:<key:"region" value:"EU" > > lease_preferences:<constraints:<key:"region" value:"EU" > >
executed at: 2022-03-21 11:45:00
event: assertion checking event
1. assertion=conformance unavailable=0 under=0 over=0 violating=0
passed
executed at: 2022-03-21 11:45:00
event: set span config event with span={0000000000-9999999999}, config=range_min_bytes:134217728 range_max_bytes:536870912 gc_policy:<ttl_seconds:14400 > num_replicas:5 num_voters:5 constraints:<num_replicas:1 constraints:<key:"region" value:"US_East" > > constraints:<num_replicas:1 constraints:<key:"region" value:"US_West" > > constraints:<num_replicas:1 constraints:<key:"region" value:"EU" > > voter_constraints:<num_replicas:2 constraints:<key:"region" value:"US_West" > > lease_preferences:<constraints:<key:"region" value:"US_West" > >
executed at: 2022-03-21 11:50:00
event: assertion checking event
1. assertion=conformance unavailable=0 under=0 over=0 violating=0
passed
executed at: 2022-03-21 11:50:00
event: set span config event with span={0000000000-9999999999}, config=range_min_bytes:134217728 range_max_bytes:536870912 gc_policy:<ttl_seconds:14400 > num_replicas:5 num_voters:5 constraints:<num_replicas:1 constraints:<key:"region" value:"US_East" > > constraints:<num_replicas:1 constraints:<key:"region" value:"US_West" > > constraints:<num_replicas:1 constraints:<key:"region" value:"EU" > > voter_constraints:<num_replicas:2 constraints:<key:"region" value:"EU" > > lease_preferences:<constraints:<key:"region" value:"EU" > >
executed at: 2022-03-21 11:55:00
event: assertion checking event
1. assertion=conformance unavailable=0 under=0 over=0 violating=0
passed
executed at: 2022-03-21 11:55:00
event: set span config event with span={0000000000-9999999999}, config=range_min_bytes:134217728 range_max_bytes:536870912 gc_policy:<ttl_seconds:14400 > num_replicas:5 num_voters:3 constraints:<num_replicas:1 constraints:<key:"region" value:"US_East" > > constraints:<num_replicas:1 constraints:<key:"region" value:"US_West" > > constraints:<num_replicas:1 constraints:<key:"region" value:"EU" > > voter_constraints:<constraints:<key:"region" value:"EU" > > lease_preferences:<constraints:<key:"region" value:"EU" > >
executed at: 2022-03-21 12:00:00
event: assertion checking event
1. assertion=conformance unavailable=0 under=0 over=0 violating=0
passed
sample1: pass
----------------------------------
19 changes: 19 additions & 0 deletions pkg/sql/region_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,25 @@ func makeRequiredConstraintForRegion(r catpb.RegionName) zonepb.Constraint {
}
}

// TestingConvertRegionToZoneConfig converts a given region config into a zone
// configuration, ensuring the result is fully hydrated. Refer to the
// zoneConfigForMultiRegionDatabase function for details on how the conversion
// is made. Note that this should only be used for testing purposes.
func TestingConvertRegionToZoneConfig(
regionConfig multiregion.RegionConfig,
) (zonepb.ZoneConfig, error) {
zc, err := zoneConfigForMultiRegionDatabase(regionConfig)

// Hardcode settings based on DefaultZoneConfig() to ensure that generated
// zone configuration is fully hydrated for AsSpanConfig() conversion.
defaultZoneConfig := zonepb.DefaultZoneConfig()
zc.RangeMinBytes = defaultZoneConfig.RangeMinBytes
zc.RangeMaxBytes = defaultZoneConfig.RangeMaxBytes
zc.GC = defaultZoneConfig.GC
zc.NullVoterConstraintsIsEmpty = defaultZoneConfig.NullVoterConstraintsIsEmpty
return zc, err
}

// zoneConfigForMultiRegionDatabase generates a ZoneConfig stub for a
// multi-region database such that at least one replica (voting or non-voting)
// is constrained to each region defined within the given `regionConfig` and
Expand Down

0 comments on commit 91adebd

Please sign in to comment.