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.

208 lines
5.5 KiB

  1. package p2p
  2. import (
  3. "math"
  4. "math/rand"
  5. "testing"
  6. "time"
  7. gogotypes "github.com/gogo/protobuf/types"
  8. "github.com/stretchr/testify/require"
  9. tmsync "github.com/tendermint/tendermint/internal/libs/sync"
  10. "github.com/tendermint/tendermint/libs/log"
  11. )
  12. type testMessage = gogotypes.StringValue
  13. func TestWDRRQueue_EqualWeights(t *testing.T) {
  14. chDescs := []ChannelDescriptor{
  15. {ID: 0x01, Priority: 1, MaxSendBytes: 4},
  16. {ID: 0x02, Priority: 1, MaxSendBytes: 4},
  17. {ID: 0x03, Priority: 1, MaxSendBytes: 4},
  18. {ID: 0x04, Priority: 1, MaxSendBytes: 4},
  19. {ID: 0x05, Priority: 1, MaxSendBytes: 4},
  20. {ID: 0x06, Priority: 1, MaxSendBytes: 4},
  21. }
  22. peerQueue := newWDRRScheduler(log.NewNopLogger(), NopMetrics(), chDescs, 1000, 1000, 120)
  23. peerQueue.start()
  24. totalMsgs := make(map[ChannelID]int)
  25. deliveredMsgs := make(map[ChannelID]int)
  26. successRates := make(map[ChannelID]float64)
  27. closer := tmsync.NewCloser()
  28. go func() {
  29. timout := 10 * time.Second
  30. ticker := time.NewTicker(timout)
  31. defer ticker.Stop()
  32. for {
  33. select {
  34. case e := <-peerQueue.dequeue():
  35. deliveredMsgs[e.channelID]++
  36. ticker.Reset(timout)
  37. case <-ticker.C:
  38. closer.Close()
  39. }
  40. }
  41. }()
  42. rng := rand.New(rand.NewSource(time.Now().UnixNano()))
  43. maxMsgs := 5000
  44. minMsgs := 1000
  45. for _, chDesc := range chDescs {
  46. total := rng.Intn(maxMsgs-minMsgs) + minMsgs // total = rand[minMsgs, maxMsgs)
  47. totalMsgs[ChannelID(chDesc.ID)] = total
  48. go func(cID ChannelID, n int) {
  49. for i := 0; i < n; i++ {
  50. peerQueue.enqueue() <- Envelope{
  51. channelID: cID,
  52. Message: &testMessage{Value: "foo"}, // 5 bytes
  53. }
  54. }
  55. }(ChannelID(chDesc.ID), total)
  56. }
  57. // wait for dequeueing to complete
  58. <-closer.Done()
  59. // close queue and wait for cleanup
  60. peerQueue.close()
  61. <-peerQueue.closed()
  62. var (
  63. sum float64
  64. stdDev float64
  65. )
  66. for _, chDesc := range peerQueue.chDescs {
  67. chID := ChannelID(chDesc.ID)
  68. require.Zero(t, peerQueue.deficits[chID], "expected flow deficit to be zero")
  69. require.Len(t, peerQueue.buffer[chID], 0, "expected flow queue to be empty")
  70. total := totalMsgs[chID]
  71. delivered := deliveredMsgs[chID]
  72. successRate := float64(delivered) / float64(total)
  73. sum += successRate
  74. successRates[chID] = successRate
  75. // require some messages dropped
  76. require.Less(t, delivered, total, "expected some messages to be dropped")
  77. require.Less(t, successRate, 1.0, "expected a success rate below 100%")
  78. }
  79. require.Zero(t, peerQueue.size, "expected scheduler size to be zero")
  80. numFlows := float64(len(peerQueue.buffer))
  81. mean := sum / numFlows
  82. for _, successRate := range successRates {
  83. stdDev += math.Pow(successRate-mean, 2)
  84. }
  85. stdDev = math.Sqrt(stdDev / numFlows)
  86. require.Less(t, stdDev, 0.02, "expected success rate standard deviation to be less than 2%")
  87. }
  88. func TestWDRRQueue_DecreasingWeights(t *testing.T) {
  89. chDescs := []ChannelDescriptor{
  90. {ID: 0x01, Priority: 18, MaxSendBytes: 4},
  91. {ID: 0x02, Priority: 10, MaxSendBytes: 4},
  92. {ID: 0x03, Priority: 2, MaxSendBytes: 4},
  93. {ID: 0x04, Priority: 1, MaxSendBytes: 4},
  94. {ID: 0x05, Priority: 1, MaxSendBytes: 4},
  95. {ID: 0x06, Priority: 1, MaxSendBytes: 4},
  96. }
  97. peerQueue := newWDRRScheduler(log.NewNopLogger(), NopMetrics(), chDescs, 0, 0, 500)
  98. peerQueue.start()
  99. totalMsgs := make(map[ChannelID]int)
  100. deliveredMsgs := make(map[ChannelID]int)
  101. successRates := make(map[ChannelID]float64)
  102. for _, chDesc := range chDescs {
  103. total := 1000
  104. totalMsgs[ChannelID(chDesc.ID)] = total
  105. go func(cID ChannelID, n int) {
  106. for i := 0; i < n; i++ {
  107. peerQueue.enqueue() <- Envelope{
  108. channelID: cID,
  109. Message: &testMessage{Value: "foo"}, // 5 bytes
  110. }
  111. }
  112. }(ChannelID(chDesc.ID), total)
  113. }
  114. closer := tmsync.NewCloser()
  115. go func() {
  116. timout := 20 * time.Second
  117. ticker := time.NewTicker(timout)
  118. defer ticker.Stop()
  119. for {
  120. select {
  121. case e := <-peerQueue.dequeue():
  122. deliveredMsgs[e.channelID]++
  123. ticker.Reset(timout)
  124. case <-ticker.C:
  125. closer.Close()
  126. }
  127. }
  128. }()
  129. // wait for dequeueing to complete
  130. <-closer.Done()
  131. // close queue and wait for cleanup
  132. peerQueue.close()
  133. <-peerQueue.closed()
  134. for i, chDesc := range peerQueue.chDescs {
  135. chID := ChannelID(chDesc.ID)
  136. require.Zero(t, peerQueue.deficits[chID], "expected flow deficit to be zero")
  137. require.Len(t, peerQueue.buffer[chID], 0, "expected flow queue to be empty")
  138. total := totalMsgs[chID]
  139. delivered := deliveredMsgs[chID]
  140. successRate := float64(delivered) / float64(total)
  141. successRates[chID] = successRate
  142. // Require some messages dropped. Note, the top weighted flows may not have
  143. // any dropped if lower priority non-empty queues always exist.
  144. if i > 2 {
  145. require.Less(t, delivered, total, "expected some messages to be dropped")
  146. require.Less(t, successRate, 1.0, "expected a success rate below 100%")
  147. }
  148. }
  149. require.Zero(t, peerQueue.size, "expected scheduler size to be zero")
  150. // require channel 0x01 to have the highest success rate due to its weight
  151. ch01Rate := successRates[ChannelID(chDescs[0].ID)]
  152. for i := 1; i < len(chDescs); i++ {
  153. require.GreaterOrEqual(t, ch01Rate, successRates[ChannelID(chDescs[i].ID)])
  154. }
  155. // require channel 0x02 to have the 2nd highest success rate due to its weight
  156. ch02Rate := successRates[ChannelID(chDescs[1].ID)]
  157. for i := 2; i < len(chDescs); i++ {
  158. require.GreaterOrEqual(t, ch02Rate, successRates[ChannelID(chDescs[i].ID)])
  159. }
  160. // require channel 0x03 to have the 3rd highest success rate due to its weight
  161. ch03Rate := successRates[ChannelID(chDescs[2].ID)]
  162. for i := 3; i < len(chDescs); i++ {
  163. require.GreaterOrEqual(t, ch03Rate, successRates[ChannelID(chDescs[i].ID)])
  164. }
  165. }