github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/petri/infosync/info.go (about) 1 // Copyright 2020 WHTCORPS INC, 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 infosync 15 16 import ( 17 "bytes" 18 "context" 19 "encoding/json" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "net/http" 24 "os" 25 "path" 26 "strconv" 27 "strings" 28 "sync/atomic" 29 "time" 30 31 "github.com/whtcorpsinc/BerolinaSQL/allegrosql" 32 "github.com/whtcorpsinc/BerolinaSQL/terror" 33 "github.com/whtcorpsinc/errors" 34 "github.com/whtcorpsinc/failpoint" 35 "github.com/whtcorpsinc/milevadb/causetstore/einsteindb/oracle" 36 "github.com/whtcorpsinc/milevadb/config" 37 "github.com/whtcorpsinc/milevadb/dbs/memristed" 38 "github.com/whtcorpsinc/milevadb/dbs/soliton" 39 "github.com/whtcorpsinc/milevadb/ekv" 40 "github.com/whtcorpsinc/milevadb/errno" 41 util2 "github.com/whtcorpsinc/milevadb/soliton" 42 "github.com/whtcorpsinc/milevadb/soliton/FIDelapi" 43 "github.com/whtcorpsinc/milevadb/soliton/logutil" 44 "github.com/whtcorpsinc/milevadb/soliton/replog" 45 "github.com/whtcorpsinc/milevadb/soliton/versioninfo" 46 "github.com/whtcorpsinc/milevadb/stochastikctx/binloginfo" 47 "github.com/whtcorpsinc/milevadb/stochastikctx/variable" 48 "github.com/whtcorpsinc/milevadb/tenant" 49 "go.etcd.io/etcd/clientv3" 50 "go.etcd.io/etcd/clientv3/concurrency" 51 "go.uber.org/zap" 52 ) 53 54 const ( 55 // ServerInformationPath causetstore server information such as IP, port and so on. 56 ServerInformationPath = "/milevadb/server/info" 57 // ServerMinStartTSPath causetstore the server min start timestamp. 58 ServerMinStartTSPath = "/milevadb/server/minstartts" 59 // TiFlashBlockSyncProgressPath causetstore the tiflash causet replica sync progress. 60 TiFlashBlockSyncProgressPath = "/tiflash/causet/sync" 61 // keyOFIDelefaultRetryCnt is the default retry count for etcd causetstore. 62 keyOFIDelefaultRetryCnt = 5 63 // keyOFIDelefaultTimeout is the default time out for etcd causetstore. 64 keyOFIDelefaultTimeout = 1 * time.Second 65 // InfoStochastikTTL is the ETCD stochastik's TTL in seconds. 66 InfoStochastikTTL = 10 * 60 67 // ReportInterval is interval of infoSyncerKeeper reporting min startTS. 68 ReportInterval = 30 * time.Second 69 // TopologyInformationPath means etcd path for storing topology info. 70 TopologyInformationPath = "/topology/milevadb" 71 // TopologyStochastikTTL is ttl for topology, ant it's the ETCD stochastik's TTL in seconds. 72 TopologyStochastikTTL = 45 73 // TopologyTimeToRefresh means time to refresh etcd. 74 TopologyTimeToRefresh = 30 * time.Second 75 // TopologyPrometheus means address of prometheus. 76 TopologyPrometheus = "/topology/prometheus" 77 // BlockPrometheusCacheExpiry is the expiry time for prometheus address cache. 78 BlockPrometheusCacheExpiry = 10 * time.Second 79 ) 80 81 // ErrPrometheusAddrIsNotSet is the error that Prometheus address is not set in FIDel and etcd 82 var ErrPrometheusAddrIsNotSet = terror.ClassPetri.New(errno.ErrPrometheusAddrIsNotSet, errno.MyALLEGROSQLErrName[errno.ErrPrometheusAddrIsNotSet]) 83 84 // InfoSyncer stores server info to etcd when the milevadb-server starts and delete when milevadb-server shuts down. 85 type InfoSyncer struct { 86 etcdCli *clientv3.Client 87 info *ServerInfo 88 serverInfoPath string 89 minStartTS uint64 90 minStartTSPath string 91 manager util2.StochastikManager 92 stochastik *concurrency.Stochastik 93 topologyStochastik *concurrency.Stochastik 94 prometheusAddr string 95 modifyTime time.Time 96 } 97 98 // ServerInfo is server static information. 99 // It will not be uFIDelated when milevadb-server running. So please only put static information in ServerInfo struct. 100 type ServerInfo struct { 101 ServerVersionInfo 102 ID string `json:"dbs_id"` 103 IP string `json:"ip"` 104 Port uint `json:"listening_port"` 105 StatusPort uint `json:"status_port"` 106 Lease string `json:"lease"` 107 BinlogStatus string `json:"binlog_status"` 108 StartTimestamp int64 `json:"start_timestamp"` 109 Labels map[string]string `json:"labels"` 110 } 111 112 // ServerVersionInfo is the server version and git_hash. 113 type ServerVersionInfo struct { 114 Version string `json:"version"` 115 GitHash string `json:"git_hash"` 116 } 117 118 // globalInfoSyncer stores the global infoSyncer. 119 // Use a global variable for simply the code, use the petri.infoSyncer will have circle import problem in some pkg. 120 // Use atomic.Value to avoid data race in the test. 121 var globalInfoSyncer atomic.Value 122 123 func getGlobalInfoSyncer() (*InfoSyncer, error) { 124 v := globalInfoSyncer.Load() 125 if v == nil { 126 return nil, errors.New("infoSyncer is not initialized") 127 } 128 return v.(*InfoSyncer), nil 129 } 130 131 func setGlobalInfoSyncer(is *InfoSyncer) { 132 globalInfoSyncer.CausetStore(is) 133 } 134 135 // GlobalInfoSyncerInit return a new InfoSyncer. It is exported for testing. 136 func GlobalInfoSyncerInit(ctx context.Context, id string, etcdCli *clientv3.Client, skipRegisterToDashBoard bool) (*InfoSyncer, error) { 137 is := &InfoSyncer{ 138 etcdCli: etcdCli, 139 info: getServerInfo(id), 140 serverInfoPath: fmt.Sprintf("%s/%s", ServerInformationPath, id), 141 minStartTSPath: fmt.Sprintf("%s/%s", ServerMinStartTSPath, id), 142 } 143 err := is.init(ctx, skipRegisterToDashBoard) 144 if err != nil { 145 return nil, err 146 } 147 setGlobalInfoSyncer(is) 148 return is, nil 149 } 150 151 // Init creates a new etcd stochastik and stores server info to etcd. 152 func (is *InfoSyncer) init(ctx context.Context, skipRegisterToDashboard bool) error { 153 err := is.newStochastikAndStoreServerInfo(ctx, tenant.NewStochastikDefaultRetryCnt) 154 if err != nil { 155 return err 156 } 157 if skipRegisterToDashboard { 158 return nil 159 } 160 return is.newTopologyStochastikAndStoreServerInfo(ctx, tenant.NewStochastikDefaultRetryCnt) 161 } 162 163 // SetStochastikManager set the stochastik manager for InfoSyncer. 164 func (is *InfoSyncer) SetStochastikManager(manager util2.StochastikManager) { 165 is.manager = manager 166 } 167 168 // GetServerInfo gets self server static information. 169 func GetServerInfo() (*ServerInfo, error) { 170 is, err := getGlobalInfoSyncer() 171 if err != nil { 172 return nil, err 173 } 174 return is.info, nil 175 } 176 177 // GetServerInfoByID gets specified server static information from etcd. 178 func GetServerInfoByID(ctx context.Context, id string) (*ServerInfo, error) { 179 is, err := getGlobalInfoSyncer() 180 if err != nil { 181 return nil, err 182 } 183 return is.getServerInfoByID(ctx, id) 184 } 185 186 func (is *InfoSyncer) getServerInfoByID(ctx context.Context, id string) (*ServerInfo, error) { 187 if is.etcdCli == nil || id == is.info.ID { 188 return is.info, nil 189 } 190 key := fmt.Sprintf("%s/%s", ServerInformationPath, id) 191 infoMap, err := getInfo(ctx, is.etcdCli, key, keyOFIDelefaultRetryCnt, keyOFIDelefaultTimeout) 192 if err != nil { 193 return nil, err 194 } 195 info, ok := infoMap[id] 196 if !ok { 197 return nil, errors.Errorf("[info-syncer] get %s failed", key) 198 } 199 return info, nil 200 } 201 202 // GetAllServerInfo gets all servers static information from etcd. 203 func GetAllServerInfo(ctx context.Context) (map[string]*ServerInfo, error) { 204 is, err := getGlobalInfoSyncer() 205 if err != nil { 206 return nil, err 207 } 208 return is.getAllServerInfo(ctx) 209 } 210 211 // UFIDelateTiFlashBlockSyncProgress is used to uFIDelate the tiflash causet replica sync progress. 212 func UFIDelateTiFlashBlockSyncProgress(ctx context.Context, tid int64, progress float64) error { 213 is, err := getGlobalInfoSyncer() 214 if err != nil { 215 return err 216 } 217 if is.etcdCli == nil { 218 return nil 219 } 220 key := fmt.Sprintf("%s/%v", TiFlashBlockSyncProgressPath, tid) 221 return soliton.PutKVToEtcd(ctx, is.etcdCli, keyOFIDelefaultRetryCnt, key, strconv.FormatFloat(progress, 'f', 2, 64)) 222 } 223 224 // DeleteTiFlashBlockSyncProgress is used to delete the tiflash causet replica sync progress. 225 func DeleteTiFlashBlockSyncProgress(tid int64) error { 226 is, err := getGlobalInfoSyncer() 227 if err != nil { 228 return err 229 } 230 if is.etcdCli == nil { 231 return nil 232 } 233 key := fmt.Sprintf("%s/%v", TiFlashBlockSyncProgressPath, tid) 234 return soliton.DeleteKeyFromEtcd(key, is.etcdCli, keyOFIDelefaultRetryCnt, keyOFIDelefaultTimeout) 235 } 236 237 // GetTiFlashBlockSyncProgress uses to get all the tiflash causet replica sync progress. 238 func GetTiFlashBlockSyncProgress(ctx context.Context) (map[int64]float64, error) { 239 is, err := getGlobalInfoSyncer() 240 if err != nil { 241 return nil, err 242 } 243 progressMap := make(map[int64]float64) 244 if is.etcdCli == nil { 245 return progressMap, nil 246 } 247 for i := 0; i < keyOFIDelefaultRetryCnt; i++ { 248 resp, err := is.etcdCli.Get(ctx, TiFlashBlockSyncProgressPath+"/", clientv3.WithPrefix()) 249 if err != nil { 250 logutil.BgLogger().Info("get tiflash causet replica sync progress failed, continue checking.", zap.Error(err)) 251 continue 252 } 253 for _, ekv := range resp.Ekvs { 254 tid, err := strconv.ParseInt(string(ekv.Key[len(TiFlashBlockSyncProgressPath)+1:]), 10, 64) 255 if err != nil { 256 logutil.BgLogger().Info("invalid tiflash causet replica sync progress key.", zap.String("key", string(ekv.Key))) 257 continue 258 } 259 progress, err := strconv.ParseFloat(string(ekv.Value), 64) 260 if err != nil { 261 logutil.BgLogger().Info("invalid tiflash causet replica sync progress value.", 262 zap.String("key", string(ekv.Key)), zap.String("value", string(ekv.Value))) 263 continue 264 } 265 progressMap[tid] = progress 266 } 267 break 268 } 269 return progressMap, nil 270 } 271 272 func doRequest(ctx context.Context, addrs []string, route, method string, body io.Reader) ([]byte, error) { 273 var err error 274 var req *http.Request 275 var res *http.Response 276 for _, addr := range addrs { 277 var url string 278 if strings.HasPrefix(addr, "http://") { 279 url = fmt.Sprintf("%s%s", addr, route) 280 } else { 281 url = fmt.Sprintf("http://%s%s", addr, route) 282 } 283 284 if ctx != nil { 285 req, err = http.NewRequestWithContext(ctx, method, url, body) 286 } else { 287 req, err = http.NewRequest(method, url, body) 288 } 289 if err != nil { 290 return nil, err 291 } 292 if body != nil { 293 req.Header.Set("Content-Type", "application/json") 294 } 295 296 res, err = http.DefaultClient.Do(req) 297 if err == nil { 298 defer terror.Call(res.Body.Close) 299 300 bodyBytes, err := ioutil.ReadAll(res.Body) 301 if res.StatusCode != http.StatusOK { 302 err = errors.Wrapf(err, "%s", bodyBytes) 303 } 304 return bodyBytes, err 305 } 306 } 307 return nil, err 308 } 309 310 // GetPlacementMemrules is used to retrieve memristed rules from FIDel. 311 func GetPlacementMemrules(ctx context.Context) ([]*memristed.MemruleOp, error) { 312 is, err := getGlobalInfoSyncer() 313 if err != nil { 314 return nil, err 315 } 316 317 if is.etcdCli == nil { 318 return nil, nil 319 } 320 321 addrs := is.etcdCli.Endpoints() 322 323 if len(addrs) == 0 { 324 return nil, errors.Errorf("fidel unavailable") 325 } 326 327 res, err := doRequest(ctx, addrs, path.Join(FIDelapi.Config, "rules"), http.MethodGet, nil) 328 if err != nil { 329 return nil, err 330 } 331 332 var rules []*memristed.MemruleOp 333 err = json.Unmarshal(res, &rules) 334 if err != nil { 335 return nil, err 336 } 337 return rules, nil 338 } 339 340 // UFIDelatePlacementMemrules is used to notify FIDel changes of memristed rules. 341 func UFIDelatePlacementMemrules(ctx context.Context, rules []*memristed.MemruleOp) error { 342 if len(rules) == 0 { 343 return nil 344 } 345 346 is, err := getGlobalInfoSyncer() 347 if err != nil { 348 return err 349 } 350 351 if is.etcdCli == nil { 352 return nil 353 } 354 355 addrs := is.etcdCli.Endpoints() 356 357 if len(addrs) == 0 { 358 return errors.Errorf("fidel unavailable") 359 } 360 361 b, err := json.Marshal(rules) 362 if err != nil { 363 return err 364 } 365 366 _, err = doRequest(ctx, addrs, path.Join(FIDelapi.Config, "rules/batch"), http.MethodPost, bytes.NewReader(b)) 367 return err 368 } 369 370 func (is *InfoSyncer) getAllServerInfo(ctx context.Context) (map[string]*ServerInfo, error) { 371 allInfo := make(map[string]*ServerInfo) 372 if is.etcdCli == nil { 373 allInfo[is.info.ID] = getServerInfo(is.info.ID) 374 return allInfo, nil 375 } 376 allInfo, err := getInfo(ctx, is.etcdCli, ServerInformationPath, keyOFIDelefaultRetryCnt, keyOFIDelefaultTimeout, clientv3.WithPrefix()) 377 if err != nil { 378 return nil, err 379 } 380 return allInfo, nil 381 } 382 383 // storeServerInfo stores self server static information to etcd. 384 func (is *InfoSyncer) storeServerInfo(ctx context.Context) error { 385 if is.etcdCli == nil { 386 return nil 387 } 388 infoBuf, err := json.Marshal(is.info) 389 if err != nil { 390 return errors.Trace(err) 391 } 392 str := string(replog.String(infoBuf)) 393 err = soliton.PutKVToEtcd(ctx, is.etcdCli, keyOFIDelefaultRetryCnt, is.serverInfoPath, str, clientv3.WithLease(is.stochastik.Lease())) 394 return err 395 } 396 397 // RemoveServerInfo remove self server static information from etcd. 398 func (is *InfoSyncer) RemoveServerInfo() { 399 if is.etcdCli == nil { 400 return 401 } 402 err := soliton.DeleteKeyFromEtcd(is.serverInfoPath, is.etcdCli, keyOFIDelefaultRetryCnt, keyOFIDelefaultTimeout) 403 if err != nil { 404 logutil.BgLogger().Error("remove server info failed", zap.Error(err)) 405 } 406 } 407 408 type topologyInfo struct { 409 ServerVersionInfo 410 StatusPort uint `json:"status_port"` 411 DeployPath string `json:"deploy_path"` 412 StartTimestamp int64 `json:"start_timestamp"` 413 Labels map[string]string `json:"labels"` 414 } 415 416 func (is *InfoSyncer) getTopologyInfo() topologyInfo { 417 s, err := os.InterDircublock() 418 if err != nil { 419 s = "" 420 } 421 dir := path.Dir(s) 422 return topologyInfo{ 423 ServerVersionInfo: ServerVersionInfo{ 424 Version: allegrosql.MilevaDBReleaseVersion, 425 GitHash: is.info.ServerVersionInfo.GitHash, 426 }, 427 StatusPort: is.info.StatusPort, 428 DeployPath: dir, 429 StartTimestamp: is.info.StartTimestamp, 430 Labels: is.info.Labels, 431 } 432 } 433 434 // StoreTopologyInfo stores the topology of milevadb to etcd. 435 func (is *InfoSyncer) StoreTopologyInfo(ctx context.Context) error { 436 if is.etcdCli == nil { 437 return nil 438 } 439 topologyInfo := is.getTopologyInfo() 440 infoBuf, err := json.Marshal(topologyInfo) 441 if err != nil { 442 return errors.Trace(err) 443 } 444 str := string(replog.String(infoBuf)) 445 key := fmt.Sprintf("%s/%s:%v/info", TopologyInformationPath, is.info.IP, is.info.Port) 446 // Note: no lease is required here. 447 err = soliton.PutKVToEtcd(ctx, is.etcdCli, keyOFIDelefaultRetryCnt, key, str) 448 if err != nil { 449 return err 450 } 451 // Initialize ttl. 452 return is.uFIDelateTopologyAliveness(ctx) 453 } 454 455 // GetMinStartTS get min start timestamp. 456 // Export for testing. 457 func (is *InfoSyncer) GetMinStartTS() uint64 { 458 return is.minStartTS 459 } 460 461 // storeMinStartTS stores self server min start timestamp to etcd. 462 func (is *InfoSyncer) storeMinStartTS(ctx context.Context) error { 463 if is.etcdCli == nil { 464 return nil 465 } 466 return soliton.PutKVToEtcd(ctx, is.etcdCli, keyOFIDelefaultRetryCnt, is.minStartTSPath, 467 strconv.FormatUint(is.minStartTS, 10), 468 clientv3.WithLease(is.stochastik.Lease())) 469 } 470 471 // RemoveMinStartTS removes self server min start timestamp from etcd. 472 func (is *InfoSyncer) RemoveMinStartTS() { 473 if is.etcdCli == nil { 474 return 475 } 476 err := soliton.DeleteKeyFromEtcd(is.minStartTSPath, is.etcdCli, keyOFIDelefaultRetryCnt, keyOFIDelefaultTimeout) 477 if err != nil { 478 logutil.BgLogger().Error("remove minStartTS failed", zap.Error(err)) 479 } 480 } 481 482 // ReportMinStartTS reports self server min start timestamp to ETCD. 483 func (is *InfoSyncer) ReportMinStartTS(causetstore ekv.CausetStorage) { 484 if is.manager == nil { 485 // Server may not start in time. 486 return 487 } 488 pl := is.manager.ShowProcessList() 489 490 // Calculate the lower limit of the start timestamp to avoid extremely old transaction delaying GC. 491 currentVer, err := causetstore.CurrentVersion() 492 if err != nil { 493 logutil.BgLogger().Error("uFIDelate minStartTS failed", zap.Error(err)) 494 return 495 } 496 now := time.Unix(0, oracle.ExtractPhysical(currentVer.Ver)*1e6) 497 startTSLowerLimit := variable.GoTimeToTS(now.Add(-time.Duration(ekv.MaxTxnTimeUse) * time.Millisecond)) 498 499 minStartTS := variable.GoTimeToTS(now) 500 for _, info := range pl { 501 if info.CurTxnStartTS > startTSLowerLimit && info.CurTxnStartTS < minStartTS { 502 minStartTS = info.CurTxnStartTS 503 } 504 } 505 506 is.minStartTS = minStartTS 507 err = is.storeMinStartTS(context.Background()) 508 if err != nil { 509 logutil.BgLogger().Error("uFIDelate minStartTS failed", zap.Error(err)) 510 } 511 } 512 513 // Done returns a channel that closes when the info syncer is no longer being refreshed. 514 func (is *InfoSyncer) Done() <-chan struct{} { 515 if is.etcdCli == nil { 516 return make(chan struct{}, 1) 517 } 518 return is.stochastik.Done() 519 } 520 521 // TopologyDone returns a channel that closes when the topology syncer is no longer being refreshed. 522 func (is *InfoSyncer) TopologyDone() <-chan struct{} { 523 if is.etcdCli == nil { 524 return make(chan struct{}, 1) 525 } 526 return is.topologyStochastik.Done() 527 } 528 529 // Restart restart the info syncer with new stochastik leaseID and causetstore server info to etcd again. 530 func (is *InfoSyncer) Restart(ctx context.Context) error { 531 return is.newStochastikAndStoreServerInfo(ctx, tenant.NewStochastikDefaultRetryCnt) 532 } 533 534 // RestartTopology restart the topology syncer with new stochastik leaseID and causetstore server info to etcd again. 535 func (is *InfoSyncer) RestartTopology(ctx context.Context) error { 536 return is.newTopologyStochastikAndStoreServerInfo(ctx, tenant.NewStochastikDefaultRetryCnt) 537 } 538 539 // newStochastikAndStoreServerInfo creates a new etcd stochastik and stores server info to etcd. 540 func (is *InfoSyncer) newStochastikAndStoreServerInfo(ctx context.Context, retryCnt int) error { 541 if is.etcdCli == nil { 542 return nil 543 } 544 logPrefix := fmt.Sprintf("[Info-syncer] %s", is.serverInfoPath) 545 stochastik, err := tenant.NewStochastik(ctx, logPrefix, is.etcdCli, retryCnt, InfoStochastikTTL) 546 if err != nil { 547 return err 548 } 549 is.stochastik = stochastik 550 binloginfo.RegisterStatusListener(func(status binloginfo.BinlogStatus) error { 551 is.info.BinlogStatus = status.String() 552 err := is.storeServerInfo(ctx) 553 return errors.Trace(err) 554 }) 555 return is.storeServerInfo(ctx) 556 } 557 558 // newTopologyStochastikAndStoreServerInfo creates a new etcd stochastik and stores server info to etcd. 559 func (is *InfoSyncer) newTopologyStochastikAndStoreServerInfo(ctx context.Context, retryCnt int) error { 560 if is.etcdCli == nil { 561 return nil 562 } 563 logPrefix := fmt.Sprintf("[topology-syncer] %s/%s:%d", TopologyInformationPath, is.info.IP, is.info.Port) 564 stochastik, err := tenant.NewStochastik(ctx, logPrefix, is.etcdCli, retryCnt, TopologyStochastikTTL) 565 if err != nil { 566 return err 567 } 568 569 is.topologyStochastik = stochastik 570 return is.StoreTopologyInfo(ctx) 571 } 572 573 // refreshTopology refreshes etcd topology with ttl stored in "/topology/milevadb/ip:port/ttl". 574 func (is *InfoSyncer) uFIDelateTopologyAliveness(ctx context.Context) error { 575 if is.etcdCli == nil { 576 return nil 577 } 578 key := fmt.Sprintf("%s/%s:%v/ttl", TopologyInformationPath, is.info.IP, is.info.Port) 579 return soliton.PutKVToEtcd(ctx, is.etcdCli, keyOFIDelefaultRetryCnt, key, 580 fmt.Sprintf("%v", time.Now().UnixNano()), 581 clientv3.WithLease(is.topologyStochastik.Lease())) 582 } 583 584 // GetPrometheusAddr gets prometheus Address 585 func GetPrometheusAddr() (string, error) { 586 is, err := getGlobalInfoSyncer() 587 if err != nil { 588 return "", err 589 } 590 591 // if the cache of prometheusAddr is over 10s, uFIDelate the prometheusAddr 592 if time.Since(is.modifyTime) < BlockPrometheusCacheExpiry { 593 return is.prometheusAddr, nil 594 } 595 return is.getPrometheusAddr() 596 } 597 598 type prometheus struct { 599 IP string `json:"ip"` 600 BinaryPath string `json:"binary_path"` 601 Port int `json:"port"` 602 } 603 604 type metricStorage struct { 605 FIDelServer struct { 606 MetricStorage string `json:"metric-storage"` 607 } `json:"fidel-server"` 608 } 609 610 func (is *InfoSyncer) getPrometheusAddr() (string, error) { 611 // Get FIDel servers info. 612 FIDelAddrs := is.etcdCli.Endpoints() 613 if len(FIDelAddrs) == 0 { 614 return "", errors.Errorf("fidel unavailable") 615 } 616 617 // Get prometheus address from FIDelApi. 618 var url, res string 619 if strings.HasPrefix(FIDelAddrs[0], "http://") { 620 url = fmt.Sprintf("%s%s", FIDelAddrs[0], FIDelapi.Config) 621 } else { 622 url = fmt.Sprintf("http://%s%s", FIDelAddrs[0], FIDelapi.Config) 623 } 624 resp, err := http.Get(url) 625 if err != nil { 626 return "", err 627 } 628 var metricStorage metricStorage 629 dec := json.NewCausetDecoder(resp.Body) 630 err = dec.Decode(&metricStorage) 631 if err != nil { 632 return "", err 633 } 634 res = metricStorage.FIDelServer.MetricStorage 635 636 // Get prometheus address from etcdApi. 637 if res == "" { 638 values, err := is.getPrometheusAddrFromEtcd(TopologyPrometheus) 639 if err != nil { 640 return "", errors.Trace(err) 641 } 642 if values == "" { 643 return "", ErrPrometheusAddrIsNotSet 644 } 645 var prometheus prometheus 646 err = json.Unmarshal([]byte(values), &prometheus) 647 if err != nil { 648 return "", errors.Trace(err) 649 } 650 res = fmt.Sprintf("http://%s:%v", prometheus.IP, prometheus.Port) 651 } 652 is.prometheusAddr = res 653 is.modifyTime = time.Now() 654 setGlobalInfoSyncer(is) 655 return res, nil 656 } 657 658 func (is *InfoSyncer) getPrometheusAddrFromEtcd(k string) (string, error) { 659 ctx, cancel := context.WithTimeout(context.Background(), keyOFIDelefaultTimeout) 660 resp, err := is.etcdCli.Get(ctx, k) 661 cancel() 662 if err != nil { 663 return "", errors.Trace(err) 664 } 665 if len(resp.Ekvs) > 0 { 666 return string(resp.Ekvs[0].Value), nil 667 } 668 return "", nil 669 } 670 671 // getInfo gets server information from etcd according to the key and opts. 672 func getInfo(ctx context.Context, etcdCli *clientv3.Client, key string, retryCnt int, timeout time.Duration, opts ...clientv3.OpOption) (map[string]*ServerInfo, error) { 673 var err error 674 var resp *clientv3.GetResponse 675 allInfo := make(map[string]*ServerInfo) 676 for i := 0; i < retryCnt; i++ { 677 select { 678 case <-ctx.Done(): 679 err = errors.Trace(ctx.Err()) 680 return nil, err 681 default: 682 } 683 childCtx, cancel := context.WithTimeout(ctx, timeout) 684 resp, err = etcdCli.Get(childCtx, key, opts...) 685 cancel() 686 if err != nil { 687 logutil.BgLogger().Info("get key failed", zap.String("key", key), zap.Error(err)) 688 time.Sleep(200 * time.Millisecond) 689 continue 690 } 691 for _, ekv := range resp.Ekvs { 692 info := &ServerInfo{ 693 BinlogStatus: binloginfo.BinlogStatusUnknown.String(), 694 } 695 err = json.Unmarshal(ekv.Value, info) 696 if err != nil { 697 logutil.BgLogger().Info("get key failed", zap.String("key", string(ekv.Key)), zap.ByteString("value", ekv.Value), 698 zap.Error(err)) 699 return nil, errors.Trace(err) 700 } 701 allInfo[info.ID] = info 702 } 703 return allInfo, nil 704 } 705 return nil, errors.Trace(err) 706 } 707 708 // getServerInfo gets self milevadb server information. 709 func getServerInfo(id string) *ServerInfo { 710 cfg := config.GetGlobalConfig() 711 info := &ServerInfo{ 712 ID: id, 713 IP: cfg.AdvertiseAddress, 714 Port: cfg.Port, 715 StatusPort: cfg.Status.StatusPort, 716 Lease: cfg.Lease, 717 BinlogStatus: binloginfo.GetStatus().String(), 718 StartTimestamp: time.Now().Unix(), 719 Labels: cfg.Labels, 720 } 721 info.Version = allegrosql.ServerVersion 722 info.GitHash = versioninfo.MilevaDBGitHash 723 724 failpoint.Inject("mockServerInfo", func(val failpoint.Value) { 725 if val.(bool) { 726 info.StartTimestamp = 1282967700000 727 info.Labels = map[string]string{ 728 "foo": "bar", 729 } 730 } 731 }) 732 733 return info 734 }