github.com/m3db/m3@v1.5.0/src/cluster/placement/planner/planner.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 planner 22 23 import ( 24 "sort" 25 26 "github.com/m3db/m3/src/cluster/placement" 27 ) 28 29 // shardAwareDeploymentPlanner plans the deployment so that as many instances can be deployed 30 // at the same time without making more than 1 replica of any shard unavailable 31 type shardAwareDeploymentPlanner struct { 32 options placement.DeploymentOptions 33 } 34 35 // NewShardAwareDeploymentPlanner returns a deployment planner 36 func NewShardAwareDeploymentPlanner(options placement.DeploymentOptions) placement.DeploymentPlanner { 37 return shardAwareDeploymentPlanner{options: options} 38 } 39 40 func (dp shardAwareDeploymentPlanner) DeploymentSteps(p placement.Placement) [][]placement.Instance { 41 instances := p.Instances() 42 sort.Sort(placement.ByIDAscending(instances)) 43 var steps sortableSteps 44 for len(instances) > 0 { 45 step := getDeployStep(instances, dp.options.MaxStepSize()) 46 steps = append(steps, step) 47 instances = getLeftInstances(instances, step) 48 } 49 sort.Sort(steps) 50 return steps 51 } 52 53 func getDeployStep(instances []placement.Instance, maxStepSize int) []placement.Instance { 54 var parallel []placement.Instance 55 shards := make(map[uint32]struct{}) 56 for _, instance := range instances { 57 if len(parallel) >= maxStepSize { 58 break 59 } 60 if isSharingShard(shards, instance) { 61 continue 62 } 63 parallel = append(parallel, instance) 64 for _, s := range instance.Shards().All() { 65 shards[s.ID()] = struct{}{} 66 } 67 } 68 return parallel 69 } 70 71 func isSharingShard(shards map[uint32]struct{}, instance placement.Instance) bool { 72 for _, s := range instance.Shards().All() { 73 if _, exist := shards[s.ID()]; exist { 74 return true 75 } 76 } 77 return false 78 } 79 80 func getLeftInstances(all, toBeRemoved []placement.Instance) []placement.Instance { 81 for _, instance := range toBeRemoved { 82 all = removeInstance(all, instance) 83 } 84 return all 85 } 86 87 func removeInstance(all []placement.Instance, remove placement.Instance) []placement.Instance { 88 for i, instance := range all { 89 if instance.ID() == remove.ID() { 90 return append(all[:i], all[i+1:]...) 91 } 92 } 93 return all 94 } 95 96 type sortableSteps [][]placement.Instance 97 98 func (s sortableSteps) Len() int { 99 return len(s) 100 } 101 102 func (s sortableSteps) Less(i, j int) bool { 103 return len(s[i]) > len(s[j]) 104 } 105 106 func (s sortableSteps) Swap(i, j int) { 107 s[i], s[j] = s[j], s[i] 108 }