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

     1  // Copyright (c) 2016 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  	"errors"
    25  	"fmt"
    26  
    27  	"github.com/m3db/m3/src/cluster/placement"
    28  )
    29  
    30  var (
    31  	errNotEnoughIsolationGroups    = errors.New("not enough isolation groups to take shards, please make sure RF is less than number of isolation groups")
    32  	errIncompatibleWithShardedAlgo = errors.New("could not apply sharded algo on the placement")
    33  )
    34  
    35  type shardedPlacementAlgorithm struct {
    36  	opts placement.Options
    37  }
    38  
    39  func newShardedAlgorithm(opts placement.Options) placement.Algorithm {
    40  	return shardedPlacementAlgorithm{opts: opts}
    41  }
    42  
    43  func (a shardedPlacementAlgorithm) IsCompatibleWith(p placement.Placement) error {
    44  	if !p.IsSharded() {
    45  		return errIncompatibleWithShardedAlgo
    46  	}
    47  
    48  	return nil
    49  }
    50  
    51  func (a shardedPlacementAlgorithm) InitialPlacement(
    52  	instances []placement.Instance,
    53  	shards []uint32,
    54  	rf int,
    55  ) (placement.Placement, error) {
    56  	ph := newInitHelper(placement.Instances(instances).Clone(), shards, a.opts)
    57  	if err := ph.placeShards(newShards(shards), nil, ph.Instances()); err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	var (
    62  		p   = ph.generatePlacement()
    63  		err error
    64  	)
    65  	for i := 1; i < rf; i++ {
    66  		p, err = a.AddReplica(p)
    67  		if err != nil {
    68  			return nil, err
    69  		}
    70  	}
    71  
    72  	return tryCleanupShardState(p, a.opts)
    73  }
    74  
    75  func (a shardedPlacementAlgorithm) AddReplica(p placement.Placement) (placement.Placement, error) {
    76  	if err := a.IsCompatibleWith(p); err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	p = p.Clone()
    81  	ph := newAddReplicaHelper(p, a.opts)
    82  	if err := ph.placeShards(newShards(p.Shards()), nil, nonLeavingInstances(ph.Instances())); err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	if err := ph.optimize(safe); err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	return tryCleanupShardState(ph.generatePlacement(), a.opts)
    91  }
    92  
    93  func (a shardedPlacementAlgorithm) RemoveInstances(
    94  	p placement.Placement,
    95  	instanceIDs []string,
    96  ) (placement.Placement, error) {
    97  	if err := a.IsCompatibleWith(p); err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	p = p.Clone()
   102  	for _, instanceID := range instanceIDs {
   103  		ph, leavingInstance, err := newRemoveInstanceHelper(p, instanceID, a.opts)
   104  		if err != nil {
   105  			return nil, err
   106  		}
   107  		// place the shards from the leaving instance to the rest of the cluster
   108  		if err := ph.placeShards(leavingInstance.Shards().All(), leavingInstance, ph.Instances()); err != nil {
   109  			return nil, err
   110  		}
   111  
   112  		if err := ph.optimize(safe); err != nil {
   113  			return nil, err
   114  		}
   115  
   116  		if p, _, err = addInstanceToPlacement(ph.generatePlacement(), leavingInstance, withShards); err != nil {
   117  			return nil, err
   118  		}
   119  	}
   120  	return tryCleanupShardState(p, a.opts)
   121  }
   122  
   123  func (a shardedPlacementAlgorithm) AddInstances(
   124  	p placement.Placement,
   125  	instances []placement.Instance,
   126  ) (placement.Placement, error) {
   127  	if err := a.IsCompatibleWith(p); err != nil {
   128  		return nil, err
   129  	}
   130  
   131  	p = p.Clone()
   132  	for _, instance := range instances {
   133  		ph, addingInstance, err := newAddInstanceHelper(p, instance, a.opts, withLeavingShardsOnly)
   134  		if err != nil {
   135  			return nil, err
   136  		}
   137  
   138  		if err := ph.addInstance(addingInstance); err != nil {
   139  			return nil, err
   140  		}
   141  
   142  		p = ph.generatePlacement()
   143  	}
   144  
   145  	return tryCleanupShardState(p, a.opts)
   146  }
   147  
   148  func (a shardedPlacementAlgorithm) ReplaceInstances(
   149  	p placement.Placement,
   150  	leavingInstanceIDs []string,
   151  	addingInstances []placement.Instance,
   152  ) (placement.Placement, error) {
   153  	if err := a.IsCompatibleWith(p); err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	p = p.Clone()
   158  	ph, leavingInstances, addingInstances, err := newReplaceInstanceHelper(p, leavingInstanceIDs, addingInstances, a.opts)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  
   163  	for _, leavingInstance := range leavingInstances {
   164  		err = ph.placeShards(leavingInstance.Shards().All(), leavingInstance, addingInstances)
   165  		if err != nil && err != errNotEnoughIsolationGroups {
   166  			// errNotEnoughIsolationGroups means the adding instances do not
   167  			// have enough isolation groups to take all the shards, but the rest
   168  			// instances might have more isolation groups to take all the shards.
   169  			return nil, err
   170  		}
   171  		load := loadOnInstance(leavingInstance)
   172  		if load != 0 && !a.opts.AllowPartialReplace() {
   173  			return nil, fmt.Errorf("could not fully replace all shards from %s, %d shards left unassigned",
   174  				leavingInstance.ID(), load)
   175  		}
   176  	}
   177  
   178  	if a.opts.AllowPartialReplace() {
   179  		// Place the shards left on the leaving instance to the rest of the cluster.
   180  		for _, leavingInstance := range leavingInstances {
   181  			if err = ph.placeShards(leavingInstance.Shards().All(), leavingInstance, ph.Instances()); err != nil {
   182  				return nil, err
   183  			}
   184  		}
   185  
   186  		if err := ph.optimize(unsafe); err != nil {
   187  			return nil, err
   188  		}
   189  	}
   190  
   191  	p = ph.generatePlacement()
   192  	for _, leavingInstance := range leavingInstances {
   193  		if p, _, err = addInstanceToPlacement(p, leavingInstance, withShards); err != nil {
   194  			return nil, err
   195  		}
   196  	}
   197  	return tryCleanupShardState(p, a.opts)
   198  }
   199  
   200  func (a shardedPlacementAlgorithm) MarkShardsAvailable(
   201  	p placement.Placement,
   202  	instanceID string,
   203  	shardIDs ...uint32,
   204  ) (placement.Placement, error) {
   205  	if err := a.IsCompatibleWith(p); err != nil {
   206  		return nil, err
   207  	}
   208  
   209  	return markShardsAvailable(p.Clone(), instanceID, shardIDs, a.opts)
   210  }
   211  
   212  func (a shardedPlacementAlgorithm) MarkAllShardsAvailable(
   213  	p placement.Placement,
   214  ) (placement.Placement, bool, error) {
   215  	if err := a.IsCompatibleWith(p); err != nil {
   216  		return nil, false, err
   217  	}
   218  
   219  	return markAllShardsAvailable(p, a.opts)
   220  }
   221  
   222  func (a shardedPlacementAlgorithm) BalanceShards(
   223  	p placement.Placement,
   224  ) (placement.Placement, error) {
   225  	ph := newHelper(p, p.ReplicaFactor(), a.opts)
   226  	if err := ph.optimize(unsafe); err != nil {
   227  		return nil, fmt.Errorf("shard balance optimization failed: %w", err)
   228  	}
   229  
   230  	return tryCleanupShardState(ph.generatePlacement(), a.opts)
   231  }