github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/lorry/engines/wesql/manager.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package wesql 21 22 import ( 23 "context" 24 "database/sql" 25 "fmt" 26 "strings" 27 28 "github.com/pkg/errors" 29 ctrl "sigs.k8s.io/controller-runtime" 30 31 "github.com/1aal/kubeblocks/pkg/lorry/dcs" 32 "github.com/1aal/kubeblocks/pkg/lorry/engines" 33 "github.com/1aal/kubeblocks/pkg/lorry/engines/mysql" 34 ) 35 36 const ( 37 Role = "ROLE" 38 CurrentRole = "CURRENT_ROLE" 39 Leader = "Leader" 40 ) 41 42 type Manager struct { 43 mysql.Manager 44 } 45 46 var _ engines.DBManager = &Manager{} 47 48 func NewManager(properties engines.Properties) (engines.DBManager, error) { 49 logger := ctrl.Log.WithName("WeSQL") 50 _, err := NewConfig(properties) 51 if err != nil { 52 return nil, err 53 } 54 55 mysqlMgr, err := mysql.NewManager(properties) 56 if err != nil { 57 return nil, err 58 } 59 60 mgr := &Manager{ 61 Manager: *mysqlMgr.(*mysql.Manager), 62 } 63 64 mgr.SetLogger(logger) 65 return mgr, nil 66 } 67 68 func (mgr *Manager) InitializeCluster(ctx context.Context, cluster *dcs.Cluster) error { 69 return nil 70 } 71 72 func (mgr *Manager) IsLeader(ctx context.Context, cluster *dcs.Cluster) (bool, error) { 73 role, err := mgr.GetReplicaRole(ctx, cluster) 74 75 if err != nil { 76 return false, err 77 } 78 79 if strings.EqualFold(role, Leader) { 80 return true, nil 81 } 82 83 return false, nil 84 } 85 86 func (mgr *Manager) IsLeaderMember(ctx context.Context, cluster *dcs.Cluster, member *dcs.Member) (bool, error) { 87 if member == nil { 88 return false, nil 89 } 90 91 leaderMember := mgr.GetLeaderMember(ctx, cluster) 92 if leaderMember == nil { 93 return false, nil 94 } 95 96 if leaderMember.Name != member.Name { 97 return false, nil 98 } 99 100 return true, nil 101 } 102 103 func (mgr *Manager) InitiateCluster(cluster *dcs.Cluster) error { 104 return nil 105 } 106 107 func (mgr *Manager) GetMemberAddrs(ctx context.Context, cluster *dcs.Cluster) []string { 108 addrs := make([]string, 0, 3) 109 clusterInfo := mgr.GetClusterInfo(ctx, cluster) 110 clusterInfo = strings.Split(clusterInfo, "@")[0] 111 for _, addr := range strings.Split(clusterInfo, ";") { 112 if !strings.Contains(addr, ":") { 113 continue 114 } 115 addrs = append(addrs, strings.Split(addr, "#")[0]) 116 } 117 118 return addrs 119 } 120 121 func (mgr *Manager) GetAddrWithMemberName(ctx context.Context, cluster *dcs.Cluster, memberName string) string { 122 addrs := mgr.GetMemberAddrs(ctx, cluster) 123 for _, addr := range addrs { 124 if strings.HasPrefix(addr, memberName) { 125 return addr 126 } 127 } 128 return "" 129 } 130 131 func (mgr *Manager) IsCurrentMemberInCluster(ctx context.Context, cluster *dcs.Cluster) bool { 132 clusterInfo := mgr.GetClusterInfo(ctx, cluster) 133 return strings.Contains(clusterInfo, mgr.CurrentMemberName) 134 } 135 136 func (mgr *Manager) IsMemberLagging(context.Context, *dcs.Cluster, *dcs.Member) (bool, int64) { 137 return false, 0 138 } 139 140 func (mgr *Manager) Recover(context.Context) error { 141 return nil 142 } 143 144 func (mgr *Manager) JoinCurrentMemberToCluster(context.Context, *dcs.Cluster) error { 145 return nil 146 } 147 148 func (mgr *Manager) LeaveMemberFromCluster(ctx context.Context, cluster *dcs.Cluster, memberName string) error { 149 db, err := mgr.GetLeaderConn(ctx, cluster) 150 if err != nil { 151 mgr.Logger.Error(err, "Get leader conn failed") 152 return err 153 } 154 addr := mgr.GetAddrWithMemberName(ctx, cluster, memberName) 155 if addr == "" { 156 mgr.Logger.Info(fmt.Sprintf("member %s already deleted", memberName)) 157 return nil 158 } 159 160 sql := fmt.Sprintf("call dbms_consensus.downgrade_follower('%s');"+ 161 "call dbms_consensus.drop_learner('%s');", addr, addr) 162 _, err = db.ExecContext(ctx, sql) 163 if err != nil { 164 mgr.Logger.Error(err, "delete member from db cluster failed") 165 return errors.Wrapf(err, "error executing %s", sql) 166 } 167 return nil 168 } 169 170 func (mgr *Manager) IsClusterHealthy(ctx context.Context, cluster *dcs.Cluster) bool { 171 db, err := mgr.GetLeaderConn(ctx, cluster) 172 if err != nil { 173 mgr.Logger.Error(err, "Get leader conn failed") 174 return false 175 } 176 if db == nil { 177 return false 178 } 179 180 defer db.Close() 181 var leaderRecord mysql.RowMap 182 sql := "select * from information_schema.wesql_cluster_global;" 183 err = mysql.QueryRowsMap(db, sql, func(rMap mysql.RowMap) error { 184 if rMap.GetString(Role) == Leader { 185 leaderRecord = rMap 186 } 187 return nil 188 }) 189 if err != nil { 190 mgr.Logger.Error(err, fmt.Sprintf("error executing %s", sql)) 191 return false 192 } 193 194 if len(leaderRecord) > 0 { 195 return true 196 } 197 return false 198 } 199 200 // IsClusterInitialized is a method to check if cluster is initailized or not 201 func (mgr *Manager) IsClusterInitialized(ctx context.Context, cluster *dcs.Cluster) (bool, error) { 202 clusterInfo := mgr.GetClusterInfo(ctx, nil) 203 if clusterInfo != "" { 204 return true, nil 205 } 206 207 return false, nil 208 } 209 210 func (mgr *Manager) GetClusterInfo(ctx context.Context, cluster *dcs.Cluster) string { 211 var db *sql.DB 212 var err error 213 if cluster != nil { 214 db, err = mgr.GetLeaderConn(ctx, cluster) 215 if err != nil { 216 mgr.Logger.Error(err, "Get leader conn failed") 217 return "" 218 } 219 if db != nil { 220 defer db.Close() 221 } 222 } else { 223 db = mgr.DB 224 225 } 226 var clusterID, clusterInfo string 227 err = db.QueryRowContext(ctx, "select cluster_id, cluster_info from mysql.consensus_info"). 228 Scan(&clusterID, &clusterInfo) 229 if err != nil { 230 mgr.Logger.Error(err, "Cluster info query failed") 231 } 232 return clusterInfo 233 } 234 235 func (mgr *Manager) Promote(ctx context.Context, cluster *dcs.Cluster) error { 236 isLeader, _ := mgr.IsLeader(ctx, nil) 237 if isLeader { 238 return nil 239 } 240 241 db, err := mgr.GetLeaderConn(ctx, cluster) 242 if err != nil { 243 return errors.Wrap(err, "Get leader conn failed") 244 } 245 if db != nil { 246 defer db.Close() 247 } 248 249 currentMember := cluster.GetMemberWithName(mgr.GetCurrentMemberName()) 250 addr := cluster.GetMemberAddr(*currentMember) 251 resp, err := db.Exec(fmt.Sprintf("call dbms_consensus.change_leader('%s:13306');", addr)) 252 if err != nil { 253 mgr.Logger.Error(err, "promote err") 254 return err 255 } 256 257 mgr.Logger.Info("promote success", "resp", resp) 258 return nil 259 } 260 261 func (mgr *Manager) IsPromoted(ctx context.Context) bool { 262 isLeader, _ := mgr.IsLeader(ctx, nil) 263 return isLeader 264 } 265 266 func (mgr *Manager) Demote(context.Context) error { 267 return nil 268 } 269 270 func (mgr *Manager) Follow(ctx context.Context, cluster *dcs.Cluster) error { 271 return nil 272 } 273 274 func (mgr *Manager) GetHealthiestMember(cluster *dcs.Cluster, candidate string) *dcs.Member { 275 return nil 276 } 277 278 func (mgr *Manager) HasOtherHealthyLeader(ctx context.Context, cluster *dcs.Cluster) *dcs.Member { 279 clusterLocalInfo, err := mgr.GetClusterLocalInfo(ctx) 280 if err != nil || clusterLocalInfo == nil { 281 mgr.Logger.Error(err, "Get cluster local info failed") 282 return nil 283 } 284 285 if clusterLocalInfo.GetString(Role) == Leader { 286 // I am the leader, just return nil 287 return nil 288 } 289 290 leaderAddr := clusterLocalInfo.GetString(CurrentRole) 291 if leaderAddr == "" { 292 return nil 293 } 294 leaderParts := strings.Split(leaderAddr, ".") 295 if len(leaderParts) > 0 { 296 return cluster.GetMemberWithName(leaderParts[0]) 297 } 298 299 return nil 300 } 301 302 // HasOtherHealthyMembers checks if there are any healthy members, excluding the leader 303 func (mgr *Manager) HasOtherHealthyMembers(ctx context.Context, cluster *dcs.Cluster, leader string) []*dcs.Member { 304 members := make([]*dcs.Member, 0) 305 for _, member := range cluster.Members { 306 if member.Name == leader { 307 continue 308 } 309 if !mgr.IsMemberHealthy(ctx, cluster, &member) { 310 continue 311 } 312 members = append(members, &member) 313 } 314 315 return members 316 }