From 67eb8ee159efbcd3ee7c611cae6db2857cd62383 Mon Sep 17 00:00:00 2001 From: Aayush Shah Date: Thu, 19 Nov 2020 15:19:32 -0500 Subject: [PATCH] config: introduce `num_voters` and `voter_constraints` This commit introduces two new attributes to zone configs: `num_voters` and `voter_constraints`. These attributes semantically act as subsets of the existing `num_replicas` and `constraints` attributes, in that they apply _only_ to voting replicas. After this change, when `num_voters` is explicitly specified, `num_replicas` indicates the sum of voting and non-voting replicas. Likewise, the existing `constraints` field governs the placement constraints for all replicas (similarly defaulting to just voting replicas if non-voting replicas aren't used), whereas the new `voter_constraints` field governs placement constraints for just the voting replicas. *This retains backwards compatibility* for existing zone configs that do not use these newly added attributes. Note that the set of nodes satisfying `voter_constraints` may not necessarily be a subset of the set of nodes satisfying `constraints`. `voter_constraints` are simply required to be compatible with `constraints`. They can be totally disjoint from the `constraints` (i.e. talk about completely different set of attributes). Release note(sql change): Zone configs now support new attributes `num_voters` and `voter_constraints`. `num_voters` will specify the number of voting replicas. When `num_voters` is explicitly specified, `num_replicas` will be the sum of voting and non-voting replicas. `voter_constraints` will specify the constraints that govern the placement of just the voting replicas, whereas the existing `constraints` attributes will govern the placement of all replicas (voting as well as non-voting). --- pkg/config/zonepb/zone.go | 193 ++++++++- pkg/config/zonepb/zone.pb.go | 370 ++++++++++++++---- pkg/config/zonepb/zone.proto | 31 +- pkg/config/zonepb/zone_test.go | 144 ++++++- pkg/config/zonepb/zone_yaml.go | 32 +- .../logictest/testdata/logic_test/zone_config | 57 +++ pkg/sql/set_zone_config.go | 35 +- pkg/sql/set_zone_config_test.go | 116 +++++- pkg/sql/show_zone_config.go | 19 + 9 files changed, 856 insertions(+), 141 deletions(-) diff --git a/pkg/config/zonepb/zone.go b/pkg/config/zonepb/zone.go index 49dc7edb8999..e62c427edc0a 100644 --- a/pkg/config/zonepb/zone.go +++ b/pkg/config/zonepb/zone.go @@ -175,6 +175,7 @@ func (c *Constraint) FromString(short string) error { func NewZoneConfig() *ZoneConfig { return &ZoneConfig{ InheritedConstraints: true, + InheritedVoterConstraints: true, InheritedLeasePreferences: true, } } @@ -183,12 +184,15 @@ func NewZoneConfig() *ZoneConfig { // all fields are set but set to their respective zero values. func EmptyCompleteZoneConfig() *ZoneConfig { return &ZoneConfig{ - NumReplicas: proto.Int32(0), - RangeMinBytes: proto.Int64(0), - RangeMaxBytes: proto.Int64(0), - GC: &GCPolicy{TTLSeconds: 0}, - InheritedConstraints: true, - InheritedLeasePreferences: true, + NumReplicas: proto.Int32(0), + NumVoters: proto.Int32(0), + NumVotersConfiguredSeparately: false, + RangeMinBytes: proto.Int64(0), + RangeMaxBytes: proto.Int64(0), + GC: &GCPolicy{TTLSeconds: 0}, + InheritedConstraints: true, + InheritedVoterConstraints: true, + InheritedLeasePreferences: true, } } @@ -238,9 +242,10 @@ func DefaultSystemZoneConfigRef() *ZoneConfig { // IsComplete returns whether all the fields are set. func (z *ZoneConfig) IsComplete() bool { - return ((z.NumReplicas != nil) && (z.RangeMinBytes != nil) && - (z.RangeMaxBytes != nil) && (z.GC != nil) && - (!z.InheritedConstraints) && (!z.InheritedLeasePreferences)) + return ((z.NumReplicas != nil) && (z.NumVoters != nil || !z.NumVotersConfiguredSeparately) && + (z.RangeMinBytes != nil) && (z.RangeMaxBytes != nil) && (z.GC != nil) && + (!z.InheritedVoterConstraints) && (!z.InheritedConstraints) && + (!z.InheritedLeasePreferences)) } // ValidateTandemFields returns an error if the ZoneConfig to be written @@ -255,11 +260,29 @@ func (z *ZoneConfig) ValidateTandemFields() error { if numConstrainedRepls > 0 && z.NumReplicas == nil { return fmt.Errorf("when per-replica constraints are set, num_replicas must be set as well") } + + var numConstrainedVoters int32 + for _, constraint := range z.VoterConstraints { + numConstrainedVoters += constraint.NumReplicas + } + + if numConstrainedVoters > 0 && z.NumVoters == nil { + return fmt.Errorf("when per-voter constraints are set, num_voters must be set as well") + } + + if !z.NumVotersConfiguredSeparately && len(z.VoterConstraints) > 0 { + return fmt.Errorf("voter_constraints can not be set without explicitly setting num_voters as well") + } + if (z.RangeMinBytes != nil || z.RangeMaxBytes != nil) && (z.RangeMinBytes == nil || z.RangeMaxBytes == nil) { return fmt.Errorf("range_min_bytes and range_max_bytes must be set together") } - if !z.InheritedLeasePreferences && z.InheritedConstraints { + if z.NumVotersConfiguredSeparately { + if !z.InheritedLeasePreferences && z.InheritedVoterConstraints { + return fmt.Errorf("lease preferences can not be set unless the voter constraints are explicitly set as well") + } + } else if !z.InheritedLeasePreferences && z.InheritedConstraints { return fmt.Errorf("lease preferences can not be set unless the constraints are explicitly set as well") } return nil @@ -286,7 +309,29 @@ func (z *ZoneConfig) Validate() error { } return fmt.Errorf("at least one replica is required") case *z.NumReplicas == 2: - return fmt.Errorf("at least 3 replicas are required for multi-replica configurations") + if !z.NumVotersConfiguredSeparately { + return fmt.Errorf("at least 3 replicas are required for multi-replica configurations") + } + } + } + + if z.NumVotersConfiguredSeparately { + if z.NumVoters != nil { + switch { + case *z.NumVoters <= 0: + return fmt.Errorf("at least one voting replica is required") + case *z.NumVoters == 2: + return fmt.Errorf("at least 3 voting replicas are required for multi-replica configurations") + } + if z.NumReplicas != nil && *z.NumVoters > *z.NumReplicas { + return fmt.Errorf("num_voters cannot be greater than num_replicas") + } + } + } else { + if z.NumVoters != nil && *z.NumVoters > 0 { + // TODO(aayush): Remove panic before merging. + panic(fmt.Sprintf("programming error: cannot have positive value for `num_voters` (%d)"+ + " when `num_voters_configured_separately` is false", *z.NumVoters)) } } @@ -318,6 +363,19 @@ func (z *ZoneConfig) Validate() error { } } + for _, constraints := range z.VoterConstraints { + for _, constraint := range constraints.Constraints { + if constraint.Type == Constraint_DEPRECATED_POSITIVE { + return fmt.Errorf("voter_constraints must be of type 'required' (prefixed with a '+')") + } + // TODO(aayush): Allowing these makes validating `voter_constraints` + // against `constraints` harder. Revisit this decision if need be. + if constraint.Type == Constraint_PROHIBITED { + return fmt.Errorf("voter_constraints cannot contain prohibitive constraints") + } + } + } + // We only need to further validate constraints if per-replica constraints // are in use. The old style of constraints that apply to all replicas don't // require validation. @@ -344,6 +402,59 @@ func (z *ZoneConfig) Validate() error { } } + // If we have per replica constraints inside voter_constraints, make sure + // that the number of replicas adds up to less than the number of voters. + // + // NB: We intentionally allow the number of replicas constrained by + // `constraints` plus the number of voters constrained by `voter_constraints` + // to exceed num_voters. + // For instance, the following would be a valid zone configuration: + // num_replicas = 3 + // num_voters = 3 + // constraints = {"+region=A": 1, "+region=B": 1, "+region=C": 1} + // voter_constraints = {"+ssd": 3} + // In the current state of our zone config validation logic, allowing + // examples like the one shown above also allows the user walk themselves into + // unsatisfiable zone configurations like the following: + // num_replicas = 3 + // num_voters = 3 + // constraints = {"+region=A": 2. "+region=B": 1} + // voter_constraints = {"region=C": 2, "+region=D": 1} + // + // TODO(aayush): We could disallow examples like the one shown above by + // instead requiring that all the constraints inside `voter_constraints` must + // also be present in the `constraints` field. Revisit this decision if it + // makes things harder at the allocator level. + if len(z.VoterConstraints) > 1 || (len(z.VoterConstraints) == 1 && z.VoterConstraints[0].NumReplicas != 0) { + var numConstrainedRepls int64 + for _, constraints := range z.VoterConstraints { + if constraints.NumReplicas <= 0 { + return fmt.Errorf("constraints must apply to at least one replica") + } + numConstrainedRepls += int64(constraints.NumReplicas) + } + if z.NumVotersConfiguredSeparately { + // NB: These nil checks are not required in production code but they are + // for testing as some tests run `Validate()` on incomplete zone configs. + if z.NumVoters != nil && numConstrainedRepls > int64(*z.NumVoters) { + return fmt.Errorf("the number of replicas specified in voter_constraints (%d) cannot be greater "+ + "than the number of voters configured for the zone (%d)", + numConstrainedRepls, *z.NumVoters) + } + } else { + if z.NumReplicas != nil && numConstrainedRepls > int64(*z.NumReplicas) { + return fmt.Errorf("the number of replicas specified in constraints (%d) cannot be greater "+ + "than the number of replicas configured for the zone (%d)", + numConstrainedRepls, *z.NumReplicas) + } + } + } + + // Validate that `constraints` aren't incompatible with `voter_constraints`. + if err := validateVoterConstraintsCompatibility(z.VoterConstraints, z.Constraints); err != nil { + return err + } + for _, leasePref := range z.LeasePreferences { if len(leasePref.Constraints) == 0 { return fmt.Errorf("every lease preference must include at least one constraint") @@ -359,6 +470,35 @@ func (z *ZoneConfig) Validate() error { return nil } +// validateVoterConstraintsCompatibility cross-validates `voter_constraints` +// against `constraints` and ensures that nothing that is prohibited at the +// overall `constraints` level is required at the `voter_constraints` level, +// since this sort of incongruity will lead to an unsatisfiable zone +// configuration. +func validateVoterConstraintsCompatibility( + voterConstraints, overallConstraints []ConstraintsConjunction, +) error { + // We know that prohibitive constraints are not allowed under + // `voter_constraints`. Walk through overallConstraints to ensure that none of + // the prohibitive constraints conflict with the `required` constraints in + // voterConstraints. + for _, constraints := range overallConstraints { + for _, constraint := range constraints.Constraints { + if constraint.Type == Constraint_PROHIBITED { + for _, otherConstraints := range voterConstraints { + for _, otherConstraint := range otherConstraints.Constraints { + conflicting := otherConstraint.Value == constraint.Value && otherConstraint.Key == constraint.Key + if conflicting { + return fmt.Errorf("prohibitive constraint %s conflicts with voter_constraint %s", constraint, otherConstraint) + } + } + } + } + } + } + return nil +} + // InheritFromParent hydrates a zones missing fields from its parent. func (z *ZoneConfig) InheritFromParent(parent *ZoneConfig) { // Allow for subzonePlaceholders to inherit fields from parents if needed. @@ -367,6 +507,11 @@ func (z *ZoneConfig) InheritFromParent(parent *ZoneConfig) { z.NumReplicas = proto.Int32(*parent.NumReplicas) } } + if z.NumVoters == nil || (z.NumVoters != nil && *z.NumVoters == 0) { + if parent.NumVoters != nil { + z.NumVoters = proto.Int32(*parent.NumVoters) + } + } if z.RangeMinBytes == nil { if parent.RangeMinBytes != nil { z.RangeMinBytes = proto.Int64(*parent.RangeMinBytes) @@ -389,6 +534,12 @@ func (z *ZoneConfig) InheritFromParent(parent *ZoneConfig) { z.InheritedConstraints = false } } + if z.InheritedVoterConstraints { + if !parent.InheritedVoterConstraints { + z.VoterConstraints = parent.VoterConstraints + z.InheritedVoterConstraints = false + } + } if z.InheritedLeasePreferences { if !parent.InheritedLeasePreferences { z.LeasePreferences = parent.LeasePreferences @@ -406,6 +557,17 @@ func (z *ZoneConfig) CopyFromZone(other ZoneConfig, fieldList []tree.Name) { z.NumReplicas = proto.Int32(*other.NumReplicas) } } + if fieldName == "num_voters" { + z.NumVoters = nil + if other.NumVoters != nil { + z.NumVoters = proto.Int32(*other.NumVoters) + z.NumVotersConfiguredSeparately = true + // TODO(aayush): Remove this panic once done with testing + if !other.NumVotersConfiguredSeparately { + panic(`NumVotersConfiguredSeparately was unexpectedly false when num_voters has an explicit value`) + } + } + } if fieldName == "range_min_bytes" { z.RangeMinBytes = nil if other.RangeMinBytes != nil { @@ -429,6 +591,10 @@ func (z *ZoneConfig) CopyFromZone(other ZoneConfig, fieldList []tree.Name) { z.Constraints = other.Constraints z.InheritedConstraints = other.InheritedConstraints } + if fieldName == "voter_constraints" { + z.VoterConstraints = other.VoterConstraints + z.InheritedVoterConstraints = other.InheritedVoterConstraints + } if fieldName == "lease_preferences" { z.LeasePreferences = other.LeasePreferences z.InheritedLeasePreferences = other.InheritedLeasePreferences @@ -494,6 +660,8 @@ func (z *ZoneConfig) IsSubzonePlaceholder() bool { // A ZoneConfig with zero replicas is otherwise invalid, so we repurpose it to // indicate that a ZoneConfig is a placeholder for subzones rather than // introducing a dedicated IsPlaceholder flag. + // TODO(aayush): Decide whether its worth introducing a isPlaceholder flag to + // clean this up after num_voters is introduced. return z.NumReplicas != nil && *z.NumReplicas == 0 } @@ -602,6 +770,9 @@ func (z ZoneConfig) SubzoneSplits() []roachpb.RKey { } // ReplicaConstraintsCount is part of the cat.Zone interface. +// +// TODO(aayush): Go through the callers of the methods here and decide the right +// semantics. func (z *ZoneConfig) ReplicaConstraintsCount() int { return len(z.Constraints) } diff --git a/pkg/config/zonepb/zone.pb.go b/pkg/config/zonepb/zone.pb.go index 8990d9488f34..73cdab64f3c7 100644 --- a/pkg/config/zonepb/zone.pb.go +++ b/pkg/config/zonepb/zone.pb.go @@ -64,7 +64,7 @@ func (x *Constraint_Type) UnmarshalJSON(data []byte) error { return nil } func (Constraint_Type) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_zone_cd8e37a638b916ef, []int{1, 0} + return fileDescriptor_zone_845007ab3f32b5c9, []int{1, 0} } // GCPolicy defines garbage collection policies which apply to MVCC @@ -84,7 +84,7 @@ func (m *GCPolicy) Reset() { *m = GCPolicy{} } func (m *GCPolicy) String() string { return proto.CompactTextString(m) } func (*GCPolicy) ProtoMessage() {} func (*GCPolicy) Descriptor() ([]byte, []int) { - return fileDescriptor_zone_cd8e37a638b916ef, []int{0} + return fileDescriptor_zone_845007ab3f32b5c9, []int{0} } func (m *GCPolicy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -121,7 +121,7 @@ type Constraint struct { func (m *Constraint) Reset() { *m = Constraint{} } func (*Constraint) ProtoMessage() {} func (*Constraint) Descriptor() ([]byte, []int) { - return fileDescriptor_zone_cd8e37a638b916ef, []int{1} + return fileDescriptor_zone_845007ab3f32b5c9, []int{1} } func (m *Constraint) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -163,7 +163,7 @@ type ConstraintsConjunction struct { func (m *ConstraintsConjunction) Reset() { *m = ConstraintsConjunction{} } func (*ConstraintsConjunction) ProtoMessage() {} func (*ConstraintsConjunction) Descriptor() ([]byte, []int) { - return fileDescriptor_zone_cd8e37a638b916ef, []int{2} + return fileDescriptor_zone_845007ab3f32b5c9, []int{2} } func (m *ConstraintsConjunction) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -198,7 +198,7 @@ func (m *LeasePreference) Reset() { *m = LeasePreference{} } func (m *LeasePreference) String() string { return proto.CompactTextString(m) } func (*LeasePreference) ProtoMessage() {} func (*LeasePreference) Descriptor() ([]byte, []int) { - return fileDescriptor_zone_cd8e37a638b916ef, []int{3} + return fileDescriptor_zone_845007ab3f32b5c9, []int{3} } func (m *LeasePreference) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -234,19 +234,40 @@ type ZoneConfig struct { // If GC policy is not set, uses the next highest, non-null policy // in the zone config hierarchy, up to the default policy if necessary. GC *GCPolicy `protobuf:"bytes,4,opt,name=gc" json:"gc,omitempty"` - // NumReplicas specifies the desired number of replicas + // NumReplicas specifies the desired number of replicas. It is the sum of the + // desired number of voting and non-voting replicas. NumReplicas *int32 `protobuf:"varint,5,opt,name=num_replicas,json=numReplicas" json:"num_replicas,omitempty" yaml:"num_replicas"` + // NumVotersConfiguredSeparately indicates whether voting replicas are + // configured and constrained separately, using the `num_voters` and + // `voter_constraints` attributes. When false (default), `num_replicas` + // indicates the number of voting replicas. Otherwise, `num_replicas` + // indicates the sum of voting and non-voting replicas. + // TODO(aayush): Maybe figure out a better name for this. + NumVotersConfiguredSeparately bool `protobuf:"varint,15,opt,name=num_voters_configured_separately,json=numVotersConfiguredSeparately" json:"num_voters_configured_separately"` + // NumVoters specifies the desired number of voter replicas. + NumVoters *int32 `protobuf:"varint,12,opt,name=num_voters,json=numVoters" json:"num_voters,omitempty" yaml:"num_voters"` // Constraints constrains which stores the replicas can be stored on. The // order in which the constraints are stored is arbitrary and may change. // https://github.com/cockroachdb/cockroach/blob/master/docs/RFCS/20160706_expressive_zone_config.md#constraint-system // // NOTE: The sum of the num_replicas fields of the Constraints must add up to - // ZoneConfig.num_replicas, or there must be no more than a single Constraints - // field with num_replicas set to 0. + // less than ZoneConfig.num_replicas, or there must be no more than a single + // Constraints field with num_replicas set to 0. + // TODO(aayush): Check if the per replica constraints even accept a zone + // config statement with the number of replicas set to 0. If not, fix the + // above comment. Constraints []ConstraintsConjunction `protobuf:"bytes,6,rep,name=constraints" json:"constraints" yaml:"constraints,flow"` + // VoterConstraints constrains which stores the voting replicas can be stored + // on. This field must be compatible with the `constraints` field above, but + // not necessarily a subset. + VoterConstraints []ConstraintsConjunction `protobuf:"bytes,13,rep,name=voter_constraints,json=voterConstraints" json:"voter_constraints" yaml:"voter_constraints,flow"` // InheritedContraints specifies if the value in the Constraints field was // inherited from the zone's parent or specified explicitly by the user. InheritedConstraints bool `protobuf:"varint,10,opt,name=inherited_constraints,json=inheritedConstraints" json:"inherited_constraints"` + // InheritedVoterConstraints specifies if the value in the VoterConstraints + // field was inherited from the zone's parent or specifiedly explicitly by the + // user. + InheritedVoterConstraints bool `protobuf:"varint,14,opt,name=inherited_voter_constraints,json=inheritedVoterConstraints" json:"inherited_voter_constraints"` // LeasePreference stores information about where the user would prefer for // range leases to be placed. Leases are allowed to be placed elsewhere if // needed, but will follow the provided preference when possible. @@ -276,7 +297,7 @@ func (m *ZoneConfig) Reset() { *m = ZoneConfig{} } func (m *ZoneConfig) String() string { return proto.CompactTextString(m) } func (*ZoneConfig) ProtoMessage() {} func (*ZoneConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_zone_cd8e37a638b916ef, []int{4} + return fileDescriptor_zone_845007ab3f32b5c9, []int{4} } func (m *ZoneConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -317,7 +338,7 @@ func (m *Subzone) Reset() { *m = Subzone{} } func (m *Subzone) String() string { return proto.CompactTextString(m) } func (*Subzone) ProtoMessage() {} func (*Subzone) Descriptor() ([]byte, []int) { - return fileDescriptor_zone_cd8e37a638b916ef, []int{5} + return fileDescriptor_zone_845007ab3f32b5c9, []int{5} } func (m *Subzone) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -363,7 +384,7 @@ func (m *SubzoneSpan) Reset() { *m = SubzoneSpan{} } func (m *SubzoneSpan) String() string { return proto.CompactTextString(m) } func (*SubzoneSpan) ProtoMessage() {} func (*SubzoneSpan) Descriptor() ([]byte, []int) { - return fileDescriptor_zone_cd8e37a638b916ef, []int{6} + return fileDescriptor_zone_845007ab3f32b5c9, []int{6} } func (m *SubzoneSpan) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -562,6 +583,18 @@ func (this *ZoneConfig) Equal(that interface{}) bool { } else if that1.NumReplicas != nil { return false } + if this.NumVotersConfiguredSeparately != that1.NumVotersConfiguredSeparately { + return false + } + if this.NumVoters != nil && that1.NumVoters != nil { + if *this.NumVoters != *that1.NumVoters { + return false + } + } else if this.NumVoters != nil { + return false + } else if that1.NumVoters != nil { + return false + } if len(this.Constraints) != len(that1.Constraints) { return false } @@ -570,9 +603,20 @@ func (this *ZoneConfig) Equal(that interface{}) bool { return false } } + if len(this.VoterConstraints) != len(that1.VoterConstraints) { + return false + } + for i := range this.VoterConstraints { + if !this.VoterConstraints[i].Equal(&that1.VoterConstraints[i]) { + return false + } + } if this.InheritedConstraints != that1.InheritedConstraints { return false } + if this.InheritedVoterConstraints != that1.InheritedVoterConstraints { + return false + } if len(this.LeasePreferences) != len(that1.LeasePreferences) { return false } @@ -879,6 +923,39 @@ func (m *ZoneConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0 } i++ + if m.NumVoters != nil { + dAtA[i] = 0x60 + i++ + i = encodeVarintZone(dAtA, i, uint64(*m.NumVoters)) + } + if len(m.VoterConstraints) > 0 { + for _, msg := range m.VoterConstraints { + dAtA[i] = 0x6a + i++ + i = encodeVarintZone(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + dAtA[i] = 0x70 + i++ + if m.InheritedVoterConstraints { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + dAtA[i] = 0x78 + i++ + if m.NumVotersConfiguredSeparately { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ return i, nil } @@ -1072,6 +1149,23 @@ func NewPopulatedZoneConfig(r randyZone, easy bool) *ZoneConfig { } this.InheritedConstraints = bool(bool(r.Intn(2) == 0)) this.InheritedLeasePreferences = bool(bool(r.Intn(2) == 0)) + if r.Intn(10) != 0 { + v16 := int32(r.Int31()) + if r.Intn(2) == 0 { + v16 *= -1 + } + this.NumVoters = &v16 + } + if r.Intn(10) != 0 { + v17 := r.Intn(5) + this.VoterConstraints = make([]ConstraintsConjunction, v17) + for i := 0; i < v17; i++ { + v18 := NewPopulatedConstraintsConjunction(r, easy) + this.VoterConstraints[i] = *v18 + } + } + this.InheritedVoterConstraints = bool(bool(r.Intn(2) == 0)) + this.NumVotersConfiguredSeparately = bool(bool(r.Intn(2) == 0)) if !easy && r.Intn(10) != 0 { } return this @@ -1081,8 +1175,8 @@ func NewPopulatedSubzone(r randyZone, easy bool) *Subzone { this := &Subzone{} this.IndexID = uint32(r.Uint32()) this.PartitionName = string(randStringZone(r)) - v16 := NewPopulatedZoneConfig(r, easy) - this.Config = *v16 + v19 := NewPopulatedZoneConfig(r, easy) + this.Config = *v19 if !easy && r.Intn(10) != 0 { } return this @@ -1091,16 +1185,16 @@ func NewPopulatedSubzone(r randyZone, easy bool) *Subzone { func NewPopulatedSubzoneSpan(r randyZone, easy bool) *SubzoneSpan { this := &SubzoneSpan{} if r.Intn(10) != 0 { - v17 := r.Intn(100) - this.Key = make(github_com_cockroachdb_cockroach_pkg_roachpb.Key, v17) - for i := 0; i < v17; i++ { + v20 := r.Intn(100) + this.Key = make(github_com_cockroachdb_cockroach_pkg_roachpb.Key, v20) + for i := 0; i < v20; i++ { this.Key[i] = byte(r.Intn(256)) } } if r.Intn(10) != 0 { - v18 := r.Intn(100) - this.EndKey = make(github_com_cockroachdb_cockroach_pkg_roachpb.Key, v18) - for i := 0; i < v18; i++ { + v21 := r.Intn(100) + this.EndKey = make(github_com_cockroachdb_cockroach_pkg_roachpb.Key, v21) + for i := 0; i < v21; i++ { this.EndKey[i] = byte(r.Intn(256)) } } @@ -1132,9 +1226,9 @@ func randUTF8RuneZone(r randyZone) rune { return rune(ru + 61) } func randStringZone(r randyZone) string { - v19 := r.Intn(100) - tmps := make([]rune, v19) - for i := 0; i < v19; i++ { + v22 := r.Intn(100) + tmps := make([]rune, v22) + for i := 0; i < v22; i++ { tmps[i] = randUTF8RuneZone(r) } return string(tmps) @@ -1156,11 +1250,11 @@ func randFieldZone(dAtA []byte, r randyZone, fieldNumber int, wire int) []byte { switch wire { case 0: dAtA = encodeVarintPopulateZone(dAtA, uint64(key)) - v20 := r.Int63() + v23 := r.Int63() if r.Intn(2) == 0 { - v20 *= -1 + v23 *= -1 } - dAtA = encodeVarintPopulateZone(dAtA, uint64(v20)) + dAtA = encodeVarintPopulateZone(dAtA, uint64(v23)) case 1: dAtA = encodeVarintPopulateZone(dAtA, uint64(key)) dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) @@ -1285,6 +1379,17 @@ func (m *ZoneConfig) Size() (n int) { } n += 2 n += 2 + if m.NumVoters != nil { + n += 1 + sovZone(uint64(*m.NumVoters)) + } + if len(m.VoterConstraints) > 0 { + for _, e := range m.VoterConstraints { + l = e.Size() + n += 1 + l + sovZone(uint64(l)) + } + } + n += 2 + n += 2 return n } @@ -1996,6 +2101,97 @@ func (m *ZoneConfig) Unmarshal(dAtA []byte) error { } } m.InheritedLeasePreferences = bool(v != 0) + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NumVoters", wireType) + } + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowZone + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.NumVoters = &v + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoterConstraints", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowZone + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthZone + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.VoterConstraints = append(m.VoterConstraints, ConstraintsConjunction{}) + if err := m.VoterConstraints[len(m.VoterConstraints)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 14: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InheritedVoterConstraints", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowZone + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.InheritedVoterConstraints = bool(v != 0) + case 15: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NumVotersConfiguredSeparately", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowZone + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.NumVotersConfiguredSeparately = bool(v != 0) default: iNdEx = preIndex skippy, err := skipZone(dAtA[iNdEx:]) @@ -2381,63 +2577,69 @@ var ( ErrIntOverflowZone = fmt.Errorf("proto: integer overflow") ) -func init() { proto.RegisterFile("config/zonepb/zone.proto", fileDescriptor_zone_cd8e37a638b916ef) } - -var fileDescriptor_zone_cd8e37a638b916ef = []byte{ - // 873 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0x3d, 0x73, 0xe3, 0x44, - 0x18, 0xf6, 0xda, 0x8e, 0xad, 0x7b, 0xed, 0x24, 0x66, 0xef, 0x48, 0x44, 0x18, 0x2c, 0x23, 0x60, - 0x30, 0x5f, 0x36, 0x13, 0x68, 0xc8, 0x0c, 0xc5, 0xc9, 0x36, 0x87, 0xc8, 0x7d, 0x18, 0xc5, 0x50, - 0x1c, 0x85, 0x46, 0x96, 0x37, 0x8e, 0x88, 0xbc, 0xab, 0x91, 0x64, 0x88, 0x18, 0x3a, 0xfe, 0x00, - 0x25, 0x05, 0x45, 0xfe, 0x01, 0x2d, 0x33, 0xfc, 0x81, 0x94, 0x47, 0x77, 0x34, 0x1e, 0x70, 0x1a, - 0xea, 0x94, 0x54, 0x8c, 0x56, 0xb2, 0x24, 0xfb, 0x62, 0x08, 0x54, 0x5a, 0xed, 0xfb, 0xec, 0xf3, - 0x7e, 0x3d, 0xef, 0x0b, 0xa2, 0xc9, 0xe8, 0xb1, 0x35, 0x6e, 0x7f, 0xc3, 0x28, 0x71, 0x86, 0xfc, - 0xd3, 0x72, 0x5c, 0xe6, 0x33, 0xbc, 0x6b, 0x32, 0xf3, 0xd4, 0x65, 0x86, 0x79, 0xd2, 0x8a, 0x30, - 0xad, 0x08, 0xb3, 0x77, 0x67, 0xcc, 0xc6, 0x8c, 0x63, 0xda, 0xe1, 0x29, 0x82, 0xcb, 0x2a, 0x08, - 0xf7, 0x3a, 0x7d, 0x66, 0x5b, 0x66, 0x80, 0xdf, 0x83, 0x8a, 0xef, 0xdb, 0xba, 0x47, 0x4c, 0x46, - 0x47, 0x9e, 0x88, 0x1a, 0xa8, 0xb9, 0xa1, 0xe0, 0x8b, 0x99, 0x94, 0x9b, 0xcf, 0x24, 0x18, 0x0c, - 0xee, 0x1f, 0x45, 0x16, 0x0d, 0x7c, 0xdf, 0x8e, 0xcf, 0x07, 0xc2, 0xcf, 0xe7, 0x12, 0xfa, 0xf3, - 0x5c, 0x42, 0xf2, 0xaf, 0x08, 0xa0, 0xc3, 0xa8, 0xe7, 0xbb, 0x86, 0x45, 0x7d, 0xac, 0x40, 0xd1, - 0x0f, 0x1c, 0xc2, 0x69, 0xb6, 0xf6, 0x9b, 0xad, 0x35, 0x71, 0xb5, 0xd2, 0x27, 0xad, 0x41, 0xe0, - 0x10, 0xa5, 0x18, 0x3a, 0xd4, 0xf8, 0x5b, 0xbc, 0x03, 0x85, 0x53, 0x12, 0x88, 0xf9, 0x06, 0x6a, - 0xde, 0x8a, 0x0d, 0xe1, 0x05, 0xde, 0x83, 0x8d, 0xaf, 0x0c, 0x7b, 0x4a, 0xc4, 0x42, 0xc6, 0x12, - 0x5d, 0xc9, 0x1f, 0x42, 0x31, 0xe4, 0xc1, 0xbb, 0x70, 0xbb, 0xdb, 0xeb, 0x6b, 0xbd, 0xce, 0xdd, - 0x41, 0xaf, 0xab, 0xf7, 0x1f, 0x1d, 0xa9, 0x03, 0xf5, 0xf3, 0x5e, 0x2d, 0x87, 0xab, 0x20, 0x68, - 0xbd, 0x4f, 0x3f, 0x53, 0xb5, 0x5e, 0xb7, 0x86, 0xf0, 0x16, 0x40, 0x5f, 0x7b, 0xf4, 0xb1, 0xaa, - 0xa8, 0x83, 0x5e, 0xb7, 0x96, 0x3f, 0xa8, 0xfe, 0x70, 0x2e, 0xe5, 0x92, 0x9c, 0x7e, 0x44, 0xb0, - 0x93, 0x06, 0xe8, 0x75, 0x18, 0xfd, 0x72, 0x4a, 0x4d, 0xdf, 0x62, 0x14, 0x1f, 0x42, 0xc5, 0x4c, - 0x2d, 0x62, 0xa9, 0x51, 0x68, 0x56, 0xf6, 0x5f, 0xb9, 0x41, 0x9a, 0x71, 0xb8, 0xd9, 0xd7, 0xf8, - 0x75, 0xa8, 0xd2, 0xe9, 0x44, 0x77, 0x89, 0x63, 0x5b, 0xa6, 0xe1, 0x89, 0x65, 0x5e, 0xfb, 0x18, - 0x48, 0xa7, 0x13, 0x2d, 0x36, 0xac, 0x84, 0xf7, 0x1d, 0x82, 0xed, 0xfb, 0xc4, 0xf0, 0x48, 0xdf, - 0x25, 0xc7, 0xc4, 0x25, 0xd4, 0x24, 0xf8, 0x78, 0x39, 0x2e, 0x74, 0xf3, 0xb8, 0xa4, 0xd0, 0xdd, - 0xd5, 0x4c, 0xda, 0x0d, 0x8c, 0x89, 0x7d, 0x20, 0x67, 0x58, 0xde, 0x3e, 0xb6, 0xd9, 0xd7, 0xf2, - 0x52, 0xc8, 0x99, 0xc6, 0xff, 0x52, 0x02, 0x78, 0xcc, 0x28, 0xe9, 0x70, 0x62, 0xac, 0xc0, 0xb6, - 0x6b, 0xd0, 0x31, 0xd1, 0x27, 0x16, 0xd5, 0x87, 0x81, 0x4f, 0x3c, 0xde, 0xc0, 0x82, 0xb2, 0x77, - 0x35, 0x93, 0x76, 0x22, 0xee, 0x15, 0x80, 0xac, 0x6d, 0xf2, 0x9b, 0x07, 0x16, 0x55, 0xc2, 0xff, - 0x0c, 0x87, 0x71, 0x16, 0x73, 0x14, 0xd6, 0x70, 0x2c, 0x00, 0x09, 0x87, 0x71, 0x16, 0x71, 0x7c, - 0x00, 0xf9, 0xb1, 0x29, 0x16, 0x1b, 0xa8, 0x59, 0xd9, 0x7f, 0x79, 0x6d, 0xfe, 0x0b, 0xf5, 0x2b, - 0xa5, 0xf9, 0x4c, 0xca, 0xdf, 0xeb, 0x68, 0xf9, 0xb1, 0x89, 0x0f, 0x56, 0xda, 0xb1, 0xc1, 0xdb, - 0xb1, 0x7b, 0x35, 0x93, 0x6e, 0x47, 0xbe, 0xb3, 0x56, 0x79, 0xa9, 0x43, 0xd8, 0xbd, 0x4e, 0x17, - 0xed, 0x1b, 0xd4, 0x3f, 0xab, 0xae, 0xff, 0xd6, 0x0b, 0xfc, 0x05, 0x6c, 0x7a, 0xd3, 0x61, 0xc8, - 0xa8, 0x7b, 0x8e, 0x41, 0x43, 0xfd, 0x84, 0x5e, 0x5f, 0x5d, 0xeb, 0xf5, 0x28, 0x42, 0x1f, 0x39, - 0x06, 0x55, 0x6a, 0xb1, 0x2b, 0x21, 0x72, 0xf5, 0x8e, 0xac, 0x55, 0xbd, 0xd4, 0xec, 0xe1, 0x87, - 0x20, 0xc4, 0xff, 0x9e, 0x28, 0x70, 0xde, 0xc6, 0xbf, 0xf1, 0x5e, 0xc3, 0x99, 0x70, 0xe0, 0x6f, - 0xe1, 0x39, 0x3b, 0xd4, 0xac, 0xee, 0x24, 0xa2, 0xf5, 0xc4, 0x5b, 0x9c, 0x78, 0xfd, 0x96, 0x58, - 0x51, 0xb9, 0xf2, 0x5a, 0xec, 0xe0, 0xa5, 0xc8, 0xc1, 0x33, 0x84, 0x71, 0x95, 0x6a, 0xf6, 0xf2, - 0xbb, 0x50, 0x15, 0xcf, 0x5b, 0xf4, 0x84, 0xb8, 0x96, 0x4f, 0x46, 0x7a, 0xb6, 0x51, 0xd0, 0x40, - 0x4d, 0x21, 0x1e, 0xb9, 0x3b, 0x09, 0x24, 0xd3, 0x1e, 0xdc, 0x85, 0x17, 0xd3, 0xa7, 0xcf, 0xa6, - 0x50, 0xc9, 0x10, 0xbc, 0x90, 0x00, 0x57, 0x02, 0xcf, 0xcc, 0xcd, 0x27, 0x45, 0x01, 0xd5, 0xf2, - 0xf2, 0x4f, 0x08, 0xca, 0x71, 0xd9, 0xf0, 0x9b, 0x20, 0x58, 0x74, 0x44, 0xce, 0x74, 0x6b, 0xc4, - 0xf7, 0xe6, 0xa6, 0xb2, 0x1d, 0xaf, 0xdf, 0xb2, 0x1a, 0xde, 0xab, 0x5d, 0xad, 0xcc, 0x01, 0xea, - 0x08, 0xbf, 0x05, 0x5b, 0x8e, 0xe1, 0xfa, 0x56, 0x28, 0x17, 0x9d, 0x1a, 0x13, 0xb2, 0xb4, 0x26, - 0x37, 0x13, 0xdb, 0x43, 0x63, 0x42, 0xf0, 0x5d, 0x28, 0x45, 0xf5, 0xe4, 0x63, 0xf4, 0x4f, 0xfb, - 0x20, 0x1d, 0xe4, 0x98, 0x29, 0x7e, 0x98, 0x99, 0xf7, 0xdf, 0x10, 0x54, 0x32, 0x02, 0xc2, 0x1f, - 0x45, 0x5b, 0x3a, 0x0c, 0xb8, 0xaa, 0xbc, 0xff, 0xd7, 0x4c, 0x7a, 0x77, 0x6c, 0xf9, 0x27, 0xd3, - 0x61, 0xcb, 0x64, 0x93, 0x76, 0xe2, 0x67, 0x34, 0x4c, 0xcf, 0x6d, 0xe7, 0x74, 0xdc, 0xe6, 0x27, - 0x67, 0xd8, 0x3a, 0x24, 0x41, 0xb4, 0xd5, 0x1f, 0x40, 0x99, 0xd0, 0x91, 0xbe, 0xd8, 0xf8, 0xff, - 0x97, 0xab, 0x44, 0xe8, 0xe8, 0x90, 0x04, 0xf8, 0x8d, 0x74, 0x28, 0x78, 0xcd, 0x78, 0xea, 0x8b, - 0xa5, 0xba, 0x90, 0x38, 0x2f, 0x6b, 0x9a, 0x9b, 0xd2, 0xbc, 0xf8, 0xa3, 0x9e, 0xbb, 0x98, 0xd7, - 0xd1, 0x93, 0x79, 0x1d, 0x3d, 0x9d, 0xd7, 0xd1, 0xef, 0xf3, 0x3a, 0xfa, 0xfe, 0xb2, 0x9e, 0x7b, - 0x72, 0x59, 0xcf, 0x3d, 0xbd, 0xac, 0xe7, 0x1e, 0x97, 0xa2, 0x42, 0xfd, 0x1d, 0x00, 0x00, 0xff, - 0xff, 0x1b, 0xf7, 0x5e, 0xb9, 0x83, 0x07, 0x00, 0x00, +func init() { proto.RegisterFile("config/zonepb/zone.proto", fileDescriptor_zone_845007ab3f32b5c9) } + +var fileDescriptor_zone_845007ab3f32b5c9 = []byte{ + // 968 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xbf, 0x73, 0xe3, 0x54, + 0x10, 0xf6, 0x73, 0x1c, 0xdb, 0x59, 0xdb, 0x89, 0xf3, 0xee, 0x2e, 0x11, 0x61, 0xce, 0x32, 0x02, + 0x06, 0xf3, 0xcb, 0x66, 0xc2, 0x35, 0x64, 0x86, 0xe2, 0x64, 0x9b, 0xc3, 0x24, 0xb9, 0x33, 0xb2, + 0x49, 0x71, 0x14, 0x1a, 0x59, 0x7a, 0x71, 0x44, 0xe4, 0x27, 0x8d, 0x24, 0x1f, 0x11, 0x43, 0x77, + 0x2d, 0x05, 0x25, 0x05, 0x45, 0xfe, 0x03, 0x5a, 0xfe, 0x84, 0x94, 0x47, 0x77, 0x34, 0x1e, 0x70, + 0x1a, 0xea, 0x94, 0x54, 0x8c, 0x9e, 0x64, 0x49, 0x76, 0x62, 0x08, 0x57, 0xf9, 0xf9, 0xed, 0xbe, + 0x6f, 0xbf, 0xdd, 0xfd, 0xb4, 0x0b, 0x9c, 0x6a, 0xd2, 0x63, 0x7d, 0xd8, 0xf8, 0xce, 0xa4, 0xc4, + 0x1a, 0xb0, 0x9f, 0xba, 0x65, 0x9b, 0xae, 0x89, 0xb7, 0x55, 0x53, 0x3d, 0xb5, 0x4d, 0x45, 0x3d, + 0xa9, 0x07, 0x3e, 0xf5, 0xc0, 0x67, 0xe7, 0xee, 0xd0, 0x1c, 0x9a, 0xcc, 0xa7, 0xe1, 0x9f, 0x02, + 0x77, 0xa1, 0x03, 0xf9, 0x47, 0xcd, 0xae, 0x69, 0xe8, 0xaa, 0x87, 0x3f, 0x86, 0x82, 0xeb, 0x1a, + 0xb2, 0x43, 0x54, 0x93, 0x6a, 0x0e, 0x87, 0xaa, 0xa8, 0xb6, 0x2a, 0xe2, 0x8b, 0x09, 0x9f, 0x9a, + 0x4e, 0x78, 0xe8, 0xf7, 0x0f, 0x7a, 0x81, 0x45, 0x02, 0xd7, 0x35, 0xc2, 0xf3, 0x5e, 0xfe, 0xd7, + 0x73, 0x1e, 0xfd, 0x75, 0xce, 0x23, 0xe1, 0x37, 0x04, 0xd0, 0x34, 0xa9, 0xe3, 0xda, 0x8a, 0x4e, + 0x5d, 0x2c, 0x42, 0xc6, 0xf5, 0x2c, 0xc2, 0x60, 0xd6, 0x77, 0x6b, 0xf5, 0x25, 0xbc, 0xea, 0xf1, + 0x93, 0x7a, 0xdf, 0xb3, 0x88, 0x98, 0xf1, 0x03, 0x4a, 0xec, 0x2d, 0xde, 0x82, 0x95, 0x53, 0xe2, + 0x71, 0xe9, 0x2a, 0xaa, 0xad, 0x85, 0x06, 0xff, 0x02, 0xef, 0xc0, 0xea, 0x33, 0xc5, 0x18, 0x13, + 0x6e, 0x25, 0x61, 0x09, 0xae, 0x84, 0x4f, 0x21, 0xe3, 0xe3, 0xe0, 0x6d, 0xb8, 0xd3, 0x6a, 0x77, + 0xa5, 0x76, 0xf3, 0x61, 0xbf, 0xdd, 0x92, 0xbb, 0x4f, 0x7a, 0x9d, 0x7e, 0xe7, 0xa8, 0x5d, 0x4e, + 0xe1, 0x22, 0xe4, 0xa5, 0xf6, 0x97, 0x5f, 0x75, 0xa4, 0x76, 0xab, 0x8c, 0xf0, 0x3a, 0x40, 0x57, + 0x7a, 0xf2, 0x79, 0x47, 0xec, 0xf4, 0xdb, 0xad, 0x72, 0x7a, 0xaf, 0xf8, 0xd3, 0x39, 0x9f, 0x8a, + 0x72, 0xfa, 0x19, 0xc1, 0x56, 0x4c, 0xd0, 0x69, 0x9a, 0xf4, 0x9b, 0x31, 0x55, 0x5d, 0xdd, 0xa4, + 0x78, 0x1f, 0x0a, 0x6a, 0x6c, 0xe1, 0xb2, 0xd5, 0x95, 0x5a, 0x61, 0xf7, 0xcd, 0x5b, 0xa4, 0x19, + 0xd2, 0x4d, 0xbe, 0xc6, 0xef, 0x40, 0x91, 0x8e, 0x47, 0xb2, 0x4d, 0x2c, 0x43, 0x57, 0x15, 0x87, + 0xcb, 0xb1, 0xda, 0x87, 0x8e, 0x74, 0x3c, 0x92, 0x42, 0xc3, 0x02, 0xbd, 0xe7, 0x08, 0x36, 0x0e, + 0x88, 0xe2, 0x90, 0xae, 0x4d, 0x8e, 0x89, 0x4d, 0xa8, 0x4a, 0xf0, 0xf1, 0x3c, 0x2f, 0x74, 0x7b, + 0x5e, 0xbc, 0x1f, 0xee, 0x6a, 0xc2, 0x6f, 0x7b, 0xca, 0xc8, 0xd8, 0x13, 0x12, 0x28, 0x1f, 0x1c, + 0x1b, 0xe6, 0xb7, 0xc2, 0x1c, 0xe5, 0x44, 0xe3, 0x7f, 0x58, 0x03, 0x78, 0x6a, 0x52, 0xd2, 0x64, + 0xc0, 0x58, 0x84, 0x0d, 0x5b, 0xa1, 0x43, 0x22, 0x8f, 0x74, 0x2a, 0x0f, 0x3c, 0x97, 0x38, 0xac, + 0x81, 0x2b, 0xe2, 0xce, 0xd5, 0x84, 0xdf, 0x0a, 0xb0, 0x17, 0x1c, 0x04, 0xa9, 0xc4, 0x6e, 0x0e, + 0x75, 0x2a, 0xfa, 0xff, 0x13, 0x18, 0xca, 0x59, 0x88, 0xb1, 0xb2, 0x04, 0x63, 0xe6, 0x10, 0x61, + 0x28, 0x67, 0x01, 0xc6, 0x27, 0x90, 0x1e, 0xaa, 0x5c, 0xa6, 0x8a, 0x6a, 0x85, 0xdd, 0x37, 0x96, + 0xe6, 0x3f, 0x53, 0xbf, 0x98, 0x9d, 0x4e, 0xf8, 0xf4, 0xa3, 0xa6, 0x94, 0x1e, 0xaa, 0x78, 0x6f, + 0xa1, 0x1d, 0xab, 0xac, 0x1d, 0xdb, 0x57, 0x13, 0xfe, 0x4e, 0x10, 0x3b, 0x69, 0x15, 0xe6, 0x3a, + 0x84, 0xed, 0x9b, 0x74, 0xd1, 0xb8, 0x45, 0xfd, 0x93, 0xea, 0xfa, 0x7f, 0xbd, 0xc0, 0x5f, 0x43, + 0xc9, 0x19, 0x0f, 0x7c, 0x44, 0xd9, 0xb1, 0x14, 0xea, 0xeb, 0xc7, 0x8f, 0xfa, 0xd6, 0xd2, 0xa8, + 0xbd, 0xc0, 0xbb, 0x67, 0x29, 0x54, 0x2c, 0x87, 0xa1, 0xf2, 0x41, 0xa8, 0x0f, 0x05, 0xa9, 0xe8, + 0xc4, 0x66, 0x07, 0x3f, 0x86, 0x7c, 0xf8, 0xdf, 0xe1, 0xf2, 0x0c, 0xb7, 0xfa, 0x5f, 0xb8, 0x37, + 0x60, 0x46, 0x18, 0xf8, 0x7b, 0xd8, 0x34, 0x7c, 0xcd, 0xca, 0x56, 0x24, 0x5a, 0x87, 0x5b, 0x63, + 0xc0, 0xcb, 0xa7, 0xc4, 0x82, 0xca, 0xc5, 0xb7, 0xc3, 0x00, 0xf7, 0x83, 0x00, 0xd7, 0x00, 0xc3, + 0x2a, 0x95, 0x8d, 0xf9, 0x77, 0xbe, 0x2a, 0xee, 0xe9, 0xf4, 0x84, 0xd8, 0xba, 0x4b, 0x34, 0x39, + 0xd9, 0x28, 0xa8, 0xa2, 0x5a, 0x3e, 0xfc, 0xe4, 0xee, 0x46, 0x2e, 0x89, 0xf6, 0xe0, 0x16, 0xbc, + 0x1e, 0x3f, 0xbd, 0x9e, 0x42, 0x21, 0x01, 0xf0, 0x5a, 0xe4, 0x78, 0xb0, 0x48, 0xe0, 0x01, 0x80, + 0xaf, 0x9e, 0x67, 0xa6, 0x4b, 0x6c, 0x87, 0x2b, 0x32, 0x65, 0xdd, 0xbb, 0x9a, 0xf0, 0x9b, 0xb1, + 0xb2, 0x02, 0x9b, 0x20, 0xad, 0xd1, 0xf1, 0xe8, 0x88, 0x9d, 0xf1, 0x73, 0x04, 0x9b, 0xec, 0x7a, + 0x8e, 0x73, 0xe9, 0xd5, 0xc4, 0xb5, 0x50, 0xbc, 0x6b, 0xb8, 0xb3, 0xe2, 0x31, 0xc3, 0xd2, 0x0a, + 0x5c, 0xa7, 0xb3, 0x7e, 0x63, 0x05, 0x8e, 0x16, 0x51, 0x0e, 0xa1, 0x1a, 0x67, 0x29, 0x07, 0x8c, + 0xc7, 0x36, 0xd1, 0x64, 0x87, 0x58, 0x8a, 0xad, 0xb8, 0xc4, 0xf0, 0xb8, 0x8d, 0x04, 0xd4, 0xfd, + 0xa8, 0x0c, 0xcd, 0xc8, 0xb7, 0x17, 0xb9, 0xc6, 0x83, 0xe8, 0x8b, 0x4c, 0x1e, 0x95, 0xd3, 0xc2, + 0x2f, 0x08, 0x72, 0xa1, 0x0e, 0xf1, 0x7b, 0x90, 0xd7, 0xa9, 0x46, 0xce, 0x64, 0x5d, 0x63, 0x8b, + 0xa8, 0x24, 0x6e, 0x84, 0xfb, 0x2c, 0xd7, 0xf1, 0xef, 0x3b, 0x2d, 0x29, 0xc7, 0x1c, 0x3a, 0x1a, + 0x7e, 0x1f, 0xd6, 0x2d, 0xc5, 0x76, 0x75, 0xbf, 0x44, 0x32, 0x55, 0x46, 0x64, 0x6e, 0xef, 0x94, + 0x22, 0xdb, 0x63, 0x65, 0x44, 0xf0, 0x43, 0xc8, 0x06, 0xc4, 0xd9, 0x5c, 0xfa, 0xb7, 0x01, 0x1b, + 0x4f, 0xc6, 0x10, 0x29, 0x7c, 0x98, 0x18, 0xa0, 0xbf, 0x23, 0x28, 0x24, 0xbe, 0x48, 0xfc, 0x59, + 0xb0, 0xf6, 0x7c, 0xc2, 0x45, 0xf1, 0xc1, 0xdf, 0x13, 0xfe, 0xa3, 0xa1, 0xee, 0x9e, 0x8c, 0x07, + 0x75, 0xd5, 0x1c, 0x35, 0xa2, 0x38, 0xda, 0x20, 0x3e, 0x37, 0xac, 0xd3, 0x61, 0x83, 0x9d, 0xac, + 0x41, 0x7d, 0x9f, 0x78, 0xc1, 0x9a, 0x3c, 0x84, 0x1c, 0xa1, 0x9a, 0x3c, 0x5b, 0xa1, 0xaf, 0x8a, + 0x95, 0x25, 0x54, 0xdb, 0x27, 0x1e, 0x7e, 0x37, 0x9e, 0x32, 0xac, 0x66, 0x2c, 0xf5, 0xd9, 0x96, + 0x9a, 0xcd, 0x0c, 0x56, 0xd6, 0x38, 0x37, 0xb1, 0x76, 0xf1, 0x67, 0x25, 0x75, 0x31, 0xad, 0xa0, + 0x17, 0xd3, 0x0a, 0x7a, 0x39, 0xad, 0xa0, 0x3f, 0xa6, 0x15, 0xf4, 0xe3, 0x65, 0x25, 0xf5, 0xe2, + 0xb2, 0x92, 0x7a, 0x79, 0x59, 0x49, 0x3d, 0xcd, 0x06, 0x85, 0xfa, 0x27, 0x00, 0x00, 0xff, 0xff, + 0x85, 0x8f, 0x13, 0x49, 0xd4, 0x08, 0x00, 0x00, } diff --git a/pkg/config/zonepb/zone.proto b/pkg/config/zonepb/zone.proto index 93200a9c9684..2b29f612546c 100644 --- a/pkg/config/zonepb/zone.proto +++ b/pkg/config/zonepb/zone.proto @@ -97,22 +97,47 @@ message ZoneConfig { // in the zone config hierarchy, up to the default policy if necessary. optional GCPolicy gc = 4 [(gogoproto.customname) = "GC"]; - // NumReplicas specifies the desired number of replicas + // NumReplicas specifies the desired number of replicas. It is the sum of the + // desired number of voting and non-voting replicas. optional int32 num_replicas = 5 [(gogoproto.moretags) = "yaml:\"num_replicas\""]; + // NumVotersConfiguredSeparately indicates whether voting replicas are + // configured and constrained separately, using the `num_voters` and + // `voter_constraints` attributes. When false (default), `num_replicas` + // indicates the number of voting replicas. Otherwise, `num_replicas` + // indicates the sum of voting and non-voting replicas. + // TODO(aayush): Maybe figure out a better name for this. + optional bool num_voters_configured_separately = 15 [(gogoproto.nullable) = false]; + + // NumVoters specifies the desired number of voter replicas. + optional int32 num_voters = 12 [(gogoproto.moretags) = "yaml:\"num_voters\""]; + // Constraints constrains which stores the replicas can be stored on. The // order in which the constraints are stored is arbitrary and may change. // https://github.com/cockroachdb/cockroach/blob/master/docs/RFCS/20160706_expressive_zone_config.md#constraint-system // // NOTE: The sum of the num_replicas fields of the Constraints must add up to - // ZoneConfig.num_replicas, or there must be no more than a single Constraints - // field with num_replicas set to 0. + // less than ZoneConfig.num_replicas, or there must be no more than a single + // Constraints field with num_replicas set to 0. + // TODO(aayush): Check if the per replica constraints even accept a zone + // config statement with the number of replicas set to 0. If not, fix the + // above comment. repeated ConstraintsConjunction constraints = 6 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"constraints,flow\""]; + // VoterConstraints constrains which stores the voting replicas can be stored + // on. This field must be compatible with the `constraints` field above, but + // not necessarily a subset. + repeated ConstraintsConjunction voter_constraints = 13 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"voter_constraints,flow\""]; + // InheritedContraints specifies if the value in the Constraints field was // inherited from the zone's parent or specified explicitly by the user. optional bool inherited_constraints = 10 [(gogoproto.nullable) = false]; + // InheritedVoterConstraints specifies if the value in the VoterConstraints + // field was inherited from the zone's parent or specifiedly explicitly by the + // user. + optional bool inherited_voter_constraints = 14 [(gogoproto.nullable) = false]; + // LeasePreference stores information about where the user would prefer for // range leases to be placed. Leases are allowed to be placed elsewhere if // needed, but will follow the provided preference when possible. diff --git a/pkg/config/zonepb/zone_test.go b/pkg/config/zonepb/zone_test.go index 86f01ae6f67b..2ad30bd28379 100644 --- a/pkg/config/zonepb/zone_test.go +++ b/pkg/config/zonepb/zone_test.go @@ -244,6 +244,24 @@ func TestZoneConfigValidate(t *testing.T) { } } +func TestZoneConfigValidateNonVoterSpecific(t *testing.T) { + defer leaktest.AfterTest(t)() + + // TODO(aayush): Add tests. + testCases := []struct { + cfg ZoneConfig + expected string + }{} + + for i, c := range testCases { + err := c.cfg.Validate() + if !testutils.IsError(err, c.expected) { + t.Errorf("%d: expected %q, got %v", i, c.expected, err) + } + } +} + +// TODO(aayush): Add some tests here func TestZoneConfigValidateTandemFields(t *testing.T) { defer leaktest.AfterTest(t)() @@ -286,6 +304,20 @@ func TestZoneConfigValidateTandemFields(t *testing.T) { }, "lease preferences can not be set unless the constraints are explicitly set as well", }, + { + ZoneConfig{ + InheritedConstraints: false, + NumVotersConfiguredSeparately: true, + InheritedVoterConstraints: true, + InheritedLeasePreferences: false, + LeasePreferences: []LeasePreference{ + { + Constraints: []Constraint{}, + }, + }, + }, + "lease preferences can not be set unless the voter constraints are explicitly set as well", + }, } for i, c := range testCases { @@ -394,11 +426,14 @@ func TestZoneConfigMarshalYAML(t *testing.T) { GC: &GCPolicy{ TTLSeconds: 1, }, - NumReplicas: proto.Int32(1), + NumReplicas: proto.Int32(2), + NumVoters: proto.Int32(1), + NumVotersConfiguredSeparately: true, } testCases := []struct { constraints []ConstraintsConjunction + voterConstraints []ConstraintsConjunction leasePreferences []LeasePreference expected string }{ @@ -407,8 +442,11 @@ func TestZoneConfigMarshalYAML(t *testing.T) { range_max_bytes: 1 gc: ttlseconds: 1 -num_replicas: 1 +num_replicas: 2 +num_voters: 1 +num_voters_configured_separately: true constraints: [] +voter_constraints: [] lease_preferences: [] `, }, @@ -424,12 +462,26 @@ lease_preferences: [] }, }, }, + voterConstraints: []ConstraintsConjunction{ + { + Constraints: []Constraint{ + { + Type: Constraint_REQUIRED, + Key: "foo", + Value: "bar", + }, + }, + }, + }, expected: `range_min_bytes: 1 range_max_bytes: 1 gc: ttlseconds: 1 -num_replicas: 1 +num_replicas: 2 +num_voters: 1 +num_voters_configured_separately: true constraints: [+duck=foo] +voter_constraints: [+foo=bar] lease_preferences: [] `, }, @@ -458,8 +510,11 @@ lease_preferences: [] range_max_bytes: 1 gc: ttlseconds: 1 -num_replicas: 1 +num_replicas: 2 +num_voters: 1 +num_voters_configured_separately: true constraints: [foo, +duck=foo, -duck=foo] +voter_constraints: [] lease_preferences: [] `, }, @@ -476,12 +531,27 @@ lease_preferences: [] }, }, }, + voterConstraints: []ConstraintsConjunction{ + { + NumReplicas: 1, + Constraints: []Constraint{ + { + Type: Constraint_REQUIRED, + Key: "duck", + Value: "foo", + }, + }, + }, + }, expected: `range_min_bytes: 1 range_max_bytes: 1 gc: ttlseconds: 1 -num_replicas: 1 +num_replicas: 2 +num_voters: 1 +num_voters_configured_separately: true constraints: {+duck=foo: 3} +voter_constraints: {+duck=foo: 1} lease_preferences: [] `, }, @@ -511,8 +581,11 @@ lease_preferences: [] range_max_bytes: 1 gc: ttlseconds: 1 -num_replicas: 1 +num_replicas: 2 +num_voters: 1 +num_voters_configured_separately: true constraints: {'foo,+duck=foo,-duck=foo': 3} +voter_constraints: [] lease_preferences: [] `, }, @@ -544,12 +617,42 @@ lease_preferences: [] }, }, }, + voterConstraints: []ConstraintsConjunction{ + { + NumReplicas: 1, + Constraints: []Constraint{ + { + Type: Constraint_REQUIRED, + Key: "duck", + Value: "bar1", + }, + { + Type: Constraint_REQUIRED, + Key: "duck", + Value: "bar2", + }, + }, + }, + { + NumReplicas: 2, + Constraints: []Constraint{ + { + Type: Constraint_REQUIRED, + Key: "duck", + Value: "foo", + }, + }, + }, + }, expected: `range_min_bytes: 1 range_max_bytes: 1 gc: ttlseconds: 1 -num_replicas: 1 +num_replicas: 2 +num_voters: 1 +num_voters_configured_separately: true constraints: {'+duck=bar1,+duck=bar2': 1, +duck=foo: 2} +voter_constraints: {'+duck=bar1,+duck=bar2': 1, +duck=foo: 2} lease_preferences: [] `, }, @@ -559,8 +662,11 @@ lease_preferences: [] range_max_bytes: 1 gc: ttlseconds: 1 -num_replicas: 1 +num_replicas: 2 +num_voters: 1 +num_voters_configured_separately: true constraints: [] +voter_constraints: [] lease_preferences: [] `, }, @@ -580,8 +686,11 @@ lease_preferences: [] range_max_bytes: 1 gc: ttlseconds: 1 -num_replicas: 1 +num_replicas: 2 +num_voters: 1 +num_voters_configured_separately: true constraints: [] +voter_constraints: [] lease_preferences: [[+duck=foo]] `, }, @@ -597,6 +706,17 @@ lease_preferences: [[+duck=foo]] }, }, }, + voterConstraints: []ConstraintsConjunction{ + { + Constraints: []Constraint{ + { + Type: Constraint_PROHIBITED, + Key: "duck", + Value: "bar", + }, + }, + }, + }, leasePreferences: []LeasePreference{ { Constraints: []Constraint{ @@ -626,8 +746,11 @@ lease_preferences: [[+duck=foo]] range_max_bytes: 1 gc: ttlseconds: 1 -num_replicas: 1 +num_replicas: 2 +num_voters: 1 +num_voters_configured_separately: true constraints: [+duck=foo] +voter_constraints: [-duck=bar] lease_preferences: [[+duck=bar1, +duck=bar2], [-duck=foo]] `, }, @@ -636,6 +759,7 @@ lease_preferences: [[+duck=bar1, +duck=bar2], [-duck=foo]] for _, tc := range testCases { t.Run("", func(t *testing.T) { original.Constraints = tc.constraints + original.VoterConstraints = tc.voterConstraints original.LeasePreferences = tc.leasePreferences body, err := yaml.Marshal(original) if err != nil { diff --git a/pkg/config/zonepb/zone_yaml.go b/pkg/config/zonepb/zone_yaml.go index f513f9b8652c..2f7a7c4425fa 100644 --- a/pkg/config/zonepb/zone_yaml.go +++ b/pkg/config/zonepb/zone_yaml.go @@ -199,15 +199,18 @@ func (c *ConstraintsList) UnmarshalYAML(unmarshal func(interface{}) error) error // // TODO(a-robinson,v2.2): Remove the experimental_lease_preferences field. type marshalableZoneConfig struct { - RangeMinBytes *int64 `json:"range_min_bytes" yaml:"range_min_bytes"` - RangeMaxBytes *int64 `json:"range_max_bytes" yaml:"range_max_bytes"` - GC *GCPolicy `json:"gc"` - NumReplicas *int32 `json:"num_replicas" yaml:"num_replicas"` - Constraints ConstraintsList `json:"constraints" yaml:"constraints,flow"` - LeasePreferences []LeasePreference `json:"lease_preferences" yaml:"lease_preferences,flow"` - ExperimentalLeasePreferences []LeasePreference `json:"experimental_lease_preferences" yaml:"experimental_lease_preferences,flow,omitempty"` - Subzones []Subzone `json:"subzones" yaml:"-"` - SubzoneSpans []SubzoneSpan `json:"subzone_spans" yaml:"-"` + RangeMinBytes *int64 `json:"range_min_bytes" yaml:"range_min_bytes"` + RangeMaxBytes *int64 `json:"range_max_bytes" yaml:"range_max_bytes"` + GC *GCPolicy `json:"gc"` + NumReplicas *int32 `json:"num_replicas" yaml:"num_replicas"` + NumVoters *int32 `json:"num_voters" yaml:"num_voters"` + NumVotersConfiguredSeparately bool `json:"num_voters_configured_separately" yaml:"num_voters_configured_separately"` + Constraints ConstraintsList `json:"constraints" yaml:"constraints,flow"` + VoterConstraints ConstraintsList `json:"voter_constraints" yaml:"voter_constraints,flow"` + LeasePreferences []LeasePreference `json:"lease_preferences" yaml:"lease_preferences,flow"` + ExperimentalLeasePreferences []LeasePreference `json:"experimental_lease_preferences" yaml:"experimental_lease_preferences,flow,omitempty"` + Subzones []Subzone `json:"subzones" yaml:"-"` + SubzoneSpans []SubzoneSpan `json:"subzone_spans" yaml:"-"` } func zoneConfigToMarshalable(c ZoneConfig) marshalableZoneConfig { @@ -226,6 +229,11 @@ func zoneConfigToMarshalable(c ZoneConfig) marshalableZoneConfig { m.NumReplicas = proto.Int32(*c.NumReplicas) } m.Constraints = ConstraintsList{c.Constraints, c.InheritedConstraints} + if c.NumVotersConfiguredSeparately && c.NumVoters != nil && *c.NumVoters != 0 { + m.NumVoters = proto.Int32(*c.NumVoters) + m.NumVotersConfiguredSeparately = true + } + m.VoterConstraints = ConstraintsList{c.VoterConstraints, c.InheritedVoterConstraints} if !c.InheritedLeasePreferences { m.LeasePreferences = c.LeasePreferences } @@ -255,6 +263,12 @@ func zoneConfigFromMarshalable(m marshalableZoneConfig, c ZoneConfig) ZoneConfig } c.Constraints = m.Constraints.Constraints c.InheritedConstraints = m.Constraints.Inherited + if m.NumVotersConfiguredSeparately && m.NumVoters != nil { + c.NumVotersConfiguredSeparately = true + c.NumVoters = proto.Int32(*m.NumVoters) + } + c.VoterConstraints = m.VoterConstraints.Constraints + c.InheritedVoterConstraints = m.VoterConstraints.Inherited if m.LeasePreferences != nil { c.LeasePreferences = m.LeasePreferences } diff --git a/pkg/sql/logictest/testdata/logic_test/zone_config b/pkg/sql/logictest/testdata/logic_test/zone_config index 3b124047e981..39ed1ba7b5b2 100644 --- a/pkg/sql/logictest/testdata/logic_test/zone_config +++ b/pkg/sql/logictest/testdata/logic_test/zone_config @@ -195,3 +195,60 @@ ORDER BY feature_name ---- sql.schema.alter_range.configure_zone sql.schema.alter_table.configure_zone + + +# Check that configuring num_voters separately from num_replicas behaves as +# expected, across setting them directly and through inheritance, + +# Check that num_voters and voter_constraints show up in tandem if num_voters is +# configured explicitly. +statement ok +ALTER TABLE a CONFIGURE ZONE USING num_voters = 1; + +query IT +SELECT zone_id, raw_config_sql FROM [SHOW ZONE CONFIGURATION FOR TABLE a] +---- +53 ALTER TABLE a CONFIGURE ZONE USING + range_min_bytes = 1234567, + range_max_bytes = 536870912, + gc.ttlseconds = 90000, + num_replicas = 3, + num_voters = 1, + constraints = '[]', + voter_constraints = '[]', + lease_preferences = '[]' + +# TODO(aayush) More testing +statement ok +ALTER TABLE a CONFIGURE ZONE DISCARD + +statement ok +ALTER TABLE a CONFIGURE ZONE USING num_replicas = 5; + +statement error pq: could not validate zone config: when per-voter constraints are set, num_voters must be set as well +ALTER TABLE a CONFIGURE ZONE USING voter_constraints = '{"+region=test": 1}' + +query IT +SELECT zone_id, raw_config_sql FROM [SHOW ZONE CONFIGURATION FOR TABLE a] +---- +53 ALTER TABLE a CONFIGURE ZONE USING + range_min_bytes = 1234567, + range_max_bytes = 536870912, + gc.ttlseconds = 90000, + num_replicas = 5, + constraints = '[]', + lease_preferences = '[]' + +statement ok +ALTER TABLE a CONFIGURE ZONE USING constraints = '{"+region=test": 1}' + +query IT +SELECT zone_id, raw_config_sql FROM [SHOW ZONE CONFIGURATION FOR TABLE a] +---- +53 ALTER TABLE a CONFIGURE ZONE USING + range_min_bytes = 1234567, + range_max_bytes = 536870912, + gc.ttlseconds = 90000, + num_replicas = 5, + constraints = '{+region=test: 1}', + lease_preferences = '[]' diff --git a/pkg/sql/set_zone_config.go b/pkg/sql/set_zone_config.go index 9c13456e8b10..1ec68a2bc75a 100644 --- a/pkg/sql/set_zone_config.go +++ b/pkg/sql/set_zone_config.go @@ -66,6 +66,10 @@ var supportedZoneConfigOptions = map[tree.Name]struct { "range_min_bytes": {types.Int, func(c *zonepb.ZoneConfig, d tree.Datum) { c.RangeMinBytes = proto.Int64(int64(tree.MustBeDInt(d))) }}, "range_max_bytes": {types.Int, func(c *zonepb.ZoneConfig, d tree.Datum) { c.RangeMaxBytes = proto.Int64(int64(tree.MustBeDInt(d))) }}, "num_replicas": {types.Int, func(c *zonepb.ZoneConfig, d tree.Datum) { c.NumReplicas = proto.Int32(int32(tree.MustBeDInt(d))) }}, + "num_voters": {types.Int, func(c *zonepb.ZoneConfig, d tree.Datum) { + c.NumVoters = proto.Int32(int32(tree.MustBeDInt(d))) + c.NumVotersConfiguredSeparately = true + }}, "gc.ttlseconds": {types.Int, func(c *zonepb.ZoneConfig, d tree.Datum) { c.GC = &zonepb.GCPolicy{TTLSeconds: int32(tree.MustBeDInt(d))} }}, @@ -78,6 +82,15 @@ var supportedZoneConfigOptions = map[tree.Name]struct { c.Constraints = constraintsList.Constraints c.InheritedConstraints = false }}, + "voter_constraints": {types.String, func(c *zonepb.ZoneConfig, d tree.Datum) { + voterConstraintsList := zonepb.ConstraintsList{ + Constraints: c.VoterConstraints, + Inherited: c.InheritedVoterConstraints, + } + loadYAML(&voterConstraintsList, string(tree.MustBeDString(d))) + c.VoterConstraints = voterConstraintsList.Constraints + c.InheritedVoterConstraints = false + }}, "lease_preferences": {types.String, func(c *zonepb.ZoneConfig, d tree.Datum) { loadYAML(&c.LeasePreferences, string(tree.MustBeDString(d))) c.InheritedLeasePreferences = false @@ -645,8 +658,10 @@ func (n *setZoneConfigNode) startExec(params runParams) error { // require from changes made to parent zones. The extra protections are: // // RangeMinBytes and RangeMaxBytes must be set together - // LeasePreferences cannot be set unless Constraints are explicitly set - // Per-replica constraints cannot be set unless num_replicas is explicitly set + // LeasePreferences cannot be set unless Constraints/VoterConstraints are + // explicitly set + // Per-replica constraints cannot be set unless num_replicas is explicitly + // set if err := finalZone.ValidateTandemFields(); err != nil { err = errors.Wrap(err, "could not validate zone config") err = pgerror.WithCandidateCode(err, pgcode.InvalidParameterValue) @@ -723,7 +738,14 @@ type nodeGetter func(context.Context, *serverpb.NodesRequest) (*serverpb.NodesRe // will be rejected. Additionally, invalid constraints such as // [+region=us-east1, -region=us-east1] will also be rejected. func validateNoRepeatKeysInZone(zone *zonepb.ZoneConfig) error { - for _, constraints := range zone.Constraints { + if err := validateNoRepeatKeysInConjunction(zone.Constraints); err != nil { + return err + } + return validateNoRepeatKeysInConjunction(zone.VoterConstraints) +} + +func validateNoRepeatKeysInConjunction(conjunctions []zonepb.ConstraintsConjunction) error { + for _, constraints := range conjunctions { // Because we expect to have a small number of constraints, a nested // loop is probably better than allocating a map. for i, curr := range constraints.Constraints { @@ -771,7 +793,7 @@ func validateNoRepeatKeysInZone(zone *zonepb.ZoneConfig) error { func validateZoneAttrsAndLocalities( ctx context.Context, getNodes nodeGetter, zone *zonepb.ZoneConfig, ) error { - if len(zone.Constraints) == 0 && len(zone.LeasePreferences) == 0 { + if len(zone.VoterConstraints) == 0 && len(zone.Constraints) == 0 && len(zone.LeasePreferences) == 0 { return nil } @@ -801,6 +823,11 @@ func validateZoneAttrsAndLocalities( addToValidate(constraint) } } + for _, constraints := range zone.VoterConstraints { + for _, constraint := range constraints.Constraints { + addToValidate(constraint) + } + } for _, leasePreferences := range zone.LeasePreferences { for _, constraint := range leasePreferences.Constraints { addToValidate(constraint) diff --git a/pkg/sql/set_zone_config_test.go b/pkg/sql/set_zone_config_test.go index bd40be7069d9..6b3ab22b9e34 100644 --- a/pkg/sql/set_zone_config_test.go +++ b/pkg/sql/set_zone_config_test.go @@ -20,6 +20,8 @@ import ( "github.com/cockroachdb/cockroach/pkg/server/status/statuspb" "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/require" yaml "gopkg.in/yaml.v2" ) @@ -30,34 +32,37 @@ func TestValidateNoRepeatKeysInZone(t *testing.T) { constraint string expectSuccess bool }{ - {`constraints: ["+region=us-east-1"]`, true}, - {`constraints: ["+region=us-east-1", "+zone=pa"]`, true}, - {`constraints: ["+region=us-east-1", "-region=us-west-1"]`, true}, - {`constraints: ["+region=us-east-1", "+region=us-east-2"]`, false}, - {`constraints: ["+region=us-east-1", "+zone=pa", "+region=us-west-1"]`, false}, - {`constraints: ["+region=us-east-1", "-region=us-east-1"]`, false}, - {`constraints: ["-region=us-east-1", "+region=us-east-1"]`, false}, - {`constraints: {"+region=us-east-1":2, "+region=us-east-2":2}`, true}, - {`constraints: {"+region=us-east-1,+region=us-west-1":2, "+region=us-east-2":2}`, false}, - {`constraints: ["+x1", "+x2", "+x3"]`, true}, - {`constraints: ["+x1", "+x1"]`, false}, - {`constraints: ["+x1", "-x1"]`, false}, - {`constraints: ["-x1", "+x1"]`, false}, + {`["+region=us-east-1"]`, true}, + {`["+region=us-east-1", "+zone=pa"]`, true}, + {`["+region=us-east-1", "-region=us-west-1"]`, true}, + {`["+region=us-east-1", "+region=us-east-2"]`, false}, + {`["+region=us-east-1", "+zone=pa", "+region=us-west-1"]`, false}, + {`["+region=us-east-1", "-region=us-east-1"]`, false}, + {`["-region=us-east-1", "+region=us-east-1"]`, false}, + {`{"+region=us-east-1":2, "+region=us-east-2":2}`, true}, + {`{"+region=us-east-1,+region=us-west-1":2, "+region=us-east-2":2}`, false}, + {`["+x1", "+x2", "+x3"]`, true}, + {`["+x1", "+x1"]`, false}, + {`["+x1", "-x1"]`, false}, + {`["-x1", "+x1"]`, false}, } - - for _, tc := range testCases { + validate := func(constraint []byte, expectSuccess bool) { var zone zonepb.ZoneConfig - err := yaml.UnmarshalStrict([]byte(tc.constraint), &zone) + err := yaml.UnmarshalStrict(constraint, &zone) if err != nil { t.Fatal(err) } err = validateNoRepeatKeysInZone(&zone) - if err != nil && tc.expectSuccess { - t.Errorf("expected success for %q; got %v", tc.constraint, err) - } else if err == nil && !tc.expectSuccess { - t.Errorf("expected err for %q; got success", tc.constraint) + if err != nil && expectSuccess { + t.Errorf("expected success for %q; got %v", constraint, err) + } else if err == nil && !expectSuccess { + t.Errorf("expected err for %q; got success", constraint) } } + for _, tc := range testCases { + validate([]byte(`constraints: `+tc.constraint), tc.expectSuccess) + validate([]byte(`voter_constraints: `+tc.constraint), tc.expectSuccess) + } } func TestValidateZoneAttrsAndLocalities(t *testing.T) { @@ -137,6 +142,7 @@ func TestValidateZoneAttrsAndLocalities(t *testing.T) { const expectSuccess = 0 const expectParseErr = 1 const expectValidateErr = 2 + // TODO(aayush): Clean these tests up to be more DRY. for i, tc := range []struct { cfg string expectErr int @@ -160,6 +166,21 @@ func TestValidateZoneAttrsAndLocalities(t *testing.T) { {`constraints: ["+highcp"]`, expectValidateErr, getNodes}, {`constraints: ["+owmem"]`, expectValidateErr, getNodes}, {`constraints: ["+sssd"]`, expectValidateErr, getNodes}, + {`voter_constraints: ["+region=us-east1"]`, expectSuccess, getNodes}, + {`voter_constraints: {"+region=us-east1": 2, "+region=eu-west1": 1}`, expectSuccess, getNodes}, + {`voter_constraints: ["+region=us-eas1"]`, expectValidateErr, getNodes}, + {`voter_constraints: {"+region=us-eas1": 2, "+region=eu-west1": 1}`, expectValidateErr, getNodes}, + {`voter_constraints: {"+region=us-east1": 2, "+region=eu-wes1": 1}`, expectValidateErr, getNodes}, + {`voter_constraints: ["+regio=us-east1"]`, expectValidateErr, getNodes}, + {`voter_constraints: ["+rack=17"]`, expectSuccess, getNodes}, + {`voter_constraints: ["+rack=18"]`, expectValidateErr, getNodes}, + {`voter_constraints: ["+rach=17"]`, expectValidateErr, getNodes}, + {`voter_constraints: ["+highcpu"]`, expectSuccess, getNodes}, + {`voter_constraints: ["+lowmem"]`, expectSuccess, getNodes}, + {`voter_constraints: ["+ssd"]`, expectSuccess, getNodes}, + {`voter_constraints: ["+highcp"]`, expectValidateErr, getNodes}, + {`voter_constraints: ["+owmem"]`, expectValidateErr, getNodes}, + {`voter_constraints: ["+sssd"]`, expectValidateErr, getNodes}, {`lease_preferences: [["+region=us-east1", "+ssd"], ["+geo=us", "+highcpu"]]`, expectSuccess, getNodes}, {`lease_preferences: [["+region=us-eat1", "+ssd"], ["+geo=us", "+highcpu"]]`, expectValidateErr, getNodes}, {`lease_preferences: [["+region=us-east1", "+foo"], ["+geo=us", "+highcpu"]]`, expectValidateErr, getNodes}, @@ -172,6 +193,14 @@ func TestValidateZoneAttrsAndLocalities(t *testing.T) { {`constraints: ["-highcpu"]`, expectSuccess, getNodes}, {`constraints: ["-ssd"]`, expectSuccess, getNodes}, {`constraints: ["-fake"]`, expectSuccess, getNodes}, + {`voter_constraints: ["-region=us-east1"]`, expectSuccess, singleLocalityNode}, + {`voter_constraints: ["-ssd"]`, expectSuccess, singleAttrNode}, + {`voter_constraints: ["-regio=us-eas1"]`, expectSuccess, getNodes}, + {`voter_constraints: {"-region=us-eas1": 2, "-region=eu-wes1": 1}`, expectSuccess, getNodes}, + {`voter_constraints: ["-foo=bar"]`, expectSuccess, getNodes}, + {`voter_constraints: ["-highcpu"]`, expectSuccess, getNodes}, + {`voter_constraints: ["-ssd"]`, expectSuccess, getNodes}, + {`voter_constraints: ["-fake"]`, expectSuccess, getNodes}, } { var zone zonepb.ZoneConfig err := yaml.UnmarshalStrict([]byte(tc.cfg), &zone) @@ -189,3 +218,50 @@ func TestValidateZoneAttrsAndLocalities(t *testing.T) { } } } + +func TestValidateVoterConstraints(t *testing.T) { + defer leaktest.AfterTest(t)() + for _, tc := range []struct { + constraints string + voterConstraints string + shouldFail bool + errRegex string + }{ + {``, ``, false, ""}, + {`["-foo=bar"]`, `{"+foo=bar": 1}`, true, "conflicts with voter_constraint"}, + {`["-foo=bar"]`, `{"+foo=bar": 1}`, true, "conflicts with voter_constraint"}, + {`["-foo=bar", "+duck=bar"]`, `["-foo=duck", "+duck=bar"]`, true, "voter_constraints cannot contain prohibitive"}, + {`["-foo=bar"]`, `["-foo=bar"]`, true, "voter_constraints cannot contain prohibitive"}, + {`["-foo=bar"]`, `["-duck=bar"]`, true, "voter_constraints cannot contain prohibitive"}, + {`["+foo=bar"]`, `["+foo=bar"]`, false, ""}, + {`{"+foo=bar": 1, "+foo=duck": 2}`, `{"+foo=bar": 3}`, false, ""}, + // TODO DURING REVIEW: Allowing for configurations like this one seems very + // desirable, but it also allows the user to more easily walk themselves + // into unsatisfiable zone configurations. Do we want to allow the + // flexibility permitted by allowing `constraints` and `voter_constraints` + // to be compatible but disjoint? Or do we want to force `voter_constraints` + // to be a strictly narrower subset of `constraints`? + // + // I cant foresee any real complications with this flexibility at the + // allocator level but its likely there will be some. + {`{"+region=A": 1, "+region=B": 1, "+region=C": 1}`, `["+ssd"]`, false, ""}, + } { + zone := zonepb.NewZoneConfig() + zone.NumVotersConfiguredSeparately = true + zone.NumVoters = proto.Int32(3) + zone.NumReplicas = proto.Int32(3) + + if err := yaml.UnmarshalStrict([]byte(`constraints: `+tc.constraints), &zone); err != nil { + t.Fatal(err) + } + if err := yaml.UnmarshalStrict([]byte(`voter_constraints: `+tc.voterConstraints), &zone); err != nil { + t.Fatal(err) + } + err := zone.Validate() + if err != nil && tc.shouldFail { + require.Regexp(t, tc.errRegex, err) + } else if tc.shouldFail { + t.Fatalf("validation unexpectedly succeeded for constraints: %s and voter_constraints: %s", tc.constraints, tc.voterConstraints) + } + } +} diff --git a/pkg/sql/show_zone_config.go b/pkg/sql/show_zone_config.go index cc21da8aba4c..ecc72a7b4686 100644 --- a/pkg/sql/show_zone_config.go +++ b/pkg/sql/show_zone_config.go @@ -176,6 +176,13 @@ func zoneConfigToSQL(zs *tree.ZoneSpecifier, zone *zonepb.ZoneConfig) (string, e return "", err } constraints = strings.TrimSpace(constraints) + voterConstraints, err := yamlMarshalFlow(zonepb.ConstraintsList{ + Constraints: zone.VoterConstraints, + Inherited: zone.InheritedVoterConstraints}) + if err != nil { + return "", err + } + voterConstraints = strings.TrimSpace(voterConstraints) prefs, err := yamlMarshalFlow(zone.LeasePreferences) if err != nil { return "", err @@ -206,11 +213,23 @@ func zoneConfigToSQL(zs *tree.ZoneSpecifier, zone *zonepb.ZoneConfig) (string, e f.Printf("\tnum_replicas = %d", *zone.NumReplicas) useComma = true } + if zone.NumVotersConfiguredSeparately { + if zone.NumVoters != nil { + writeComma(f, useComma) + f.Printf("\tnum_voters = %d", *zone.NumVoters) + useComma = true + } + } if !zone.InheritedConstraints { writeComma(f, useComma) f.Printf("\tconstraints = %s", lex.EscapeSQLString(constraints)) useComma = true } + if !zone.InheritedVoterConstraints && zone.NumVotersConfiguredSeparately { + writeComma(f, useComma) + f.Printf("\tvoter_constraints = %s", lex.EscapeSQLString(voterConstraints)) + useComma = true + } if !zone.InheritedLeasePreferences { writeComma(f, useComma) f.Printf("\tlease_preferences = %s", lex.EscapeSQLString(prefs))