hostpool_test.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. package hostpool
  2. import (
  3. "errors"
  4. "github.com/bmizerany/assert"
  5. "io/ioutil"
  6. "log"
  7. "math/rand"
  8. "os"
  9. "testing"
  10. "time"
  11. )
  12. func TestHostPool(t *testing.T) {
  13. log.SetOutput(ioutil.Discard)
  14. defer log.SetOutput(os.Stdout)
  15. dummyErr := errors.New("Dummy Error")
  16. p := New([]string{"a", "b", "c"})
  17. assert.Equal(t, p.Get().Host(), "a")
  18. assert.Equal(t, p.Get().Host(), "b")
  19. assert.Equal(t, p.Get().Host(), "c")
  20. respA := p.Get()
  21. assert.Equal(t, respA.Host(), "a")
  22. respA.Mark(dummyErr)
  23. respB := p.Get()
  24. respB.Mark(dummyErr)
  25. respC := p.Get()
  26. assert.Equal(t, respC.Host(), "c")
  27. respC.Mark(nil)
  28. // get again, and verify that it's still c
  29. assert.Equal(t, p.Get().Host(), "c")
  30. // now try to mark b as success; should fail because already marked
  31. respB.Mark(nil)
  32. assert.Equal(t, p.Get().Host(), "c") // would be b if it were not dead
  33. // now restore a
  34. respA = &standardHostPoolResponse{host: "a", pool: p}
  35. respA.Mark(nil)
  36. assert.Equal(t, p.Get().Host(), "a")
  37. assert.Equal(t, p.Get().Host(), "c")
  38. // ensure that we get *something* back when all hosts fail
  39. for _, host := range []string{"a", "b", "c"} {
  40. response := &standardHostPoolResponse{host: host, pool: p}
  41. response.Mark(dummyErr)
  42. }
  43. resp := p.Get()
  44. assert.NotEqual(t, resp, nil)
  45. }
  46. type mockTimer struct {
  47. t int // the time it will always return
  48. }
  49. func (t *mockTimer) between(start time.Time, end time.Time) time.Duration {
  50. return time.Duration(t.t) * time.Millisecond
  51. }
  52. func TestEpsilonGreedy(t *testing.T) {
  53. log.SetOutput(ioutil.Discard)
  54. defer log.SetOutput(os.Stdout)
  55. rand.Seed(10)
  56. iterations := 12000
  57. p := NewEpsilonGreedy([]string{"a", "b"}, 0, &LinearEpsilonValueCalculator{}).(*epsilonGreedyHostPool)
  58. timings := make(map[string]int64)
  59. timings["a"] = 200
  60. timings["b"] = 300
  61. hitCounts := make(map[string]int)
  62. hitCounts["a"] = 0
  63. hitCounts["b"] = 0
  64. log.Printf("starting first run (a, b)")
  65. for i := 0; i < iterations; i += 1 {
  66. if i != 0 && i%100 == 0 {
  67. for _, h := range p.hosts {
  68. h.performDecay()
  69. }
  70. }
  71. hostR := p.Get()
  72. host := hostR.Host()
  73. hitCounts[host]++
  74. timing := timings[host]
  75. p.timer = &mockTimer{t: int(timing)}
  76. hostR.Mark(nil)
  77. }
  78. for host, _ := range hitCounts {
  79. log.Printf("host %s hit %d times (%0.2f percent)", host, hitCounts[host], (float64(hitCounts[host])/float64(iterations))*100.0)
  80. }
  81. assert.Equal(t, hitCounts["a"] > hitCounts["b"], true)
  82. hitCounts["a"] = 0
  83. hitCounts["b"] = 0
  84. log.Printf("starting second run (b, a)")
  85. timings["a"] = 500
  86. timings["b"] = 100
  87. for i := 0; i < iterations; i += 1 {
  88. if i != 0 && i%100 == 0 {
  89. for _, h := range p.hosts {
  90. h.performDecay()
  91. }
  92. }
  93. hostR := p.Get()
  94. host := hostR.Host()
  95. hitCounts[host]++
  96. timing := timings[host]
  97. p.timer = &mockTimer{t: int(timing)}
  98. hostR.Mark(nil)
  99. }
  100. for host, _ := range hitCounts {
  101. log.Printf("host %s hit %d times (%0.2f percent)", host, hitCounts[host], (float64(hitCounts[host])/float64(iterations))*100.0)
  102. }
  103. assert.Equal(t, hitCounts["b"] > hitCounts["a"], true)
  104. }
  105. func BenchmarkEpsilonGreedy(b *testing.B) {
  106. b.StopTimer()
  107. // Make up some response times
  108. zipfDist := rand.NewZipf(rand.New(rand.NewSource(0)), 1.1, 5, 5000)
  109. timings := make([]uint64, b.N)
  110. for i := 0; i < b.N; i++ {
  111. timings[i] = zipfDist.Uint64()
  112. }
  113. // Make the hostpool with a few hosts
  114. p := NewEpsilonGreedy([]string{"a", "b"}, 0, &LinearEpsilonValueCalculator{}).(*epsilonGreedyHostPool)
  115. b.StartTimer()
  116. for i := 0; i < b.N; i++ {
  117. if i != 0 && i%100 == 0 {
  118. p.performEpsilonGreedyDecay()
  119. }
  120. hostR := p.Get()
  121. p.timer = &mockTimer{t: int(timings[i])}
  122. hostR.Mark(nil)
  123. }
  124. }