github.com/matrixorigin/matrixone@v1.2.0/pkg/hakeeper/operator/builder.go (about) 1 // Copyright 2020 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 // Portions of this file are additionally subject to the following 15 // copyright. 16 // 17 // Copyright (C) 2021 Matrix Origin. 18 // 19 // Modified the behavior of the builder. 20 21 package operator 22 23 import ( 24 "fmt" 25 "sort" 26 27 "github.com/matrixorigin/matrixone/pkg/common/moerr" 28 29 "github.com/matrixorigin/matrixone/pkg/pb/logservice" 30 ) 31 32 // Builder is used to create operators. Usage: 33 // 34 // op, err := NewBuilder(desc, cluster, shard). 35 // RemovePeer(store1). 36 // AddPeer(peer1). 37 // Build(kind) 38 // 39 // The generated Operator will choose the most appropriate execution order 40 // according to various constraints. 41 type Builder struct { 42 // basic info 43 desc string 44 shardID uint64 45 epoch uint64 46 47 // operation record 48 originPeers peersMap 49 targetPeers peersMap 50 51 err error 52 53 // intermediate states 54 toAdd, toRemove peersMap // pending tasks. 55 steps []OpStep // generated steps. 56 } 57 58 // NewBuilder creates a Builder. 59 func NewBuilder(desc string, shardInfo logservice.LogShardInfo) *Builder { 60 b := &Builder{ 61 desc: desc, 62 shardID: shardInfo.ShardID, 63 epoch: shardInfo.Epoch, 64 } 65 66 // origin peers 67 err := b.err 68 originPeers := newPeersMap() 69 70 for replicaID, uuid := range shardInfo.Replicas { 71 if uuid == "" { 72 err = moerr.NewInternalErrorNoCtx("cannot build operator for shard with nil peer") 73 break 74 } 75 originPeers.Set(uuid, replicaID) 76 } 77 78 b.originPeers = originPeers 79 b.targetPeers = originPeers.Copy() 80 b.err = err 81 return b 82 } 83 84 // AddPeer records an add Peer operation in Builder. 85 func (b *Builder) AddPeer(uuid string, peer uint64) *Builder { 86 if b.err != nil { 87 return b 88 } 89 if uuid == "" { 90 b.err = moerr.NewInternalErrorNoCtx("cannot add peer to nil store") 91 return b 92 } 93 if old, ok := b.targetPeers[uuid]; ok { 94 b.err = moerr.NewInternalErrorNoCtx("cannot add peer %+v to %s: already have peer %+v on %s", peer, uuid, old, uuid) 95 return b 96 } 97 for oldUuid, old := range b.targetPeers { 98 if old == peer { 99 b.err = moerr.NewInternalErrorNoCtx("cannot add peer %+v to %s: already have peer %+v on %s", peer, uuid, old, oldUuid) 100 return b 101 } 102 } 103 104 b.targetPeers.Set(uuid, peer) 105 return b 106 } 107 108 // RemovePeer records a remove peer operation in Builder. 109 func (b *Builder) RemovePeer(uuid string) *Builder { 110 if b.err != nil { 111 return b 112 } 113 if _, ok := b.targetPeers[uuid]; !ok { 114 b.err = moerr.NewInternalErrorNoCtx("cannot remove peer from %s: not found", uuid) 115 } else { 116 delete(b.targetPeers, uuid) 117 } 118 return b 119 } 120 121 // Build creates the Operator. 122 func (b *Builder) Build() (*Operator, error) { 123 if b.err != nil { 124 return nil, b.err 125 } 126 127 brief := b.prepareBuild() 128 129 if b.err = b.buildSteps(); b.err != nil { 130 return nil, b.err 131 } 132 133 return NewOperator(brief, b.shardID, b.epoch, b.steps...), nil 134 } 135 136 func (b *Builder) prepareBuild() string { 137 b.toAdd = newPeersMap() 138 b.toRemove = newPeersMap() 139 140 for uuid, replicaID := range b.originPeers { 141 // new peer not exists 142 if _, ok := b.targetPeers[uuid]; !ok { 143 b.toRemove.Set(uuid, replicaID) 144 } 145 } 146 147 for uuid, replicaID := range b.targetPeers { 148 // old peer not exists. 149 if _, ok := b.originPeers[uuid]; !ok { 150 b.toAdd.Set(uuid, replicaID) 151 } 152 } 153 154 return b.brief() 155 } 156 157 func (b *Builder) buildSteps() error { 158 for len(b.toRemove) > 0 { 159 var targets []string 160 for target := range b.targetPeers { 161 targets = append(targets, target) 162 } 163 sort.Slice(targets, func(i, j int) bool { return targets[i] < targets[j] }) 164 165 uuid, replicaID := b.toRemove.Get() 166 b.steps = append(b.steps, RemoveLogService{ 167 Target: targets[0], 168 Replica: Replica{uuid, b.shardID, replicaID, b.epoch}, 169 }) 170 delete(b.toRemove, uuid) 171 continue 172 } 173 174 for len(b.toAdd) > 0 { 175 var targets []string 176 for target := range b.originPeers { 177 targets = append(targets, target) 178 } 179 sort.Slice(targets, func(i, j int) bool { return targets[i] < targets[j] }) 180 181 uuid, replicaID := b.toAdd.Get() 182 b.steps = append(b.steps, AddLogService{ 183 Target: targets[0], 184 Replica: Replica{ 185 UUID: uuid, 186 ShardID: b.shardID, 187 ReplicaID: replicaID, 188 Epoch: b.epoch, 189 }, 190 }) 191 delete(b.toAdd, uuid) 192 } 193 194 if len(b.steps) == 0 { 195 return moerr.NewInternalErrorNoCtx("no operator step is built") 196 } 197 return nil 198 } 199 200 // generate brief description of the operator. 201 func (b *Builder) brief() string { 202 switch { 203 case len(b.toAdd) > 0: 204 return fmt.Sprintf("add peer: store %s", b.toAdd) 205 case len(b.toRemove) > 0: 206 return fmt.Sprintf("rm peer: store %s", b.toRemove) 207 default: 208 return "" 209 } 210 } 211 212 // Replicas indexed by store's uuid. 213 type peersMap map[string]uint64 214 215 func newPeersMap() peersMap { 216 return make(map[string]uint64) 217 } 218 219 func (pm peersMap) Get() (string, uint64) { 220 for uuid, replicaID := range pm { 221 return uuid, replicaID 222 } 223 return "", 0 224 } 225 226 func (pm peersMap) Set(uuid string, replicaID uint64) { 227 pm[uuid] = replicaID 228 } 229 230 func (pm peersMap) String() string { 231 uuids := make([]string, 0, len(pm)) 232 for uuid := range pm { 233 uuids = append(uuids, uuid) 234 } 235 return fmt.Sprintf("%v", uuids) 236 } 237 238 func (pm peersMap) Copy() peersMap { 239 var pm2 peersMap = make(map[string]uint64, len(pm)) 240 for uuid, replicaID := range pm { 241 pm2.Set(uuid, replicaID) 242 } 243 return pm2 244 }