github.com/polarismesh/polaris@v1.17.8/store/mysql/client.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 package sqldb 19 20 import ( 21 "database/sql" 22 "errors" 23 "fmt" 24 "strings" 25 "time" 26 27 apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage" 28 29 "github.com/polarismesh/polaris/common/model" 30 "github.com/polarismesh/polaris/store" 31 ) 32 33 type clientStore struct { 34 master *BaseDB 35 slave *BaseDB // 缓存相关的读取,请求到slave 36 } 37 38 // CreateClient insert the client info 39 func (cs *clientStore) CreateClient(client *model.Client) error { 40 clientID := client.Proto().GetId().GetValue() 41 if len(clientID) == 0 { 42 log.Errorf("[Store][database] add business missing id") 43 return fmt.Errorf("add Business missing some params, id %s, name %s", clientID, 44 client.Proto().GetHost().GetValue()) 45 } 46 err := RetryTransaction("createClient", func() error { 47 return cs.createClient(client) 48 }) 49 return store.Error(err) 50 } 51 52 // UpdateClient update the client info 53 func (cs *clientStore) UpdateClient(client *model.Client) error { 54 err := RetryTransaction("updateClient", func() error { 55 return cs.updateClient(client) 56 }) 57 if err == nil { 58 return nil 59 } 60 61 serr := store.Error(err) 62 if store.Code(serr) == store.DuplicateEntryErr { 63 serr = store.NewStatusError(store.DataConflictErr, err.Error()) 64 } 65 return serr 66 } 67 68 // deleteClient delete the client info 69 func deleteClient(tx *BaseTx, clientID string) error { 70 if clientID == "" { 71 return errors.New("delete client missing client id") 72 } 73 74 str := "update client set flag = 1, mtime = sysdate() where `id` = ?" 75 _, err := tx.Exec(str, clientID) 76 return store.Error(err) 77 } 78 79 // BatchAddClients 增加多个实例 80 func (cs *clientStore) BatchAddClients(clients []*model.Client) error { 81 err := RetryTransaction("batchAddClients", func() error { 82 return cs.batchAddClients(clients) 83 }) 84 if err == nil { 85 return nil 86 } 87 return store.Error(err) 88 } 89 90 // BatchDeleteClients 批量删除实例,flag=1 91 func (cs *clientStore) BatchDeleteClients(ids []string) error { 92 err := RetryTransaction("batchDeleteClients", func() error { 93 return cs.batchDeleteClients(ids) 94 }) 95 if err == nil { 96 return nil 97 } 98 return store.Error(err) 99 } 100 101 // GetMoreClients 根据mtime获取增量clients,返回所有store的变更信息 102 func (cs *clientStore) GetMoreClients(mtime time.Time, firstUpdate bool) (map[string]*model.Client, error) { 103 str := `select client.id, client.host, client.type, IFNULL(client.version,""), IFNULL(client.region, ""), 104 IFNULL(client.zone, ""), IFNULL(client.campus, ""), client.flag, IFNULL(client_stat.target, ""), 105 IFNULL(client_stat.port, 0), IFNULL(client_stat.protocol, ""), IFNULL(client_stat.path, ""), 106 UNIX_TIMESTAMP(client.ctime), UNIX_TIMESTAMP(client.mtime) 107 from client left join client_stat on client.id = client_stat.client_id ` 108 str += " where client.mtime >= FROM_UNIXTIME(?)" 109 if firstUpdate { 110 str += " and flag != 1" 111 } 112 rows, err := cs.slave.Query(str, timeToTimestamp(mtime)) 113 if err != nil { 114 log.Errorf("[Store][database] get more client query err: %s", err.Error()) 115 return nil, err 116 } 117 118 out := make(map[string]*model.Client) 119 err = callFetchClientRows(rows, func(entry *model.ClientStore) (b bool, e error) { 120 outClient, ok := out[entry.ID] 121 if !ok { 122 out[entry.ID] = model.Store2Client(entry) 123 } else { 124 statInfo := model.Store2ClientStat(&entry.Stat) 125 outClient.Proto().Stat = append(outClient.Proto().Stat, statInfo) 126 } 127 return true, nil 128 }) 129 if err != nil { 130 log.Errorf("[Store][database] call fetch client rows err: %s", err.Error()) 131 return nil, err 132 } 133 134 return out, nil 135 } 136 137 func (cs *clientStore) batchAddClients(clients []*model.Client) error { 138 tx, err := cs.master.Begin() 139 if err != nil { 140 log.Errorf("[Store][database] batch add clients tx begin err: %s", err.Error()) 141 return err 142 } 143 defer func() { _ = tx.Rollback() }() 144 145 ids := make([]string, 0, len(clients)) 146 var client2StatInfos = make(map[string][]*apiservice.StatInfo) 147 builder := strings.Builder{} 148 for idx, entry := range clients { 149 if idx > 0 { 150 builder.WriteString(",") 151 } 152 builder.WriteString("?") 153 ids = append(ids, entry.Proto().GetId().GetValue()) 154 var statInfos []*apiservice.StatInfo 155 if len(entry.Proto().GetStat()) > 0 { 156 statInfos = append(statInfos, entry.Proto().GetStat()...) 157 client2StatInfos[entry.Proto().GetId().GetValue()] = statInfos 158 } 159 } 160 if err = batchCleanClientStats(tx, ids); nil != err { 161 log.Errorf("[Store][database] batch clean client stat err: %s", err.Error()) 162 return err 163 } 164 if err = batchAddClientMain(tx, clients); nil != err { 165 log.Errorf("[Store][database] batch add clients err: %s", err.Error()) 166 return err 167 } 168 if err = batchAddClientStat(tx, client2StatInfos); nil != err { 169 log.Errorf("[Store][database] batch add clientStats err: %s", err.Error()) 170 return err 171 } 172 if err := tx.Commit(); err != nil { 173 log.Errorf("[Store][database] batch add clients commit tx err: %s", err.Error()) 174 return err 175 } 176 return nil 177 } 178 179 func (cs *clientStore) batchDeleteClients(ids []string) error { 180 tx, err := cs.master.Begin() 181 if err != nil { 182 log.Errorf("[Store][database] batch delete clients tx begin err: %s", err.Error()) 183 return err 184 } 185 defer func() { _ = tx.Rollback() }() 186 187 if err = batchCleanClientStats(tx, ids); nil != err { 188 log.Errorf("[Store][database] batch clean client stat err: %s", err.Error()) 189 return err 190 } 191 if err = batchDeleteClientsMain(tx, ids); nil != err { 192 log.Errorf("[Store][database] batch delete clients err: %s", err.Error()) 193 return err 194 } 195 if err := tx.Commit(); err != nil { 196 log.Errorf("[Store][database] batch delete clients commit tx err: %s", err.Error()) 197 return err 198 } 199 return nil 200 } 201 202 func batchDeleteClientsMain(tx *BaseTx, ids []string) error { 203 args := make([]interface{}, 0, len(ids)) 204 for i := range ids { 205 args = append(args, ids[i]) 206 } 207 208 return BatchOperation("batch-delete-clients", args, func(objects []interface{}) error { 209 if len(objects) == 0 { 210 return nil 211 } 212 str := `update client set flag = 1, mtime = sysdate() where id in ( ` + PlaceholdersN(len(objects)) + `)` 213 _, err := tx.Exec(str, objects...) 214 return store.Error(err) 215 }) 216 } 217 218 func batchCleanClientStats(tx *BaseTx, ids []string) error { 219 args := make([]interface{}, 0, len(ids)) 220 for i := range ids { 221 args = append(args, ids[i]) 222 } 223 224 return BatchOperation("batch-delete-client-stats", args, func(objects []interface{}) error { 225 if len(objects) == 0 { 226 return nil 227 } 228 str := `delete from client_stat where client_id in (` + PlaceholdersN(len(objects)) + `)` 229 _, err := tx.Exec(str, objects...) 230 return store.Error(err) 231 }) 232 } 233 234 func (cs *clientStore) GetClientStat(clientID string) ([]*model.ClientStatStore, error) { 235 str := "select `target`, `port`, `protocol`, `path` from client_stat where client.id = ?" 236 rows, err := cs.master.Query(str, clientID) 237 if err != nil { 238 log.Errorf("[Store][database] query client stat err: %s", err.Error()) 239 return nil, err 240 } 241 defer rows.Close() 242 243 var clientStatStores []*model.ClientStatStore 244 for rows.Next() { 245 clientStatStore := &model.ClientStatStore{} 246 if err := rows.Scan(&clientStatStore.Target, 247 &clientStatStore.Port, &clientStatStore.Protocol, &clientStatStore.Path); err != nil { 248 log.Errorf("[Store][database] get client meta rows scan err: %s", err.Error()) 249 return nil, err 250 } 251 clientStatStores = append(clientStatStores, clientStatStore) 252 } 253 if err := rows.Err(); err != nil { 254 log.Errorf("[Store][database] get client meta rows next err: %s", err.Error()) 255 return nil, err 256 } 257 258 return clientStatStores, nil 259 } 260 261 // callFetchClientRows 带回调的fetch client 262 func callFetchClientRows(rows *sql.Rows, callback func(entry *model.ClientStore) (bool, error)) error { 263 if rows == nil { 264 return nil 265 } 266 defer rows.Close() 267 var item model.ClientStore 268 progress := 0 269 for rows.Next() { 270 progress++ 271 if progress%100000 == 0 { 272 log.Infof("[Store][database] client fetch rows progress: %d", progress) 273 } 274 err := rows.Scan(&item.ID, &item.Host, &item.Type, &item.Version, &item.Region, &item.Zone, 275 &item.Campus, &item.Flag, &item.Stat.Target, &item.Stat.Port, &item.Stat.Protocol, 276 &item.Stat.Path, &item.CreateTime, &item.ModifyTime) 277 if err != nil { 278 log.Errorf("[Store][database] fetch client rows err: %s", err.Error()) 279 return err 280 } 281 ok, err := callback(&item) 282 if err != nil { 283 return err 284 } 285 if !ok { 286 return nil 287 } 288 } 289 if err := rows.Err(); err != nil { 290 log.Errorf("[Store][database] client rows catch err: %s", err.Error()) 291 return err 292 } 293 294 return nil 295 } 296 297 func (cs *clientStore) createClient(client *model.Client) error { 298 tx, err := cs.master.Begin() 299 if err != nil { 300 log.Errorf("[Store][database] create client tx begin err: %s", err.Error()) 301 return err 302 } 303 defer func() { _ = tx.Rollback() }() 304 // clean the old items before add 305 if err := deleteClient(tx, client.Proto().GetId().GetValue()); err != nil { 306 return err 307 } 308 if err := addClientMain(tx, client); err != nil { 309 log.Errorf("[Store][database] add client main err: %s", err.Error()) 310 return err 311 } 312 if err := addClientStat(tx, client); err != nil { 313 log.Errorf("[Store][database] add client stat err: %s", err.Error()) 314 return err 315 } 316 if err := tx.Commit(); err != nil { 317 log.Errorf("[Store][database] create client commit tx err: %s", err.Error()) 318 return err 319 } 320 return nil 321 } 322 323 func (cs *clientStore) updateClient(client *model.Client) error { 324 tx, err := cs.master.Begin() 325 if err != nil { 326 log.Errorf("[Store][database] update client tx begin err: %s", err.Error()) 327 return err 328 } 329 defer func() { _ = tx.Rollback() }() 330 331 if err := updateClientMain(tx, client); err != nil { 332 log.Errorf("[Store][database] update client main err: %s", err.Error()) 333 return err 334 } 335 336 if err := updateClientStat(tx, client); err != nil { 337 log.Errorf("[Store][database] update client stat err: %s", err.Error()) 338 return err 339 } 340 if err := tx.Commit(); err != nil { 341 log.Errorf("[Store][database] update client commit tx err: %s", err.Error()) 342 return err 343 } 344 return nil 345 } 346 347 func addClientMain(tx *BaseTx, client *model.Client) error { 348 str := `insert into client(id, host, type, version, region, zone, campus, flag, ctime, mtime) 349 values(?, ?, ?, ?, ?, ?, ?, 0, sysdate(), sysdate())` 350 _, err := tx.Exec(str, 351 client.Proto().GetId().GetValue(), 352 client.Proto().GetHost().GetValue(), 353 client.Proto().GetType().String(), 354 client.Proto().GetVersion().GetValue(), 355 client.Proto().GetLocation().GetRegion().GetValue(), 356 client.Proto().GetLocation().GetZone().GetValue(), 357 client.Proto().GetLocation().GetCampus().GetValue(), 358 ) 359 return err 360 } 361 362 func batchAddClientMain(tx *BaseTx, clients []*model.Client) error { 363 str := `replace into client(id, host, type, version, region, zone, campus, flag, ctime, mtime) 364 values` 365 first := true 366 args := make([]interface{}, 0) 367 for _, client := range clients { 368 if !first { 369 str += "," 370 } 371 str += "(?, ?, ?, ?, ?, ?, ?, 0, sysdate(), sysdate())" 372 first = false 373 374 args = append(args, client.Proto().GetId().GetValue(), 375 client.Proto().GetHost().GetValue(), 376 client.Proto().GetType().String()) 377 args = append(args, client.Proto().GetVersion().GetValue(), 378 client.Proto().GetLocation().GetRegion().GetValue(), 379 client.Proto().GetLocation().GetZone().GetValue(), 380 client.Proto().GetLocation().GetCampus().GetValue()) 381 } 382 _, err := tx.Exec(str, args...) 383 return err 384 } 385 386 func batchAddClientStat(tx *BaseTx, client2Stats map[string][]*apiservice.StatInfo) error { 387 if len(client2Stats) == 0 { 388 return nil 389 } 390 str := `insert into client_stat(client_id, target, port, protocol, path) 391 values` 392 first := true 393 args := make([]interface{}, 0) 394 for clientId, stats := range client2Stats { 395 for _, entry := range stats { 396 if !first { 397 str += "," 398 } 399 str += "(?, ?, ?, ?, ?)" 400 first = false 401 args = append(args, 402 clientId, 403 entry.GetTarget().GetValue(), 404 entry.GetPort().GetValue(), 405 entry.GetProtocol().GetValue(), 406 entry.GetPath().GetValue()) 407 } 408 } 409 _, err := tx.Exec(str, args...) 410 return err 411 } 412 413 func addClientStat(tx *BaseTx, client *model.Client) error { 414 stats := client.Proto().GetStat() 415 if len(stats) == 0 { 416 return nil 417 } 418 str := `insert into client_stat(client_id, target, port, protocol, path) 419 values` 420 first := true 421 args := make([]interface{}, 0) 422 for _, entry := range stats { 423 if !first { 424 str += "," 425 } 426 str += "(?, ?, ?, ?, ?)" 427 first = false 428 args = append(args, 429 client.Proto().GetId().GetValue(), 430 entry.GetTarget().GetValue(), 431 entry.GetPort().GetValue(), 432 entry.GetProtocol().GetValue(), 433 entry.GetPath().GetValue()) 434 } 435 _, err := tx.Exec(str, args...) 436 return err 437 } 438 439 func updateClientMain(tx *BaseTx, client *model.Client) error { 440 str := `update client set host = ?, 441 type = ?, version = ?, region = ?, zone = ?, campus = ?, mtime = sysdate() where id = ?` 442 443 _, err := tx.Exec(str, 444 client.Proto().GetHost().GetValue(), 445 client.Proto().GetType().String(), 446 client.Proto().GetVersion().GetValue(), 447 client.Proto().GetLocation().GetRegion().GetValue(), 448 client.Proto().GetLocation().GetZone().GetValue(), 449 client.Proto().GetLocation().GetCampus().GetValue(), 450 client.Proto().GetId().GetValue(), 451 ) 452 453 return err 454 } 455 456 // updateClientStat 更新client的stat表 457 func updateClientStat(tx *BaseTx, client *model.Client) error { 458 deleteStr := "delete from client_stat where cliend_id = ?" 459 if _, err := tx.Exec(deleteStr, client.Proto().GetId().GetValue()); err != nil { 460 return err 461 } 462 return addClientStat(tx, client) 463 }