vitess.io/vitess@v0.16.2/go/vt/vtctl/reparentutil/reparent_sorter.go (about) 1 /* 2 Copyright 2021 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package reparentutil 18 19 import ( 20 "sort" 21 22 "vitess.io/vitess/go/mysql" 23 "vitess.io/vitess/go/vt/vterrors" 24 25 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 26 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 27 ) 28 29 // reparentSorter sorts tablets by GTID positions and Promotion rules aimed at finding the best 30 // candidate for intermediate promotion in emergency reparent shard, and the new primary in planned reparent shard 31 type reparentSorter struct { 32 tablets []*topodatapb.Tablet 33 positions []mysql.Position 34 durability Durabler 35 } 36 37 // newReparentSorter creates a new reparentSorter 38 func newReparentSorter(tablets []*topodatapb.Tablet, positions []mysql.Position, durability Durabler) *reparentSorter { 39 return &reparentSorter{ 40 tablets: tablets, 41 positions: positions, 42 durability: durability, 43 } 44 } 45 46 // Len implements the Interface for sorting 47 func (rs *reparentSorter) Len() int { return len(rs.tablets) } 48 49 // Swap implements the Interface for sorting 50 func (rs *reparentSorter) Swap(i, j int) { 51 rs.tablets[i], rs.tablets[j] = rs.tablets[j], rs.tablets[i] 52 rs.positions[i], rs.positions[j] = rs.positions[j], rs.positions[i] 53 } 54 55 // Less implements the Interface for sorting 56 func (rs *reparentSorter) Less(i, j int) bool { 57 // Returning "true" in this function means [i] is before [j] in the sorting order, 58 // which will lead to [i] be a better candidate for promotion 59 60 // Should not happen 61 // fail-safe code 62 if rs.tablets[i] == nil { 63 return false 64 } 65 if rs.tablets[j] == nil { 66 return true 67 } 68 69 if !rs.positions[i].AtLeast(rs.positions[j]) { 70 // [i] does not have all GTIDs that [j] does 71 return false 72 } 73 if !rs.positions[j].AtLeast(rs.positions[i]) { 74 // [j] does not have all GTIDs that [i] does 75 return true 76 } 77 78 // at this point, both have the same GTIDs 79 // so we check their promotion rules 80 jPromotionRule := PromotionRule(rs.durability, rs.tablets[j]) 81 iPromotionRule := PromotionRule(rs.durability, rs.tablets[i]) 82 return !jPromotionRule.BetterThan(iPromotionRule) 83 } 84 85 // sortTabletsForReparent sorts the tablets, given their positions for emergency reparent shard and planned reparent shard. 86 // Tablets are sorted first by their replication positions, with ties broken by the promotion rules. 87 func sortTabletsForReparent(tablets []*topodatapb.Tablet, positions []mysql.Position, durability Durabler) error { 88 // throw an error internal error in case of unequal number of tablets and positions 89 // fail-safe code prevents panic in sorting in case the lengths are unequal 90 if len(tablets) != len(positions) { 91 return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unequal number of tablets and positions") 92 } 93 94 sort.Sort(newReparentSorter(tablets, positions, durability)) 95 return nil 96 }