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.

232 lines
7.0 KiB

  1. package shell
  2. import (
  3. "fmt"
  4. "strings"
  5. "testing"
  6. "github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
  7. "github.com/seaweedfs/seaweedfs/weed/storage/erasure_coding"
  8. "github.com/seaweedfs/seaweedfs/weed/storage/needle"
  9. "github.com/seaweedfs/seaweedfs/weed/storage/super_block"
  10. )
  11. var (
  12. topology1 = parseOutput(topoData)
  13. topology2 = parseOutput(topoData2)
  14. topologyEc = parseOutput(topoDataEc)
  15. )
  16. func errorCheck(got error, want string) error {
  17. if got == nil && want == "" {
  18. return nil
  19. }
  20. if got != nil && want == "" {
  21. return fmt.Errorf("expected no error, got %q", got.Error())
  22. }
  23. if got == nil && want != "" {
  24. return fmt.Errorf("got no error, expected %q", want)
  25. }
  26. if !strings.Contains(got.Error(), want) {
  27. return fmt.Errorf("expected error %q, got %q", want, got.Error())
  28. }
  29. return nil
  30. }
  31. func TestParseReplicaPlacementArg(t *testing.T) {
  32. getDefaultReplicaPlacementOrig := getDefaultReplicaPlacement
  33. getDefaultReplicaPlacement = func(commandEnv *CommandEnv) (*super_block.ReplicaPlacement, error) {
  34. return super_block.NewReplicaPlacementFromString("123")
  35. }
  36. defer func() {
  37. getDefaultReplicaPlacement = getDefaultReplicaPlacementOrig
  38. }()
  39. testCases := []struct {
  40. argument string
  41. want string
  42. wantErr string
  43. }{
  44. {"lalala", "lal", "unexpected replication type"},
  45. {"", "123", ""},
  46. {"021", "021", ""},
  47. }
  48. for _, tc := range testCases {
  49. commandEnv := &CommandEnv{}
  50. got, gotErr := parseReplicaPlacementArg(commandEnv, tc.argument)
  51. if err := errorCheck(gotErr, tc.wantErr); err != nil {
  52. t.Errorf("argument %q: %s", tc.argument, err.Error())
  53. continue
  54. }
  55. want, _ := super_block.NewReplicaPlacementFromString(tc.want)
  56. if !got.Equals(want) {
  57. t.Errorf("got replica placement %q, want %q", got.String(), want.String())
  58. }
  59. }
  60. }
  61. func TestEcDistribution(t *testing.T) {
  62. // find out all volume servers with one slot left.
  63. ecNodes, totalFreeEcSlots := collectEcVolumeServersByDc(topology1, "")
  64. sortEcNodesByFreeslotsDescending(ecNodes)
  65. if totalFreeEcSlots < erasure_coding.TotalShardsCount {
  66. t.Errorf("not enough free ec shard slots: %d", totalFreeEcSlots)
  67. }
  68. allocatedDataNodes := ecNodes
  69. if len(allocatedDataNodes) > erasure_coding.TotalShardsCount {
  70. allocatedDataNodes = allocatedDataNodes[:erasure_coding.TotalShardsCount]
  71. }
  72. for _, dn := range allocatedDataNodes {
  73. // fmt.Printf("info %+v %+v\n", dn.info, dn)
  74. fmt.Printf("=> %+v %+v\n", dn.info.Id, dn.freeEcSlot)
  75. }
  76. }
  77. func TestPickRackToBalanceShardsInto(t *testing.T) {
  78. testCases := []struct {
  79. topology *master_pb.TopologyInfo
  80. vid string
  81. replicaPlacement string
  82. wantOneOf []string
  83. wantErr string
  84. }{
  85. // Non-EC volumes. We don't care about these, but the function should return all racks as a safeguard.
  86. {topologyEc, "", "123", []string{"rack1", "rack2", "rack3", "rack4", "rack5", "rack6"}, ""},
  87. {topologyEc, "6225", "123", []string{"rack1", "rack2", "rack3", "rack4", "rack5", "rack6"}, ""},
  88. {topologyEc, "6226", "123", []string{"rack1", "rack2", "rack3", "rack4", "rack5", "rack6"}, ""},
  89. {topologyEc, "6241", "123", []string{"rack1", "rack2", "rack3", "rack4", "rack5", "rack6"}, ""},
  90. {topologyEc, "6242", "123", []string{"rack1", "rack2", "rack3", "rack4", "rack5", "rack6"}, ""},
  91. // EC volumes.
  92. {topologyEc, "9577", "", nil, "shards 1 >= replica placement limit for other racks (0)"},
  93. {topologyEc, "9577", "111", nil, "shards 1 >= replica placement limit for other racks (1)"},
  94. {topologyEc, "9577", "222", []string{"rack1", "rack2", "rack3"}, ""},
  95. {topologyEc, "10457", "222", []string{"rack1"}, ""},
  96. {topologyEc, "12737", "222", []string{"rack2"}, ""},
  97. {topologyEc, "14322", "222", []string{"rack3"}, ""},
  98. }
  99. for _, tc := range testCases {
  100. vid, _ := needle.NewVolumeId(tc.vid)
  101. ecNodes, _ := collectEcVolumeServersByDc(tc.topology, "")
  102. rp, _ := super_block.NewReplicaPlacementFromString(tc.replicaPlacement)
  103. ecb := &ecBalancer{
  104. ecNodes: ecNodes,
  105. replicaPlacement: rp,
  106. }
  107. racks := ecb.racks()
  108. rackToShardCount := countShardsByRack(vid, ecNodes)
  109. got, gotErr := ecb.pickRackToBalanceShardsInto(racks, rackToShardCount)
  110. if err := errorCheck(gotErr, tc.wantErr); err != nil {
  111. t.Errorf("volume %q: %s", tc.vid, err.Error())
  112. continue
  113. }
  114. if string(got) == "" && len(tc.wantOneOf) == 0 {
  115. continue
  116. }
  117. found := false
  118. for _, want := range tc.wantOneOf {
  119. if got := string(got); got == want {
  120. found = true
  121. break
  122. }
  123. }
  124. if !(found) {
  125. t.Errorf("expected one of %v for volume %q, got %q", tc.wantOneOf, tc.vid, got)
  126. }
  127. }
  128. }
  129. func TestPickEcNodeToBalanceShardsInto(t *testing.T) {
  130. testCases := []struct {
  131. topology *master_pb.TopologyInfo
  132. nodeId string
  133. vid string
  134. wantOneOf []string
  135. wantErr string
  136. }{
  137. {topologyEc, "", "", nil, "INTERNAL: missing source nodes"},
  138. {topologyEc, "idontexist", "12737", nil, "INTERNAL: missing source nodes"},
  139. // Non-EC nodes. We don't care about these, but the function should return all available target nodes as a safeguard.
  140. {
  141. topologyEc, "172.19.0.10:8702", "6225", []string{
  142. "172.19.0.13:8701", "172.19.0.14:8711", "172.19.0.16:8704", "172.19.0.17:8703",
  143. "172.19.0.19:8700", "172.19.0.20:8706", "172.19.0.21:8710", "172.19.0.3:8708",
  144. "172.19.0.4:8707", "172.19.0.5:8705", "172.19.0.6:8713", "172.19.0.8:8709",
  145. "172.19.0.9:8712"},
  146. "",
  147. },
  148. {
  149. topologyEc, "172.19.0.8:8709", "6226", []string{
  150. "172.19.0.10:8702", "172.19.0.13:8701", "172.19.0.14:8711", "172.19.0.16:8704",
  151. "172.19.0.17:8703", "172.19.0.19:8700", "172.19.0.20:8706", "172.19.0.21:8710",
  152. "172.19.0.3:8708", "172.19.0.4:8707", "172.19.0.5:8705", "172.19.0.6:8713",
  153. "172.19.0.9:8712"},
  154. "",
  155. },
  156. // EC volumes.
  157. {topologyEc, "172.19.0.10:8702", "14322", []string{
  158. "172.19.0.14:8711", "172.19.0.5:8705", "172.19.0.6:8713"},
  159. ""},
  160. {topologyEc, "172.19.0.13:8701", "10457", []string{
  161. "172.19.0.10:8702", "172.19.0.6:8713"},
  162. ""},
  163. {topologyEc, "172.19.0.17:8703", "12737", []string{
  164. "172.19.0.13:8701"},
  165. ""},
  166. {topologyEc, "172.19.0.20:8706", "14322", []string{
  167. "172.19.0.14:8711", "172.19.0.5:8705", "172.19.0.6:8713"},
  168. ""},
  169. }
  170. for _, tc := range testCases {
  171. vid, _ := needle.NewVolumeId(tc.vid)
  172. allEcNodes, _ := collectEcVolumeServersByDc(tc.topology, "")
  173. ecb := &ecBalancer{
  174. ecNodes: allEcNodes,
  175. }
  176. // Resolve target node by name
  177. var ecNode *EcNode
  178. for _, n := range allEcNodes {
  179. if n.info.Id == tc.nodeId {
  180. ecNode = n
  181. break
  182. }
  183. }
  184. got, gotErr := ecb.pickEcNodeToBalanceShardsInto(vid, ecNode, allEcNodes)
  185. if err := errorCheck(gotErr, tc.wantErr); err != nil {
  186. t.Errorf("node %q, volume %q: %s", tc.nodeId, tc.vid, err.Error())
  187. continue
  188. }
  189. if got == nil {
  190. if len(tc.wantOneOf) == 0 {
  191. continue
  192. }
  193. t.Errorf("node %q, volume %q: got no node, want %q", tc.nodeId, tc.vid, tc.wantOneOf)
  194. continue
  195. }
  196. found := false
  197. for _, want := range tc.wantOneOf {
  198. if got := got.info.Id; got == want {
  199. found = true
  200. break
  201. }
  202. }
  203. if !(found) {
  204. t.Errorf("expected one of %v for volume %q, got %q", tc.wantOneOf, tc.vid, got.info.Id)
  205. }
  206. }
  207. }