github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/helper/helper.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 helper 15 16 import ( 17 "bytes" 18 "context" 19 "encoding/hex" 20 "encoding/json" 21 "fmt" 22 "io" 23 "math" 24 "net/http" 25 "net/url" 26 "sort" 27 "strconv" 28 "strings" 29 "time" 30 31 log "github.com/sirupsen/logrus" 32 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 33 "github.com/whtcorpsinc/ekvproto/pkg/ekvrpcpb" 34 "github.com/whtcorpsinc/errors" 35 "github.com/whtcorpsinc/milevadb/blockcodec" 36 "github.com/whtcorpsinc/milevadb/causetstore/einsteindb" 37 "github.com/whtcorpsinc/milevadb/causetstore/einsteindb/einsteindbrpc" 38 "github.com/whtcorpsinc/milevadb/ekv" 39 "github.com/whtcorpsinc/milevadb/soliton" 40 "github.com/whtcorpsinc/milevadb/soliton/FIDelapi" 41 "github.com/whtcorpsinc/milevadb/soliton/codec" 42 "github.com/whtcorpsinc/milevadb/soliton/logutil" 43 "go.uber.org/zap" 44 ) 45 46 // Helper is a midbseware to get some information from einsteindb/fidel. It can be used for MilevaDB's http api or mem causet. 47 type Helper struct { 48 CausetStore einsteindb.CausetStorage 49 RegionCache *einsteindb.RegionCache 50 } 51 52 // NewHelper get a Helper from CausetStorage 53 func NewHelper(causetstore einsteindb.CausetStorage) *Helper { 54 return &Helper{ 55 CausetStore: causetstore, 56 RegionCache: causetstore.GetRegionCache(), 57 } 58 } 59 60 // GetMvccByEncodedKey get the MVCC value by the specific encoded key. 61 func (h *Helper) GetMvccByEncodedKey(encodedKey ekv.Key) (*ekvrpcpb.MvccGetByKeyResponse, error) { 62 keyLocation, err := h.RegionCache.LocateKey(einsteindb.NewBackofferWithVars(context.Background(), 500, nil), encodedKey) 63 if err != nil { 64 return nil, errors.Trace(err) 65 } 66 67 einsteindbReq := einsteindbrpc.NewRequest(einsteindbrpc.CmdMvccGetByKey, &ekvrpcpb.MvccGetByKeyRequest{Key: encodedKey}) 68 ekvResp, err := h.CausetStore.SendReq(einsteindb.NewBackofferWithVars(context.Background(), 500, nil), einsteindbReq, keyLocation.Region, time.Minute) 69 if err != nil { 70 logutil.BgLogger().Info("get MVCC by encoded key failed", 71 zap.Stringer("encodeKey", encodedKey), 72 zap.Reflect("region", keyLocation.Region), 73 zap.Stringer("startKey", keyLocation.StartKey), 74 zap.Stringer("endKey", keyLocation.EndKey), 75 zap.Reflect("ekvResp", ekvResp), 76 zap.Error(err)) 77 return nil, errors.Trace(err) 78 } 79 return ekvResp.Resp.(*ekvrpcpb.MvccGetByKeyResponse), nil 80 } 81 82 // StoreHotRegionInfos records all hog region stores. 83 // it's the response of FIDel. 84 type StoreHotRegionInfos struct { 85 AsPeer map[uint64]*HotRegionsStat `json:"as_peer"` 86 AsLeader map[uint64]*HotRegionsStat `json:"as_leader"` 87 } 88 89 // HotRegionsStat records echo causetstore's hot region. 90 // it's the response of FIDel. 91 type HotRegionsStat struct { 92 RegionsStat []RegionStat `json:"statistics"` 93 } 94 95 // RegionStat records each hot region's statistics 96 // it's the response of FIDel. 97 type RegionStat struct { 98 RegionID uint64 `json:"region_id"` 99 FlowBytes float64 `json:"flow_bytes"` 100 HotDegree int `json:"hot_degree"` 101 } 102 103 // RegionMetric presents the final metric output entry. 104 type RegionMetric struct { 105 FlowBytes uint64 `json:"flow_bytes"` 106 MaxHotDegree int `json:"max_hot_degree"` 107 Count int `json:"region_count"` 108 } 109 110 // ScrapeHotInfo gets the needed hot region information by the url given. 111 func (h *Helper) ScrapeHotInfo(rw string, allSchemas []*perceptron.DBInfo) ([]HotBlockIndex, error) { 112 regionMetrics, err := h.FetchHotRegion(rw) 113 if err != nil { 114 return nil, err 115 } 116 return h.FetchRegionBlockIndex(regionMetrics, allSchemas) 117 } 118 119 // FetchHotRegion fetches the hot region information from FIDel's http api. 120 func (h *Helper) FetchHotRegion(rw string) (map[uint64]RegionMetric, error) { 121 etcd, ok := h.CausetStore.(einsteindb.EtcdBackend) 122 if !ok { 123 return nil, errors.WithStack(errors.New("not implemented")) 124 } 125 FIDelHosts, err := etcd.EtcdAddrs() 126 if err != nil { 127 return nil, err 128 } 129 if len(FIDelHosts) == 0 { 130 return nil, errors.New("fidel unavailable") 131 } 132 req, err := http.NewRequest("GET", soliton.InternalHTTPSchema()+"://"+FIDelHosts[0]+rw, nil) 133 if err != nil { 134 return nil, errors.Trace(err) 135 } 136 resp, err := soliton.InternalHTTPClient().Do(req) 137 if err != nil { 138 return nil, errors.Trace(err) 139 } 140 defer func() { 141 err = resp.Body.Close() 142 if err != nil { 143 logutil.BgLogger().Error("close body failed", zap.Error(err)) 144 } 145 }() 146 var regionResp StoreHotRegionInfos 147 err = json.NewCausetDecoder(resp.Body).Decode(®ionResp) 148 if err != nil { 149 return nil, errors.Trace(err) 150 } 151 metricCnt := 0 152 for _, hotRegions := range regionResp.AsLeader { 153 metricCnt += len(hotRegions.RegionsStat) 154 } 155 metric := make(map[uint64]RegionMetric, metricCnt) 156 for _, hotRegions := range regionResp.AsLeader { 157 for _, region := range hotRegions.RegionsStat { 158 metric[region.RegionID] = RegionMetric{FlowBytes: uint64(region.FlowBytes), MaxHotDegree: region.HotDegree} 159 } 160 } 161 return metric, nil 162 } 163 164 // TblIndex stores the things to index one causet. 165 type TblIndex struct { 166 DbName string 167 BlockName string 168 BlockID int64 169 IndexName string 170 IndexID int64 171 } 172 173 // FrameItem includes a index's or record's spacetime data with causet's info. 174 type FrameItem struct { 175 DBName string `json:"db_name"` 176 BlockName string `json:"block_name"` 177 BlockID int64 `json:"block_id"` 178 IsRecord bool `json:"is_record"` 179 RecordID int64 `json:"record_id,omitempty"` 180 IndexName string `json:"index_name,omitempty"` 181 IndexID int64 `json:"index_id,omitempty"` 182 IndexValues []string `json:"index_values,omitempty"` 183 } 184 185 // RegionFrameRange contains a frame range info which the region covered. 186 type RegionFrameRange struct { 187 First *FrameItem // start frame of the region 188 Last *FrameItem // end frame of the region 189 region *einsteindb.KeyLocation // the region 190 } 191 192 // HotBlockIndex contains region and its causet/index info. 193 type HotBlockIndex struct { 194 RegionID uint64 `json:"region_id"` 195 RegionMetric *RegionMetric `json:"region_metric"` 196 DbName string `json:"db_name"` 197 BlockName string `json:"block_name"` 198 BlockID int64 `json:"block_id"` 199 IndexName string `json:"index_name"` 200 IndexID int64 `json:"index_id"` 201 } 202 203 // FetchRegionBlockIndex constructs a map that maps a causet to its hot region information by the given raw hot RegionMetric metrics. 204 func (h *Helper) FetchRegionBlockIndex(metrics map[uint64]RegionMetric, allSchemas []*perceptron.DBInfo) ([]HotBlockIndex, error) { 205 hotBlocks := make([]HotBlockIndex, 0, len(metrics)) 206 for regionID, regionMetric := range metrics { 207 t := HotBlockIndex{RegionID: regionID, RegionMetric: ®ionMetric} 208 region, err := h.RegionCache.LocateRegionByID(einsteindb.NewBackofferWithVars(context.Background(), 500, nil), regionID) 209 if err != nil { 210 logutil.BgLogger().Error("locate region failed", zap.Error(err)) 211 continue 212 } 213 214 hotRange, err := NewRegionFrameRange(region) 215 if err != nil { 216 return nil, err 217 } 218 f := h.FindBlockIndexOfRegion(allSchemas, hotRange) 219 if f != nil { 220 t.DbName = f.DBName 221 t.BlockName = f.BlockName 222 t.BlockID = f.BlockID 223 t.IndexName = f.IndexName 224 t.IndexID = f.IndexID 225 } 226 hotBlocks = append(hotBlocks, t) 227 } 228 229 return hotBlocks, nil 230 } 231 232 // FindBlockIndexOfRegion finds what causet is involved in this hot region. And constructs the new frame item for future use. 233 func (h *Helper) FindBlockIndexOfRegion(allSchemas []*perceptron.DBInfo, hotRange *RegionFrameRange) *FrameItem { 234 for _, EDB := range allSchemas { 235 for _, tbl := range EDB.Blocks { 236 if f := findRangeInBlock(hotRange, EDB, tbl); f != nil { 237 return f 238 } 239 } 240 } 241 return nil 242 } 243 244 func findRangeInBlock(hotRange *RegionFrameRange, EDB *perceptron.DBInfo, tbl *perceptron.BlockInfo) *FrameItem { 245 pi := tbl.GetPartitionInfo() 246 if pi == nil { 247 return findRangeInPhysicalBlock(hotRange, tbl.ID, EDB.Name.O, tbl.Name.O, tbl.Indices, tbl.IsCommonHandle) 248 } 249 250 for _, def := range pi.Definitions { 251 blockPartition := fmt.Sprintf("%s(%s)", tbl.Name.O, def.Name) 252 if f := findRangeInPhysicalBlock(hotRange, def.ID, EDB.Name.O, blockPartition, tbl.Indices, tbl.IsCommonHandle); f != nil { 253 return f 254 } 255 } 256 return nil 257 } 258 259 func findRangeInPhysicalBlock(hotRange *RegionFrameRange, physicalID int64, dbName, tblName string, indices []*perceptron.IndexInfo, isCommonHandle bool) *FrameItem { 260 if f := hotRange.GetRecordFrame(physicalID, dbName, tblName, isCommonHandle); f != nil { 261 return f 262 } 263 for _, idx := range indices { 264 if f := hotRange.GetIndexFrame(physicalID, idx.ID, dbName, tblName, idx.Name.O); f != nil { 265 return f 266 } 267 } 268 return nil 269 } 270 271 // NewRegionFrameRange init a NewRegionFrameRange with region info. 272 func NewRegionFrameRange(region *einsteindb.KeyLocation) (idxRange *RegionFrameRange, err error) { 273 var first, last *FrameItem 274 // check and init first frame 275 if len(region.StartKey) > 0 { 276 first, err = NewFrameItemFromRegionKey(region.StartKey) 277 if err != nil { 278 return 279 } 280 } else { // empty startKey means start with -infinite 281 first = &FrameItem{ 282 IndexID: int64(math.MinInt64), 283 IsRecord: false, 284 BlockID: int64(math.MinInt64), 285 } 286 } 287 288 // check and init last frame 289 if len(region.EndKey) > 0 { 290 last, err = NewFrameItemFromRegionKey(region.EndKey) 291 if err != nil { 292 return 293 } 294 } else { // empty endKey means end with +infinite 295 last = &FrameItem{ 296 BlockID: int64(math.MaxInt64), 297 IndexID: int64(math.MaxInt64), 298 IsRecord: true, 299 } 300 } 301 302 idxRange = &RegionFrameRange{ 303 region: region, 304 First: first, 305 Last: last, 306 } 307 return idxRange, nil 308 } 309 310 // NewFrameItemFromRegionKey creates a FrameItem with region's startKey or endKey, 311 // returns err when key is illegal. 312 func NewFrameItemFromRegionKey(key []byte) (frame *FrameItem, err error) { 313 frame = &FrameItem{} 314 frame.BlockID, frame.IndexID, frame.IsRecord, err = blockcodec.DecodeKeyHead(key) 315 if err == nil { 316 if frame.IsRecord { 317 var handle ekv.Handle 318 _, handle, err = blockcodec.DecodeRecordKey(key) 319 if err == nil { 320 if handle.IsInt() { 321 frame.RecordID = handle.IntValue() 322 } else { 323 data, err := handle.Data() 324 if err != nil { 325 return nil, err 326 } 327 frame.IndexName = "PRIMARY" 328 frame.IndexValues = make([]string, 0, len(data)) 329 for _, causet := range data { 330 str, err := causet.ToString() 331 if err != nil { 332 return nil, err 333 } 334 frame.IndexValues = append(frame.IndexValues, str) 335 } 336 } 337 } 338 } else { 339 _, _, frame.IndexValues, err = blockcodec.DecodeIndexKey(key) 340 } 341 logutil.BgLogger().Warn("decode region key failed", zap.ByteString("key", key), zap.Error(err)) 342 // Ignore decode errors. 343 err = nil 344 return 345 } 346 if bytes.HasPrefix(key, blockcodec.BlockPrefix()) { 347 // If SplitBlock is enabled, the key may be `t{id}`. 348 if len(key) == blockcodec.BlockSplitKeyLen { 349 frame.BlockID = blockcodec.DecodeBlockID(key) 350 return frame, nil 351 } 352 return nil, errors.Trace(err) 353 } 354 355 // key start with blockPrefix must be either record key or index key 356 // That's means causet's record key and index key are always together 357 // in the continuous interval. And for key with prefix smaller than 358 // blockPrefix, is smaller than all blocks. While for key with prefix 359 // bigger than blockPrefix, means is bigger than all blocks. 360 err = nil 361 if bytes.Compare(key, blockcodec.BlockPrefix()) < 0 { 362 frame.BlockID = math.MinInt64 363 frame.IndexID = math.MinInt64 364 frame.IsRecord = false 365 return 366 } 367 // bigger than blockPrefix, means is bigger than all blocks. 368 frame.BlockID = math.MaxInt64 369 frame.BlockID = math.MaxInt64 370 frame.IsRecord = true 371 return 372 } 373 374 // GetRecordFrame returns the record frame of a causet. If the causet's records 375 // are not covered by this frame range, it returns nil. 376 func (r *RegionFrameRange) GetRecordFrame(blockID int64, dbName, blockName string, isCommonHandle bool) (f *FrameItem) { 377 if blockID == r.First.BlockID && r.First.IsRecord { 378 r.First.DBName, r.First.BlockName = dbName, blockName 379 f = r.First 380 } else if blockID == r.Last.BlockID && r.Last.IsRecord { 381 r.Last.DBName, r.Last.BlockName = dbName, blockName 382 f = r.Last 383 } else if blockID >= r.First.BlockID && blockID < r.Last.BlockID { 384 f = &FrameItem{ 385 DBName: dbName, 386 BlockName: blockName, 387 BlockID: blockID, 388 IsRecord: true, 389 } 390 } 391 if f != nil && f.IsRecord && isCommonHandle { 392 f.IndexName = "PRIMARY" 393 } 394 return 395 } 396 397 // GetIndexFrame returns the indnex frame of a causet. If the causet's indices are 398 // not covered by this frame range, it returns nil. 399 func (r *RegionFrameRange) GetIndexFrame(blockID, indexID int64, dbName, blockName, indexName string) *FrameItem { 400 if blockID == r.First.BlockID && !r.First.IsRecord && indexID == r.First.IndexID { 401 r.First.DBName, r.First.BlockName, r.First.IndexName = dbName, blockName, indexName 402 return r.First 403 } 404 if blockID == r.Last.BlockID && indexID == r.Last.IndexID { 405 r.Last.DBName, r.Last.BlockName, r.Last.IndexName = dbName, blockName, indexName 406 return r.Last 407 } 408 409 greaterThanFirst := blockID > r.First.BlockID || (blockID == r.First.BlockID && !r.First.IsRecord && indexID > r.First.IndexID) 410 lessThanLast := blockID < r.Last.BlockID || (blockID == r.Last.BlockID && (r.Last.IsRecord || indexID < r.Last.IndexID)) 411 if greaterThanFirst && lessThanLast { 412 return &FrameItem{ 413 DBName: dbName, 414 BlockName: blockName, 415 BlockID: blockID, 416 IsRecord: false, 417 IndexName: indexName, 418 IndexID: indexID, 419 } 420 } 421 return nil 422 } 423 424 // RegionPeer stores information of one peer. 425 type RegionPeer struct { 426 ID int64 `json:"id"` 427 StoreID int64 `json:"store_id"` 428 IsLearner bool `json:"is_learner"` 429 } 430 431 // RegionEpoch stores the information about its epoch. 432 type RegionEpoch struct { 433 ConfVer int64 `json:"conf_ver"` 434 Version int64 `json:"version"` 435 } 436 437 // RegionPeerStat stores one field `DownSec` which indicates how long it's down than `RegionPeer`. 438 type RegionPeerStat struct { 439 RegionPeer 440 DownSec int64 `json:"down_seconds"` 441 } 442 443 // RegionInfo stores the information of one region. 444 type RegionInfo struct { 445 ID int64 `json:"id"` 446 StartKey string `json:"start_key"` 447 EndKey string `json:"end_key"` 448 Epoch RegionEpoch `json:"epoch"` 449 Peers []RegionPeer `json:"peers"` 450 Leader RegionPeer `json:"leader"` 451 DownPeers []RegionPeerStat `json:"down_peers"` 452 PendingPeers []RegionPeer `json:"pending_peers"` 453 WrittenBytes int64 `json:"written_bytes"` 454 ReadBytes int64 `json:"read_bytes"` 455 ApproximateSize int64 `json:"approximate_size"` 456 ApproximateKeys int64 `json:"approximate_keys"` 457 458 ReplicationStatus *ReplicationStatus `json:"replication_status,omitempty"` 459 } 460 461 // RegionsInfo stores the information of regions. 462 type RegionsInfo struct { 463 Count int64 `json:"count"` 464 Regions []RegionInfo `json:"regions"` 465 } 466 467 // ReplicationStatus represents the replication mode status of the region. 468 type ReplicationStatus struct { 469 State string `json:"state"` 470 StateID int64 `json:"state_id"` 471 } 472 473 // BlockInfo stores the information of a causet or an index 474 type BlockInfo struct { 475 EDB *perceptron.DBInfo 476 Block *perceptron.BlockInfo 477 IsIndex bool 478 Index *perceptron.IndexInfo 479 } 480 481 type withKeyRange interface { 482 getStartKey() string 483 getEndKey() string 484 } 485 486 // isIntersecting returns true if x and y intersect. 487 func isIntersecting(x, y withKeyRange) bool { 488 return isIntersectingKeyRange(x, y.getStartKey(), y.getEndKey()) 489 } 490 491 // isIntersectingKeyRange returns true if [startKey, endKey) intersect with x. 492 func isIntersectingKeyRange(x withKeyRange, startKey, endKey string) bool { 493 return !isBeforeKeyRange(x, startKey, endKey) && !isBehindKeyRange(x, startKey, endKey) 494 } 495 496 // isBehind returns true is x is behind y 497 func isBehind(x, y withKeyRange) bool { 498 return isBehindKeyRange(x, y.getStartKey(), y.getEndKey()) 499 } 500 501 // IsBefore returns true is x is before [startKey, endKey) 502 func isBeforeKeyRange(x withKeyRange, startKey, endKey string) bool { 503 return x.getEndKey() != "" && x.getEndKey() <= startKey 504 } 505 506 // IsBehind returns true is x is behind [startKey, endKey) 507 func isBehindKeyRange(x withKeyRange, startKey, endKey string) bool { 508 return endKey != "" && x.getStartKey() >= endKey 509 } 510 511 func (r *RegionInfo) getStartKey() string { return r.StartKey } 512 func (r *RegionInfo) getEndKey() string { return r.EndKey } 513 514 // for sorting 515 type byRegionStartKey []*RegionInfo 516 517 func (xs byRegionStartKey) Len() int { return len(xs) } 518 func (xs byRegionStartKey) Swap(i, j int) { xs[i], xs[j] = xs[j], xs[i] } 519 func (xs byRegionStartKey) Less(i, j int) bool { 520 return xs[i].getStartKey() < xs[j].getStartKey() 521 } 522 523 // blockInfoWithKeyRange stores causet or index informations with its key range. 524 type blockInfoWithKeyRange struct { 525 *BlockInfo 526 StartKey string 527 EndKey string 528 } 529 530 func (t blockInfoWithKeyRange) getStartKey() string { return t.StartKey } 531 func (t blockInfoWithKeyRange) getEndKey() string { return t.EndKey } 532 533 // for sorting 534 type byBlockStartKey []blockInfoWithKeyRange 535 536 func (xs byBlockStartKey) Len() int { return len(xs) } 537 func (xs byBlockStartKey) Swap(i, j int) { xs[i], xs[j] = xs[j], xs[i] } 538 func (xs byBlockStartKey) Less(i, j int) bool { 539 return xs[i].getStartKey() < xs[j].getStartKey() 540 } 541 542 func newBlockWithKeyRange(EDB *perceptron.DBInfo, causet *perceptron.BlockInfo) blockInfoWithKeyRange { 543 sk, ek := blockcodec.GetBlockHandleKeyRange(causet.ID) 544 startKey := bytesKeyToHex(codec.EncodeBytes(nil, sk)) 545 endKey := bytesKeyToHex(codec.EncodeBytes(nil, ek)) 546 return blockInfoWithKeyRange{ 547 &BlockInfo{ 548 EDB: EDB, 549 Block: causet, 550 IsIndex: false, 551 Index: nil, 552 }, 553 startKey, 554 endKey, 555 } 556 } 557 558 func newIndexWithKeyRange(EDB *perceptron.DBInfo, causet *perceptron.BlockInfo, index *perceptron.IndexInfo) blockInfoWithKeyRange { 559 sk, ek := blockcodec.GetBlockIndexKeyRange(causet.ID, index.ID) 560 startKey := bytesKeyToHex(codec.EncodeBytes(nil, sk)) 561 endKey := bytesKeyToHex(codec.EncodeBytes(nil, ek)) 562 return blockInfoWithKeyRange{ 563 &BlockInfo{ 564 EDB: EDB, 565 Block: causet, 566 IsIndex: true, 567 Index: index, 568 }, 569 startKey, 570 endKey, 571 } 572 } 573 574 // GetRegionsBlockInfo returns a map maps region id to its blocks or indices. 575 // Assuming blocks or indices key ranges never intersect. 576 // Regions key ranges can intersect. 577 func (h *Helper) GetRegionsBlockInfo(regionsInfo *RegionsInfo, schemas []*perceptron.DBInfo) map[int64][]BlockInfo { 578 blockInfos := make(map[int64][]BlockInfo, len(regionsInfo.Regions)) 579 580 regions := make([]*RegionInfo, 0, len(regionsInfo.Regions)) 581 for i := 0; i < len(regionsInfo.Regions); i++ { 582 blockInfos[regionsInfo.Regions[i].ID] = []BlockInfo{} 583 regions = append(regions, ®ionsInfo.Regions[i]) 584 } 585 586 blocks := []blockInfoWithKeyRange{} 587 for _, EDB := range schemas { 588 for _, causet := range EDB.Blocks { 589 blocks = append(blocks, newBlockWithKeyRange(EDB, causet)) 590 for _, index := range causet.Indices { 591 blocks = append(blocks, newIndexWithKeyRange(EDB, causet, index)) 592 } 593 } 594 } 595 596 if len(blocks) == 0 || len(regions) == 0 { 597 return blockInfos 598 } 599 600 sort.Sort(byRegionStartKey(regions)) 601 sort.Sort(byBlockStartKey(blocks)) 602 603 idx := 0 604 OutLoop: 605 for _, region := range regions { 606 id := region.ID 607 for isBehind(region, &blocks[idx]) { 608 idx++ 609 if idx >= len(blocks) { 610 break OutLoop 611 } 612 } 613 for i := idx; i < len(blocks) && isIntersecting(region, &blocks[i]); i++ { 614 blockInfos[id] = append(blockInfos[id], *blocks[i].BlockInfo) 615 } 616 } 617 618 return blockInfos 619 } 620 621 func bytesKeyToHex(key []byte) string { 622 return strings.ToUpper(hex.EncodeToString(key)) 623 } 624 625 // GetRegionsInfo gets the region information of current causetstore by using FIDel's api. 626 func (h *Helper) GetRegionsInfo() (*RegionsInfo, error) { 627 var regionsInfo RegionsInfo 628 err := h.requestFIDel("GET", FIDelapi.Regions, nil, ®ionsInfo) 629 return ®ionsInfo, err 630 } 631 632 // GetRegionInfoByID gets the region information of the region ID by using FIDel's api. 633 func (h *Helper) GetRegionInfoByID(regionID uint64) (*RegionInfo, error) { 634 var regionInfo RegionInfo 635 err := h.requestFIDel("GET", FIDelapi.RegionByID+strconv.FormatUint(regionID, 10), nil, ®ionInfo) 636 return ®ionInfo, err 637 } 638 639 // request FIDel API, decode the response body into res 640 func (h *Helper) requestFIDel(method, uri string, body io.Reader, res interface{}) error { 641 etcd, ok := h.CausetStore.(einsteindb.EtcdBackend) 642 if !ok { 643 return errors.WithStack(errors.New("not implemented")) 644 } 645 FIDelHosts, err := etcd.EtcdAddrs() 646 if err != nil { 647 return err 648 } 649 if len(FIDelHosts) == 0 { 650 return errors.New("fidel unavailable") 651 } 652 logutil.BgLogger().Debug("RequestFIDel URL", zap.String("url", soliton.InternalHTTPSchema()+"://"+FIDelHosts[0]+uri)) 653 req, err := http.NewRequest(method, soliton.InternalHTTPSchema()+"://"+FIDelHosts[0]+uri, body) 654 if err != nil { 655 return errors.Trace(err) 656 } 657 resp, err := soliton.InternalHTTPClient().Do(req) 658 if err != nil { 659 return errors.Trace(err) 660 } 661 662 defer func() { 663 err = resp.Body.Close() 664 if err != nil { 665 logutil.BgLogger().Error("close body failed", zap.Error(err)) 666 } 667 }() 668 669 err = json.NewCausetDecoder(resp.Body).Decode(res) 670 if err != nil { 671 return errors.Trace(err) 672 } 673 674 return nil 675 } 676 677 // StoresStat stores all information get from FIDel's api. 678 type StoresStat struct { 679 Count int `json:"count"` 680 Stores []StoreStat `json:"stores"` 681 } 682 683 // StoreStat stores information of one causetstore. 684 type StoreStat struct { 685 CausetStore StoreBaseStat `json:"causetstore"` 686 Status StoreDetailStat `json:"status"` 687 } 688 689 // StoreBaseStat stores the basic information of one causetstore. 690 type StoreBaseStat struct { 691 ID int64 `json:"id"` 692 Address string `json:"address"` 693 State int64 `json:"state"` 694 StateName string `json:"state_name"` 695 Version string `json:"version"` 696 Labels []StoreLabel `json:"labels"` 697 StatusAddress string `json:"status_address"` 698 GitHash string `json:"git_hash"` 699 StartTimestamp int64 `json:"start_timestamp"` 700 } 701 702 // StoreLabel stores the information of one causetstore label. 703 type StoreLabel struct { 704 Key string `json:"key"` 705 Value string `json:"value"` 706 } 707 708 // StoreDetailStat stores the detail information of one causetstore. 709 type StoreDetailStat struct { 710 Capacity string `json:"capacity"` 711 Available string `json:"available"` 712 LeaderCount int64 `json:"leader_count"` 713 LeaderWeight float64 `json:"leader_weight"` 714 LeaderSembedded float64 `json:"leader_sembedded"` 715 LeaderSize int64 `json:"leader_size"` 716 RegionCount int64 `json:"region_count"` 717 RegionWeight float64 `json:"region_weight"` 718 RegionSembedded float64 `json:"region_sembedded"` 719 RegionSize int64 `json:"region_size"` 720 StartTs time.Time `json:"start_ts"` 721 LastHeartbeatTs time.Time `json:"last_heartbeat_ts"` 722 Uptime string `json:"uptime"` 723 } 724 725 // GetStoresStat gets the EinsteinDB causetstore information by accessing FIDel's api. 726 func (h *Helper) GetStoresStat() (*StoresStat, error) { 727 etcd, ok := h.CausetStore.(einsteindb.EtcdBackend) 728 if !ok { 729 return nil, errors.WithStack(errors.New("not implemented")) 730 } 731 FIDelHosts, err := etcd.EtcdAddrs() 732 if err != nil { 733 return nil, err 734 } 735 if len(FIDelHosts) == 0 { 736 return nil, errors.New("fidel unavailable") 737 } 738 req, err := http.NewRequest("GET", soliton.InternalHTTPSchema()+"://"+FIDelHosts[0]+FIDelapi.Stores, nil) 739 if err != nil { 740 return nil, errors.Trace(err) 741 } 742 resp, err := soliton.InternalHTTPClient().Do(req) 743 if err != nil { 744 return nil, errors.Trace(err) 745 } 746 defer func() { 747 err = resp.Body.Close() 748 if err != nil { 749 logutil.BgLogger().Error("close body failed", zap.Error(err)) 750 } 751 }() 752 var storesStat StoresStat 753 err = json.NewCausetDecoder(resp.Body).Decode(&storesStat) 754 if err != nil { 755 return nil, errors.Trace(err) 756 } 757 return &storesStat, nil 758 } 759 760 // GetFIDelAddr return the FIDel Address. 761 func (h *Helper) GetFIDelAddr() ([]string, error) { 762 etcd, ok := h.CausetStore.(einsteindb.EtcdBackend) 763 if !ok { 764 return nil, errors.New("not implemented") 765 } 766 FIDelAddrs, err := etcd.EtcdAddrs() 767 if err != nil { 768 return nil, err 769 } 770 if len(FIDelAddrs) == 0 { 771 return nil, errors.New("fidel unavailable") 772 } 773 return FIDelAddrs, nil 774 } 775 776 // FIDelRegionStats is the json response from FIDel. 777 type FIDelRegionStats struct { 778 Count int `json:"count"` 779 EmptyCount int `json:"empty_count"` 780 StorageSize int64 `json:"storage_size"` 781 StorageKeys int64 `json:"storage_keys"` 782 StoreLeaderCount map[uint64]int `json:"store_leader_count"` 783 StorePeerCount map[uint64]int `json:"store_peer_count"` 784 } 785 786 // GetFIDelRegionStats get the RegionStats by blockID. 787 func (h *Helper) GetFIDelRegionStats(blockID int64, stats *FIDelRegionStats) error { 788 FIDelAddrs, err := h.GetFIDelAddr() 789 if err != nil { 790 return err 791 } 792 793 startKey := blockcodec.EncodeBlockPrefix(blockID) 794 endKey := blockcodec.EncodeBlockPrefix(blockID + 1) 795 startKey = codec.EncodeBytes([]byte{}, startKey) 796 endKey = codec.EncodeBytes([]byte{}, endKey) 797 798 statURL := fmt.Sprintf("%s://%s/fidel/api/v1/stats/region?start_key=%s&end_key=%s", 799 soliton.InternalHTTPSchema(), 800 FIDelAddrs[0], 801 url.QueryEscape(string(startKey)), 802 url.QueryEscape(string(endKey))) 803 804 resp, err := soliton.InternalHTTPClient().Get(statURL) 805 if err != nil { 806 return err 807 } 808 809 defer func() { 810 if err = resp.Body.Close(); err != nil { 811 log.Error(err) 812 } 813 }() 814 815 dec := json.NewCausetDecoder(resp.Body) 816 817 return dec.Decode(stats) 818 }