github.com/m3db/m3@v1.5.0/src/cluster/placement/algo/placement_checker.go (about)

     1  // Copyright (c) 2021 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package algo
    22  
    23  import (
    24  	"github.com/m3db/m3/src/cluster/placement"
    25  	"github.com/m3db/m3/src/cluster/shard"
    26  )
    27  
    28  type (
    29  	placementChecker struct {
    30  		instanceEvaluator instanceEvaluatorFunc
    31  	}
    32  	shardCheckFunc        func(shard.Shard) bool
    33  	instanceEvaluatorFunc func(map[string]struct{}, placement.Placement, shardCheckFunc) bool
    34  )
    35  
    36  var (
    37  	// globalChecker verifies that the specified list of instances are the only instances
    38  	// in the given state in the placement.
    39  	globalChecker = &placementChecker{
    40  		instanceEvaluator: onlyAmongAllInstancesInState,
    41  	}
    42  
    43  	// localChecker verifies that the specified list of instances are in the given state,
    44  	// and ignores the state of the rest of instances in the placement.
    45  	localChecker = &placementChecker{
    46  		instanceEvaluator: instancesInState,
    47  	}
    48  )
    49  
    50  func (pc *placementChecker) allInitializing(p placement.Placement, instances []string, nowNanos int64) bool {
    51  	ids := make(map[string]struct{}, len(instances))
    52  	for _, i := range instances {
    53  		ids[i] = struct{}{}
    54  	}
    55  
    56  	shardCheckFn := func(s shard.Shard) bool {
    57  		return s.State() == shard.Initializing && s.CutoverNanos() > nowNanos
    58  	}
    59  
    60  	return pc.instanceEvaluator(ids, p, shardCheckFn)
    61  }
    62  
    63  func (pc *placementChecker) allLeaving(p placement.Placement, instances []placement.Instance, nowNanos int64) bool {
    64  	ids := make(map[string]struct{}, len(instances))
    65  	for _, i := range instances {
    66  		ids[i.ID()] = struct{}{}
    67  	}
    68  
    69  	shardCheckFn := func(s shard.Shard) bool {
    70  		return s.State() == shard.Leaving && s.CutoffNanos() > nowNanos
    71  	}
    72  
    73  	return pc.instanceEvaluator(ids, p, shardCheckFn)
    74  }
    75  
    76  // allLeavingByIDs is a wrapper of allLeaving that uses string IDs
    77  func (pc *placementChecker) allLeavingByIDs(p placement.Placement, instanceIDs []string, nowNanos int64) bool {
    78  	instances := make([]placement.Instance, len(instanceIDs))
    79  	for i, id := range instanceIDs {
    80  		curr, exists := p.Instance(id)
    81  		if !exists {
    82  			return false
    83  		}
    84  
    85  		instances[i] = curr
    86  	}
    87  
    88  	return pc.allLeaving(p, instances, nowNanos)
    89  }
    90  
    91  func (pc *placementChecker) allAvailable(p placement.Placement, instances []string, nowNanos int64) bool {
    92  	ids := make(map[string]struct{}, len(instances))
    93  	for _, i := range instances {
    94  		ids[i] = struct{}{}
    95  	}
    96  
    97  	shardCheckFn := func(s shard.Shard) bool {
    98  		return s.State() == shard.Available && s.CutoffNanos() > nowNanos
    99  	}
   100  
   101  	return pc.instanceEvaluator(ids, p, shardCheckFn)
   102  }
   103  
   104  // onlyAmongAllInstancesInState returns true when only the specified instances
   105  // have all their shards in the expected state and the rest of instances
   106  // in the placement do not.
   107  func onlyAmongAllInstancesInState(
   108  	instanceIDs map[string]struct{},
   109  	p placement.Placement,
   110  	forEachShardFn shardCheckFunc,
   111  ) bool {
   112  	for _, instance := range p.Instances() {
   113  		if !instanceCheck(instance, forEachShardFn) {
   114  			continue
   115  		}
   116  		if _, ok := instanceIDs[instance.ID()]; !ok {
   117  			return false
   118  		}
   119  		delete(instanceIDs, instance.ID())
   120  	}
   121  
   122  	return len(instanceIDs) == 0
   123  }
   124  
   125  // instancesInState returns true when the specified instances
   126  // have all their shards in the expected state, and ignores the
   127  // rest of instances in the placement.
   128  func instancesInState(
   129  	instanceIDs map[string]struct{},
   130  	p placement.Placement,
   131  	forEachShardFn shardCheckFunc,
   132  ) bool {
   133  	if len(instanceIDs) == 0 {
   134  		return false
   135  	}
   136  
   137  	for id := range instanceIDs {
   138  		instance, exist := p.Instance(id)
   139  		if !exist {
   140  			return false
   141  		}
   142  
   143  		if !instanceCheck(instance, forEachShardFn) {
   144  			return false
   145  		}
   146  	}
   147  
   148  	return true
   149  }
   150  
   151  func instanceCheck(instance placement.Instance, shardCheckFn func(s shard.Shard) bool) bool {
   152  	for _, s := range instance.Shards().All() {
   153  		if !shardCheckFn(s) {
   154  			return false
   155  		}
   156  	}
   157  	return true
   158  }