blockpullreorderer.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. // Copyright (C) 2020 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package model
  7. import (
  8. "github.com/syncthing/syncthing/lib/config"
  9. "github.com/syncthing/syncthing/lib/protocol"
  10. "github.com/syncthing/syncthing/lib/rand"
  11. "sort"
  12. )
  13. type blockPullReorderer interface {
  14. Reorder(blocks []protocol.BlockInfo) []protocol.BlockInfo
  15. }
  16. func newBlockPullReorderer(order config.BlockPullOrder, id protocol.DeviceID, otherDevices []protocol.DeviceID) blockPullReorderer {
  17. switch order {
  18. case config.BlockPullOrderRandom:
  19. return randomOrderBlockPullReorderer{}
  20. case config.BlockPullOrderInOrder:
  21. return inOrderBlockPullReorderer{}
  22. case config.BlockPullOrderStandard:
  23. fallthrough
  24. default:
  25. return newStandardBlockPullReorderer(id, otherDevices)
  26. }
  27. }
  28. type inOrderBlockPullReorderer struct{}
  29. func (inOrderBlockPullReorderer) Reorder(blocks []protocol.BlockInfo) []protocol.BlockInfo {
  30. return blocks
  31. }
  32. type randomOrderBlockPullReorderer struct{}
  33. func (randomOrderBlockPullReorderer) Reorder(blocks []protocol.BlockInfo) []protocol.BlockInfo {
  34. rand.Shuffle(blocks)
  35. return blocks
  36. }
  37. type standardBlockPullReorderer struct {
  38. myIndex int
  39. count int
  40. shuffle func(interface{}) // Used for test
  41. }
  42. func newStandardBlockPullReorderer(id protocol.DeviceID, otherDevices []protocol.DeviceID) *standardBlockPullReorderer {
  43. allDevices := append(otherDevices, id)
  44. sort.Slice(allDevices, func(i, j int) bool {
  45. return allDevices[i].Compare(allDevices[j]) == -1
  46. })
  47. // Find our index
  48. myIndex := -1
  49. for i, dev := range allDevices {
  50. if dev == id {
  51. myIndex = i
  52. break
  53. }
  54. }
  55. if myIndex < 0 {
  56. panic("bug: could not find my own index")
  57. }
  58. return &standardBlockPullReorderer{
  59. myIndex: myIndex,
  60. count: len(allDevices),
  61. shuffle: rand.Shuffle,
  62. }
  63. }
  64. func (p *standardBlockPullReorderer) Reorder(blocks []protocol.BlockInfo) []protocol.BlockInfo {
  65. if len(blocks) == 0 {
  66. return blocks
  67. }
  68. // Split the blocks into len(allDevices) chunks. Chunk count might be less than device count, if there are more
  69. // devices than blocks.
  70. chunks := chunk(blocks, p.count)
  71. newBlocks := make([]protocol.BlockInfo, 0, len(blocks))
  72. // First add our own chunk. We might fall off the list if there are more devices than chunks...
  73. if p.myIndex < len(chunks) {
  74. newBlocks = append(newBlocks, chunks[p.myIndex]...)
  75. }
  76. // The rest of the chunks we fetch in a random order in whole chunks.
  77. // Generate chunk index slice and shuffle it
  78. indexes := make([]int, 0, len(chunks)-1)
  79. for i := 0; i < len(chunks); i++ {
  80. if i != p.myIndex {
  81. indexes = append(indexes, i)
  82. }
  83. }
  84. p.shuffle(indexes)
  85. // Append the chunks in the order of the index slices.
  86. for _, idx := range indexes {
  87. newBlocks = append(newBlocks, chunks[idx]...)
  88. }
  89. return newBlocks
  90. }
  91. func chunk(blocks []protocol.BlockInfo, partCount int) [][]protocol.BlockInfo {
  92. if partCount == 0 {
  93. return [][]protocol.BlockInfo{blocks}
  94. }
  95. count := len(blocks)
  96. chunkSize := (count + partCount - 1) / partCount
  97. parts := make([][]protocol.BlockInfo, 0, partCount)
  98. for i := 0; i < count; i += chunkSize {
  99. end := i + chunkSize
  100. if end > count {
  101. end = count
  102. }
  103. parts = append(parts, blocks[i:end])
  104. }
  105. return parts
  106. }