github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/constraint/analyzer.go (about)

     1  // Copyright 2019 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package constraint
    12  
    13  import (
    14  	"context"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/config/zonepb"
    17  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    18  )
    19  
    20  // AnalyzedConstraints represents the result or AnalyzeConstraints(). It
    21  // combines a zone's constraints with information about which stores satisfy
    22  // what term of the constraints disjunction.
    23  type AnalyzedConstraints struct {
    24  	Constraints []zonepb.ConstraintsConjunction
    25  	// True if the per-replica constraints don't fully cover all the desired
    26  	// replicas in the range (sum(constraints.NumReplicas) < zone.NumReplicas).
    27  	// In such cases, we allow replicas that don't match any of the per-replica
    28  	// constraints, but never mark them as necessary.
    29  	UnconstrainedReplicas bool
    30  	// For each conjunction of constraints in the above slice, track which
    31  	// StoreIDs satisfy them. This field is unused if there are no constraints.
    32  	SatisfiedBy [][]roachpb.StoreID
    33  	// Maps from StoreID to the indices in the constraints slice of which
    34  	// constraints the store satisfies. This field is unused if there are no
    35  	// constraints.
    36  	Satisfies map[roachpb.StoreID][]int
    37  }
    38  
    39  // AnalyzeConstraints processes the zone config constraints that apply to a
    40  // range along with the current replicas for a range, spitting back out
    41  // information about which constraints are satisfied by which replicas and
    42  // which replicas satisfy which constraints, aiding in allocation decisions.
    43  func AnalyzeConstraints(
    44  	ctx context.Context,
    45  	getStoreDescFn func(roachpb.StoreID) (roachpb.StoreDescriptor, bool),
    46  	existing []roachpb.ReplicaDescriptor,
    47  	zone *zonepb.ZoneConfig,
    48  ) AnalyzedConstraints {
    49  	result := AnalyzedConstraints{
    50  		Constraints: zone.Constraints,
    51  	}
    52  
    53  	if len(zone.Constraints) > 0 {
    54  		result.SatisfiedBy = make([][]roachpb.StoreID, len(zone.Constraints))
    55  		result.Satisfies = make(map[roachpb.StoreID][]int)
    56  	}
    57  
    58  	var constrainedReplicas int32
    59  	for i, subConstraints := range zone.Constraints {
    60  		constrainedReplicas += subConstraints.NumReplicas
    61  		for _, repl := range existing {
    62  			// If for some reason we don't have the store descriptor (which shouldn't
    63  			// happen once a node is hooked into gossip), trust that it's valid. This
    64  			// is a much more stable failure state than frantically moving everything
    65  			// off such a node.
    66  			store, ok := getStoreDescFn(repl.StoreID)
    67  			if !ok || ConjunctionsCheck(store, subConstraints.Constraints) {
    68  				result.SatisfiedBy[i] = append(result.SatisfiedBy[i], store.StoreID)
    69  				result.Satisfies[store.StoreID] = append(result.Satisfies[store.StoreID], i)
    70  			}
    71  		}
    72  	}
    73  	if constrainedReplicas > 0 && constrainedReplicas < *zone.NumReplicas {
    74  		result.UnconstrainedReplicas = true
    75  	}
    76  	return result
    77  }
    78  
    79  // ConjunctionsCheck checks a store against a single set of constraints (out of
    80  // the possibly numerous sets that apply to a range), returning true iff the
    81  // store matches the constraints. The contraints are AND'ed together; a store
    82  // matches the conjunction if it matches all of them.
    83  func ConjunctionsCheck(store roachpb.StoreDescriptor, constraints []zonepb.Constraint) bool {
    84  	for _, constraint := range constraints {
    85  		// StoreSatisfiesConstraint returns whether a store matches the given constraint.
    86  		hasConstraint := zonepb.StoreMatchesConstraint(store, constraint)
    87  		if (constraint.Type == zonepb.Constraint_REQUIRED && !hasConstraint) ||
    88  			(constraint.Type == zonepb.Constraint_PROHIBITED && hasConstraint) {
    89  			return false
    90  		}
    91  	}
    92  	return true
    93  }