You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
4.2 KiB

3 months ago
  1. package shell
  2. import (
  3. "flag"
  4. "fmt"
  5. "io"
  6. )
  7. func init() {
  8. Commands = append(Commands, &commandEcBalance{})
  9. }
  10. type commandEcBalance struct {
  11. }
  12. func (c *commandEcBalance) Name() string {
  13. return "ec.balance"
  14. }
  15. // TODO: Update help string and move to command_ec_common.go once shard replica placement logic is enabled.
  16. func (c *commandEcBalance) Help() string {
  17. return `balance all ec shards among all racks and volume servers
  18. ec.balance [-c EACH_COLLECTION|<collection_name>] [-force] [-dataCenter <data_center>] [-shardReplicaPlacement <replica_placement>]
  19. Algorithm:
  20. func EcBalance() {
  21. for each collection:
  22. balanceEcVolumes(collectionName)
  23. for each rack:
  24. balanceEcRack(rack)
  25. }
  26. func balanceEcVolumes(collectionName){
  27. for each volume:
  28. doDeduplicateEcShards(volumeId)
  29. tracks rack~shardCount mapping
  30. for each volume:
  31. doBalanceEcShardsAcrossRacks(volumeId)
  32. for each volume:
  33. doBalanceEcShardsWithinRacks(volumeId)
  34. }
  35. // spread ec shards into more racks
  36. func doBalanceEcShardsAcrossRacks(volumeId){
  37. tracks rack~volumeIdShardCount mapping
  38. averageShardsPerEcRack = totalShardNumber / numRacks // totalShardNumber is 14 for now, later could varies for each dc
  39. ecShardsToMove = select overflown ec shards from racks with ec shard counts > averageShardsPerEcRack
  40. for each ecShardsToMove {
  41. destRack = pickOneRack(rack~shardCount, rack~volumeIdShardCount, averageShardsPerEcRack)
  42. destVolumeServers = volume servers on the destRack
  43. pickOneEcNodeAndMoveOneShard(destVolumeServers)
  44. }
  45. }
  46. func doBalanceEcShardsWithinRacks(volumeId){
  47. racks = collect all racks that the volume id is on
  48. for rack, shards := range racks
  49. doBalanceEcShardsWithinOneRack(volumeId, shards, rack)
  50. }
  51. // move ec shards
  52. func doBalanceEcShardsWithinOneRack(volumeId, shards, rackId){
  53. tracks volumeServer~volumeIdShardCount mapping
  54. averageShardCount = len(shards) / numVolumeServers
  55. volumeServersOverAverage = volume servers with volumeId's ec shard counts > averageShardsPerEcRack
  56. ecShardsToMove = select overflown ec shards from volumeServersOverAverage
  57. for each ecShardsToMove {
  58. destVolumeServer = pickOneVolumeServer(volumeServer~shardCount, volumeServer~volumeIdShardCount, averageShardCount)
  59. pickOneEcNodeAndMoveOneShard(destVolumeServers)
  60. }
  61. }
  62. // move ec shards while keeping shard distribution for the same volume unchanged or more even
  63. func balanceEcRack(rack){
  64. averageShardCount = total shards / numVolumeServers
  65. for hasMovedOneEcShard {
  66. sort all volume servers ordered by the number of local ec shards
  67. pick the volume server A with the lowest number of ec shards x
  68. pick the volume server B with the highest number of ec shards y
  69. if y > averageShardCount and x +1 <= averageShardCount {
  70. if B has a ec shard with volume id v that A does not have {
  71. move one ec shard v from B to A
  72. hasMovedOneEcShard = true
  73. }
  74. }
  75. }
  76. }
  77. `
  78. }
  79. func (c *commandEcBalance) HasTag(CommandTag) bool {
  80. return false
  81. }
  82. func (c *commandEcBalance) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {
  83. balanceCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
  84. collection := balanceCommand.String("collection", "EACH_COLLECTION", "collection name, or \"EACH_COLLECTION\" for each collection")
  85. dc := balanceCommand.String("dataCenter", "", "only apply the balancing for this dataCenter")
  86. shardReplicaPlacement := balanceCommand.String("shardReplicaPlacement", "", "replica placement for EC shards, or master default if empty (currently unused)")
  87. applyBalancing := balanceCommand.Bool("force", false, "apply the balancing plan")
  88. if err = balanceCommand.Parse(args); err != nil {
  89. return nil
  90. }
  91. infoAboutSimulationMode(writer, *applyBalancing, "-force")
  92. if err = commandEnv.confirmIsLocked(args); err != nil {
  93. return
  94. }
  95. var collections []string
  96. if *collection == "EACH_COLLECTION" {
  97. collections, err = ListCollectionNames(commandEnv, false, true)
  98. if err != nil {
  99. return err
  100. }
  101. } else {
  102. collections = append(collections, *collection)
  103. }
  104. fmt.Printf("balanceEcVolumes collections %+v\n", len(collections))
  105. rp, err := parseReplicaPlacementArg(commandEnv, *shardReplicaPlacement)
  106. if err != nil {
  107. return err
  108. }
  109. return EcBalance(commandEnv, collections, *dc, rp, *applyBalancing)
  110. }