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.

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