github.com/XiaoMi/Gaea@v1.2.5/proxy/router/rule.go (about) 1 // Copyright 2016 The kingshard Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 // not use this file except in compliance with the License. You may obtain 5 // 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, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations 13 // under the License. 14 15 // Copyright 2019 The Gaea Authors. All Rights Reserved. 16 // 17 // Licensed under the Apache License, Version 2.0 (the "License"); 18 // you may not use this file except in compliance with the License. 19 // You may obtain a copy of the License at 20 // 21 // http://www.apache.org/licenses/LICENSE-2.0 22 // 23 // Unless required by applicable law or agreed to in writing, software 24 // distributed under the License is distributed on an "AS IS" BASIS, 25 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26 // See the License for the specific language governing permissions and 27 // limitations under the License. 28 29 package router 30 31 import ( 32 "fmt" 33 "regexp" 34 "strconv" 35 "strings" 36 37 "github.com/XiaoMi/Gaea/core/errors" 38 "github.com/XiaoMi/Gaea/models" 39 ) 40 41 const ( 42 DefaultRuleType = models.ShardDefault 43 GlobalTableRuleType = models.ShardGlobal 44 LinkedTableRuleType = models.ShardLinked // this type only exists in conf, then transfer to LinkedRule 45 HashRuleType = models.ShardHash 46 RangeRuleType = models.ShardRange 47 ModRuleType = models.ShardMod 48 DateYearRuleType = models.ShardYear 49 DateMonthRuleType = models.ShardMonth 50 DateDayRuleType = models.ShardDay 51 MycatModRuleType = models.ShardMycatMod 52 MycatLongRuleType = models.ShardMycatLong 53 MycatStringRuleType = models.ShardMycatString 54 MycatMurmurRuleType = models.ShardMycatMURMUR 55 MycatPaddingModRuleType = models.ShardMycatPaddingMod 56 57 MinMonthDaysCount = 28 58 MaxMonthDaysCount = 31 59 MonthsCount = 12 60 ) 61 62 type Rule interface { 63 GetDB() string 64 GetTable() string 65 GetShardingColumn() string 66 IsLinkedRule() bool 67 GetShard() Shard 68 FindTableIndex(key interface{}) (int, error) 69 GetSlice(i int) string // i is slice index 70 GetSliceIndexFromTableIndex(i int) int 71 GetSlices() []string 72 GetSubTableIndexes() []int 73 GetFirstTableIndex() int 74 GetLastTableIndex() int 75 GetType() string 76 GetDatabaseNameByTableIndex(index int) (string, error) 77 } 78 79 type MycatRule interface { 80 Rule 81 GetDatabases() []string 82 GetTableIndexByDatabaseName(phyDB string) (int, bool) 83 } 84 85 type BaseRule struct { 86 db string 87 table string 88 shardingColumn string 89 90 ruleType string 91 slices []string // not the namespace slices 92 subTableIndexes []int //subTableIndexes store all the index of sharding sub-table 93 tableToSlice map[int]int //key is table index, and value is slice index 94 shard Shard 95 96 // TODO: 目前全局表也借用这两个field存放默认分片的物理DB名 97 mycatDatabases []string 98 mycatDatabaseToTableIndexMap map[string]int // key: phy db name, value: table index 99 } 100 101 type LinkedRule struct { 102 db string 103 table string 104 shardingColumn string 105 106 linkToRule *BaseRule 107 } 108 109 func NewDefaultRule(slice string) *BaseRule { 110 var r *BaseRule = &BaseRule{ 111 ruleType: DefaultRuleType, 112 slices: []string{slice}, 113 shard: new(DefaultShard), 114 tableToSlice: nil, 115 } 116 return r 117 } 118 119 func (r *BaseRule) GetDB() string { 120 return r.db 121 } 122 123 func (r *BaseRule) GetTable() string { 124 return r.table 125 } 126 127 func (r *BaseRule) GetShardingColumn() string { 128 return r.shardingColumn 129 } 130 131 func (r *BaseRule) IsLinkedRule() bool { 132 return false 133 } 134 135 func (r *BaseRule) GetShard() Shard { 136 return r.shard 137 } 138 139 func (r *BaseRule) FindTableIndex(key interface{}) (int, error) { 140 return r.shard.FindForKey(key) 141 } 142 143 // The confs should be verified before use to avoid panic. 144 func (r *BaseRule) GetSlice(i int) string { 145 return r.slices[i] 146 } 147 148 func (r *BaseRule) GetSliceIndexFromTableIndex(i int) int { 149 sliceIndex, ok := r.tableToSlice[i] 150 if !ok { 151 return -1 152 } 153 return sliceIndex 154 } 155 156 // This is dangerous since the caller can change the value in slices. 157 // It is better to return a iterator instead of exposing the origin slices. 158 func (r *BaseRule) GetSlices() []string { 159 return r.slices 160 } 161 162 func (r *BaseRule) GetSubTableIndexes() []int { 163 return r.subTableIndexes 164 } 165 166 func (r *BaseRule) GetFirstTableIndex() int { 167 return r.subTableIndexes[0] 168 } 169 170 func (r *BaseRule) GetLastTableIndex() int { 171 return r.subTableIndexes[len(r.subTableIndexes)-1] 172 } 173 174 func (r *BaseRule) GetType() string { 175 return r.ruleType 176 } 177 178 func (r *BaseRule) GetDatabaseNameByTableIndex(index int) (string, error) { 179 if IsMycatShardingRule(r.ruleType) || r.ruleType == GlobalTableRuleType { 180 if index > len(r.subTableIndexes) { 181 return "", errors.ErrInvalidArgument 182 } 183 return r.mycatDatabases[index], nil 184 } 185 return r.db, nil 186 } 187 188 func (r *BaseRule) GetTableIndexByDatabaseName(phyDB string) (int, bool) { 189 idx, ok := r.mycatDatabaseToTableIndexMap[phyDB] 190 return idx, ok 191 } 192 193 func (r *BaseRule) GetDatabases() []string { 194 return r.mycatDatabases 195 } 196 197 func (l *LinkedRule) GetDB() string { 198 return l.db 199 } 200 201 func (l *LinkedRule) GetTable() string { 202 return l.table 203 } 204 205 func (l *LinkedRule) GetParentDB() string { 206 return l.linkToRule.GetDB() 207 } 208 209 func (l *LinkedRule) GetParentTable() string { 210 return l.linkToRule.GetTable() 211 } 212 213 func (l *LinkedRule) GetShardingColumn() string { 214 return l.shardingColumn 215 } 216 217 func (l *LinkedRule) IsLinkedRule() bool { 218 return true 219 } 220 221 func (l *LinkedRule) GetShard() Shard { 222 return l.linkToRule.GetShard() 223 } 224 225 func (l *LinkedRule) FindTableIndex(key interface{}) (int, error) { 226 return l.linkToRule.FindTableIndex(key) 227 } 228 229 func (l *LinkedRule) GetFirstTableIndex() int { 230 return l.linkToRule.GetFirstTableIndex() 231 } 232 233 func (l *LinkedRule) GetLastTableIndex() int { 234 return l.linkToRule.GetLastTableIndex() 235 } 236 237 func (l *LinkedRule) GetSlice(i int) string { 238 return l.linkToRule.GetSlice(i) 239 } 240 241 func (l *LinkedRule) GetSliceIndexFromTableIndex(i int) int { 242 return l.linkToRule.GetSliceIndexFromTableIndex(i) 243 } 244 245 func (l *LinkedRule) GetSlices() []string { 246 return l.linkToRule.GetSlices() 247 } 248 249 func (l *LinkedRule) GetSubTableIndexes() []int { 250 return l.linkToRule.GetSubTableIndexes() 251 } 252 253 func (l *LinkedRule) GetType() string { 254 return l.linkToRule.GetType() 255 } 256 257 func (l *LinkedRule) GetDatabaseNameByTableIndex(index int) (string, error) { 258 return l.linkToRule.GetDatabaseNameByTableIndex(index) 259 } 260 261 func (l *LinkedRule) GetDatabases() []string { 262 return l.linkToRule.GetDatabases() 263 } 264 265 func (l *LinkedRule) GetTableIndexByDatabaseName(phyDB string) (int, bool) { 266 return l.linkToRule.GetTableIndexByDatabaseName(phyDB) 267 } 268 269 func createLinkedRule(rules map[string]map[string]Rule, shard *models.Shard) (*LinkedRule, error) { 270 if shard.Type != LinkedTableRuleType { 271 return nil, fmt.Errorf("LinkedRule type is not linked: %v", shard) 272 } 273 274 tableRules, ok := rules[shard.DB] 275 if !ok { 276 return nil, fmt.Errorf("db of LinkedRule is not found in parent rules") 277 } 278 dbRule, ok := tableRules[strings.ToLower(shard.ParentTable)] 279 if !ok { 280 return nil, fmt.Errorf("parent table of LinkedRule is not found in parent rules") 281 } 282 if dbRule.GetType() == LinkedTableRuleType { 283 return nil, fmt.Errorf("LinkedRule cannot link to another LinkedRule") 284 } 285 linkToRule, ok := dbRule.(*BaseRule) 286 if !ok { 287 return nil, fmt.Errorf("LinkedRule must link to a BaseRule") 288 } 289 290 linkedRule := &LinkedRule{ 291 db: shard.DB, 292 table: strings.ToLower(shard.Table), 293 shardingColumn: strings.ToLower(shard.Key), 294 linkToRule: linkToRule, 295 } 296 297 return linkedRule, nil 298 } 299 300 func parseRule(cfg *models.Shard) (*BaseRule, error) { 301 r := new(BaseRule) 302 r.db = cfg.DB 303 r.table = strings.ToLower(cfg.Table) 304 r.shardingColumn = strings.ToLower(cfg.Key) //ignore case 305 r.ruleType = cfg.Type 306 r.slices = cfg.Slices //将rule model中的slices赋值给rule 307 r.mycatDatabaseToTableIndexMap = make(map[string]int) 308 309 subTableIndexs, tableToSlice, shard, err := parseRuleSliceInfos(cfg) 310 if err != nil { 311 return nil, err 312 } 313 314 r.subTableIndexes = subTableIndexs 315 r.tableToSlice = tableToSlice 316 r.shard = shard 317 318 if IsMycatShardingRule(cfg.Type) { 319 r.mycatDatabases, err = getRealDatabases(cfg.Databases) 320 if err != nil { 321 return nil, err 322 } 323 for i, db := range r.mycatDatabases { 324 r.mycatDatabaseToTableIndexMap[db] = i 325 } 326 } 327 328 if cfg.Type == GlobalTableRuleType { 329 // 如果全局表指定了物理库名, 则使用mycatDatabases存储这一信息, 否则使用逻辑库名作为物理库名. 330 if len(cfg.Databases) != 0 { 331 r.mycatDatabases, err = getRealDatabases(cfg.Databases) 332 if err != nil { 333 return nil, err 334 } 335 } else { 336 for i := 0; i < len(r.subTableIndexes); i++ { 337 r.mycatDatabases = append(r.mycatDatabases, r.db) 338 } 339 } 340 for i, db := range r.mycatDatabases { 341 r.mycatDatabaseToTableIndexMap[db] = i 342 } 343 } 344 345 return r, nil 346 } 347 348 func parseRuleSliceInfos(cfg *models.Shard) ([]int, map[int]int, Shard, error) { 349 switch cfg.Type { 350 case HashRuleType: 351 subTableIndexs, tableToSlice, err := parseHashRuleSliceInfos(cfg.Locations, cfg.Slices) 352 if err != nil { 353 return nil, nil, nil, err 354 } 355 shard := &HashShard{ShardNum: len(tableToSlice)} 356 return subTableIndexs, tableToSlice, shard, nil 357 case ModRuleType: 358 subTableIndexs, tableToSlice, err := parseHashRuleSliceInfos(cfg.Locations, cfg.Slices) 359 if err != nil { 360 return nil, nil, nil, err 361 } 362 shard := &ModShard{ShardNum: len(tableToSlice)} 363 return subTableIndexs, tableToSlice, shard, nil 364 case RangeRuleType: 365 subTableIndexs, tableToSlice, err := parseHashRuleSliceInfos(cfg.Locations, cfg.Slices) 366 if err != nil { 367 return nil, nil, nil, err 368 } 369 rs, err := ParseNumSharding(cfg.Locations, cfg.TableRowLimit) 370 if err != nil { 371 return nil, nil, nil, err 372 } 373 if len(rs) != len(tableToSlice) { 374 return nil, nil, nil, fmt.Errorf("range space %d not equal tables %d", len(rs), len(tableToSlice)) 375 } 376 shard := &NumRangeShard{Shards: rs} 377 return subTableIndexs, tableToSlice, shard, nil 378 case DateDayRuleType: 379 subTableIndexs, tableToSlice, err := parseDateDayRuleSliceInfos(cfg.DateRange, cfg.Slices) 380 if err != nil { 381 return nil, nil, nil, err 382 } 383 shard := &DateDayShard{} 384 return subTableIndexs, tableToSlice, shard, nil 385 case DateMonthRuleType: 386 subTableIndexs, tableToSlice, err := parseDateMonthRuleSliceInfos(cfg.DateRange, cfg.Slices) 387 if err != nil { 388 return nil, nil, nil, err 389 } 390 shard := &DateMonthShard{} 391 return subTableIndexs, tableToSlice, shard, nil 392 case DateYearRuleType: 393 subTableIndexs, tableToSlice, err := parseDateYearRuleSliceInfos(cfg.DateRange, cfg.Slices) 394 if err != nil { 395 return nil, nil, nil, err 396 } 397 shard := &DateYearShard{} 398 return subTableIndexs, tableToSlice, shard, nil 399 case MycatModRuleType: 400 subTableIndexs, tableToSlice, err := parseMycatHashRuleSliceInfos(cfg.Locations, cfg.Slices, cfg.Databases) 401 if err != nil { 402 return nil, nil, nil, err 403 } 404 shard := NewMycatPartitionModShard(len(tableToSlice)) 405 return subTableIndexs, tableToSlice, shard, nil 406 case MycatLongRuleType: 407 subTableIndexs, tableToSlice, err := parseMycatHashRuleSliceInfos(cfg.Locations, cfg.Slices, cfg.Databases) 408 if err != nil { 409 return nil, nil, nil, err 410 } 411 shard := NewMycatPartitionLongShard(len(tableToSlice), cfg.PartitionCount, cfg.PartitionLength) 412 if err = shard.Init(); err != nil { 413 return nil, nil, nil, err 414 } 415 return subTableIndexs, tableToSlice, shard, nil 416 case MycatStringRuleType: 417 subTableIndexs, tableToSlice, err := parseMycatHashRuleSliceInfos(cfg.Locations, cfg.Slices, cfg.Databases) 418 if err != nil { 419 return nil, nil, nil, err 420 } 421 shard := NewMycatPartitionStringShard(len(tableToSlice), cfg.PartitionCount, cfg.PartitionLength, cfg.HashSlice) 422 if err = shard.Init(); err != nil { 423 return nil, nil, nil, err 424 } 425 return subTableIndexs, tableToSlice, shard, nil 426 case MycatMurmurRuleType: 427 subTableIndexs, tableToSlice, err := parseMycatHashRuleSliceInfos(cfg.Locations, cfg.Slices, cfg.Databases) 428 if err != nil { 429 return nil, nil, nil, err 430 } 431 432 shard, err := NewMycatPartitionMurmurHashShard(cfg.Seed, cfg.VirtualBucketTimes, len(tableToSlice)) 433 if err != nil { 434 return nil, nil, nil, err 435 } 436 if err = shard.Init(); err != nil { 437 return nil, nil, nil, err 438 } 439 return subTableIndexs, tableToSlice, shard, nil 440 case MycatPaddingModRuleType: 441 subTableIndexs, tableToSlice, err := parseMycatHashRuleSliceInfos(cfg.Locations, cfg.Slices, cfg.Databases) 442 if err != nil { 443 return nil, nil, nil, err 444 } 445 446 shard, err := GetMycatPartitionPaddingModShard(cfg.PadFrom, cfg.PadLength, cfg.ModBegin, cfg.ModEnd, len(tableToSlice)) 447 if err != nil { 448 return nil, nil, nil, err 449 } 450 if err = shard.Init(); err != nil { 451 return nil, nil, nil, err 452 } 453 return subTableIndexs, tableToSlice, shard, nil 454 case GlobalTableRuleType: 455 subTableIndexs, tableToSlice, err := parseGlobalTableRuleSliceInfos(cfg.Locations, cfg.Slices, cfg.Databases) 456 if err != nil { 457 return nil, nil, nil, err 458 } 459 shard := NewGlobalTableShard() 460 return subTableIndexs, tableToSlice, shard, nil 461 default: 462 return nil, nil, nil, errors.ErrUnknownRuleType 463 } 464 } 465 466 func parseHashRuleSliceInfos(locations []int, slices []string) ([]int, map[int]int, error) { 467 var sumTables int 468 var subTableIndexs []int 469 tableToSlice := make(map[int]int, 0) 470 471 if len(locations) != len(slices) { 472 return nil, nil, errors.ErrLocationsCount 473 } 474 for i := 0; i < len(locations); i++ { 475 for j := 0; j < locations[i]; j++ { 476 subTableIndexs = append(subTableIndexs, j+sumTables) 477 tableToSlice[j+sumTables] = i 478 } 479 sumTables += locations[i] 480 } 481 return subTableIndexs, tableToSlice, nil 482 } 483 484 func parseMycatHashRuleSliceInfos(locations []int, slices []string, databases []string) ([]int, map[int]int, error) { 485 subTableIndexs, tableToSlice, err := parseHashRuleSliceInfos(locations, slices) 486 if err != nil { 487 return nil, nil, err 488 } 489 490 realDatabaseList, err := getRealDatabases(databases) 491 if err != nil { 492 return nil, nil, err 493 } 494 495 if len(tableToSlice) != len(realDatabaseList) { 496 return nil, nil, errors.ErrLocationsCount 497 } 498 499 return subTableIndexs, tableToSlice, nil 500 } 501 502 func parseDateDayRuleSliceInfos(dateRange []string, slices []string) ([]int, map[int]int, error) { 503 var subTableIndexs []int 504 tableToSlice := make(map[int]int, 0) 505 506 if len(dateRange) != len(slices) { 507 return nil, nil, errors.ErrDateRangeCount 508 } 509 for i := 0; i < len(dateRange); i++ { 510 dayNumbers, err := ParseDayRange(dateRange[i]) 511 if err != nil { 512 return nil, nil, err 513 } 514 if len(subTableIndexs) > 0 && dayNumbers[0] <= subTableIndexs[len(subTableIndexs)-1] { 515 return nil, nil, errors.ErrDateRangeOverlap 516 } 517 for _, v := range dayNumbers { 518 subTableIndexs = append(subTableIndexs, v) 519 tableToSlice[v] = i 520 } 521 } 522 return subTableIndexs, tableToSlice, nil 523 } 524 525 func parseDateMonthRuleSliceInfos(dateRange []string, slices []string) ([]int, map[int]int, error) { 526 var subTableIndexs []int 527 tableToSlice := make(map[int]int, 0) 528 529 if len(dateRange) != len(slices) { 530 return nil, nil, errors.ErrDateRangeCount 531 } 532 for i := 0; i < len(dateRange); i++ { 533 monthNumbers, err := ParseMonthRange(dateRange[i]) 534 if err != nil { 535 return nil, nil, err 536 } 537 if len(subTableIndexs) > 0 && monthNumbers[0] <= subTableIndexs[len(subTableIndexs)-1] { 538 return nil, nil, errors.ErrDateRangeOverlap 539 } 540 for _, v := range monthNumbers { 541 subTableIndexs = append(subTableIndexs, v) 542 tableToSlice[v] = i 543 } 544 } 545 return subTableIndexs, tableToSlice, nil 546 } 547 548 func parseDateYearRuleSliceInfos(dateRange []string, slices []string) ([]int, map[int]int, error) { 549 var subTableIndexs []int 550 tableToSlice := make(map[int]int, 0) 551 552 if len(dateRange) != len(slices) { 553 return nil, nil, errors.ErrDateRangeCount 554 } 555 for i := 0; i < len(dateRange); i++ { 556 yearNumbers, err := ParseYearRange(dateRange[i]) 557 if err != nil { 558 return nil, nil, err 559 } 560 if len(subTableIndexs) > 0 && yearNumbers[0] <= subTableIndexs[len(subTableIndexs)-1] { 561 return nil, nil, errors.ErrDateRangeOverlap 562 } 563 for _, v := range yearNumbers { 564 tableToSlice[v] = i 565 subTableIndexs = append(subTableIndexs, v) 566 } 567 } 568 return subTableIndexs, tableToSlice, nil 569 } 570 571 func parseGlobalTableRuleSliceInfos(locations []int, slices []string, databases []string) ([]int, map[int]int, error) { 572 subTableIndexs, tableToSlice, err := parseHashRuleSliceInfos(locations, slices) 573 if err != nil { 574 return nil, nil, err 575 } 576 577 if len(databases) != 0 { 578 realDatabaseList, err := getRealDatabases(databases) 579 if err != nil { 580 return nil, nil, err 581 } 582 if len(tableToSlice) != len(realDatabaseList) { 583 return nil, nil, errors.ErrLocationsCount 584 } 585 } 586 587 return subTableIndexs, tableToSlice, nil 588 } 589 590 func includeSlice(slices []string, sliceName string) bool { 591 for _, s := range slices { 592 if s == sliceName { 593 return true 594 } 595 } 596 return false 597 } 598 599 var rangeDatabaseRegex = regexp.MustCompile(`^(\S+?)\[(\d+)-(\d+)\]$`) 600 601 // if a dbname is a database list, then parse the real dbnames and add to the result. 602 // the range contains left bound and right bound, which means [left, right]. 603 func getRealDatabases(dbs []string) ([]string, error) { 604 var ret []string 605 for _, db := range dbs { 606 if rangeDatabaseRegex.MatchString(db) { 607 matches := rangeDatabaseRegex.FindStringSubmatch(db) 608 if len(matches) != 4 { 609 return nil, fmt.Errorf("invalid database list: %s", db) 610 } 611 dbPrefix := matches[1] 612 leftBoundStr := matches[2] 613 rightBoundStr := matches[3] 614 leftBound, err := strconv.Atoi(leftBoundStr) 615 if err != nil { 616 return nil, fmt.Errorf("invalid left bound value of database list: %s", db) 617 } 618 rightBound, err := strconv.Atoi(rightBoundStr) 619 if err != nil { 620 return nil, fmt.Errorf("invalid right bound value of database list: %s", db) 621 } 622 if rightBound <= leftBound { 623 return nil, fmt.Errorf("invalid bound value of database list: %s", db) 624 } 625 for i := leftBound; i <= rightBound; i++ { 626 realDB := dbPrefix + strconv.Itoa(i) 627 ret = append(ret, realDB) 628 } 629 } else { 630 ret = append(ret, db) 631 } 632 } 633 return ret, nil 634 } 635 636 func IsMycatShardingRule(ruleType string) bool { 637 return ruleType == MycatModRuleType || ruleType == MycatLongRuleType || ruleType == MycatMurmurRuleType || ruleType == MycatPaddingModRuleType || ruleType == MycatStringRuleType 638 }