github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/pkg/client/internal/leader_resolver.go (about) 1 // Copyright 2022 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 package internal 15 16 import ( 17 "fmt" 18 "math/rand" 19 "reflect" 20 "sort" 21 "strings" 22 "sync" 23 24 "github.com/pingcap/log" 25 "github.com/pingcap/tiflow/engine/pkg/client/internal/endpoint" 26 "github.com/pingcap/tiflow/pkg/errors" 27 "go.uber.org/zap" 28 "google.golang.org/grpc/resolver" 29 "google.golang.org/grpc/resolver/manual" 30 "google.golang.org/grpc/serviceconfig" 31 ) 32 33 const ( 34 schemaName = "tiflow" 35 ) 36 37 // MasterServerList stores a map from server addresses to whether they are the leader. 38 type MasterServerList = map[string]bool 39 40 // FollowerSorterFn is a function used to customize the order of fail-over 41 // in case the servermaster's leader is unreachable. 42 type FollowerSorterFn = func(address []resolver.Address) 43 44 // LeaderResolver implements a gRPC resolver that handles the 45 // follower logic for servermaster clients. 46 type LeaderResolver struct { 47 *manual.Resolver 48 serviceConfig *serviceconfig.ParseResult 49 50 serverListMu sync.RWMutex 51 serverList MasterServerList 52 updateNotifyCh chan struct{} 53 54 doneCh chan struct{} 55 wg sync.WaitGroup 56 57 FollowerSorter FollowerSorterFn 58 } 59 60 // NewLeaderResolver returns a new LeaderResolver. 61 func NewLeaderResolver( 62 serverList MasterServerList, 63 ) *LeaderResolver { 64 return newLeaderResolverWithFollowerSorter(serverList, randomizedFollowerSorter) 65 } 66 67 func newLeaderResolverWithFollowerSorter( 68 serverList MasterServerList, 69 followerSorter FollowerSorterFn, 70 ) *LeaderResolver { 71 ret := &LeaderResolver{ 72 Resolver: manual.NewBuilderWithScheme(schemaName), 73 serverList: serverList, 74 updateNotifyCh: make(chan struct{}, 1), 75 doneCh: make(chan struct{}), 76 FollowerSorter: followerSorter, 77 } 78 ret.wg.Add(1) 79 go func() { 80 defer ret.wg.Done() 81 ret.bgUpdateServerList() 82 }() 83 84 return ret 85 } 86 87 func (b *LeaderResolver) bgUpdateServerList() { 88 for { 89 select { 90 case <-b.doneCh: 91 return 92 case <-b.updateNotifyCh: 93 } 94 95 b.Resolver.UpdateState(b.getResolverState()) 96 } 97 } 98 99 func (b *LeaderResolver) getResolverState() resolver.State { 100 b.serverListMu.RLock() 101 defer b.serverListMu.RUnlock() 102 103 if b.serverList == nil { 104 panic("getResolverState must be called after UpdateServerList") 105 } 106 107 var ( 108 leaderAddr *resolver.Address 109 addrList []resolver.Address // without leader 110 ) 111 112 for addr, isLeader := range b.serverList { 113 if isLeader { 114 if leaderAddr != nil { 115 panic(fmt.Sprintf("Duplicate leaders: %s and %s", leaderAddr.Addr, addr)) 116 } 117 leaderAddr = makeAddress(addr) 118 continue 119 } 120 121 // Not leader 122 addrList = append(addrList, *makeAddress(addr)) 123 } 124 125 // Sorts the list of the followers to provide a fail-over order. 126 b.FollowerSorter(addrList) 127 128 if leaderAddr != nil { 129 addrList = append([]resolver.Address{*leaderAddr}, addrList...) 130 } 131 132 return resolver.State{ 133 Addresses: addrList, 134 ServiceConfig: b.serviceConfig, 135 } 136 } 137 138 // Close closes the LeaderResolver. 139 // It implements resolver.Resolver. 140 func (b *LeaderResolver) Close() { 141 b.Resolver.Close() 142 close(b.doneCh) 143 b.wg.Wait() 144 } 145 146 // Build implements resolver.Builder. 147 // resolver.Builder is theoretically an abstract factory, 148 // but since we are using a one-one relationship between 149 // Builder and Resolver, we implement both interfaces on the 150 // same struct. 151 func (b *LeaderResolver) Build( 152 target resolver.Target, 153 cc resolver.ClientConn, 154 opts resolver.BuildOptions, 155 ) (resolver.Resolver, error) { 156 b.serviceConfig = cc.ParseServiceConfig(`{"loadBalancingPolicy": "pick_first"}`) 157 if b.serviceConfig.Err != nil { 158 return nil, b.serviceConfig.Err 159 } 160 161 builtResolver, err := b.Resolver.Build(target, cc, opts) 162 if err != nil { 163 return nil, errors.Trace(err) 164 } 165 166 // Must update state once before returning, 167 // so that the ClientConn knows where to connect. 168 b.Resolver.UpdateState(b.getResolverState()) 169 return builtResolver, nil 170 } 171 172 // UpdateServerList should be called by engine's service discovery mechanism 173 // to update the serverList in a timely manner. 174 func (b *LeaderResolver) UpdateServerList(serverList MasterServerList) { 175 updated := false 176 177 b.serverListMu.Lock() 178 if !reflect.DeepEqual(b.serverList, serverList) { 179 b.serverList = serverList 180 updated = true 181 } 182 b.serverListMu.Unlock() 183 184 if !updated { 185 return 186 } 187 188 select { 189 case b.updateNotifyCh <- struct{}{}: 190 log.L().Info("leader resolver state updated", 191 zap.Any("server-list", b.serverList)) 192 default: 193 } 194 } 195 196 // randomizedFollowerSorter randomizes the order of the addresses, 197 // to avoid a deterministic fail-over order. 198 // Time complexity: O(n). 199 func randomizedFollowerSorter(address []resolver.Address) { 200 rand.Shuffle(len(address), func(i, j int) { 201 address[i], address[j] = address[j], address[i] 202 }) 203 } 204 205 // orderedFollowerSorter sorts the addresses in lexicographical order. 206 // Used only for unit-testing for now. 207 // Time complexity: O(n log(n)). 208 func orderedFollowerSorter(address []resolver.Address) { 209 sort.Slice(address, func(i, j int) bool { 210 // Lexicographical comparison. 211 // Note: strings.Compare(x, y) returns -1 iff x is considered less than y. 212 return strings.Compare(address[i].Addr, address[j].Addr) < 0 213 }) 214 } 215 216 func makeAddress(ep string) *resolver.Address { 217 addr, name := endpoint.Interpret(ep) 218 return &resolver.Address{Addr: addr, ServerName: name} 219 }