hostpool_test.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  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. p.Get() // takes another go-round for it to update itself, will just choose c again for now
  37. assert.Equal(t, p.Get().Host(), "a")
  38. assert.Equal(t, p.Get().Host(), "c")
  39. // ensure that we get *something* back when all hosts fail
  40. for _, host := range []string{"a", "b", "c"} {
  41. response := &standardHostPoolResponse{host: host, pool: p}
  42. response.Mark(dummyErr)
  43. }
  44. resp := p.Get()
  45. assert.NotEqual(t, resp, nil)
  46. }
  47. type mockTimer struct {
  48. t int // the time it will always return
  49. }
  50. func (t *mockTimer) between(start time.Time, end time.Time) time.Duration {
  51. return time.Duration(t.t) * time.Millisecond
  52. }
  53. func TestEpsilonGreedy(t *testing.T) {
  54. log.SetOutput(ioutil.Discard)
  55. defer log.SetOutput(os.Stdout)
  56. rand.Seed(10)
  57. iterations := 12000
  58. p := NewEpsilonGreedy([]string{"a", "b"}, 0, &LinearEpsilonValueCalculator{}).(*epsilonGreedyHostPool)
  59. timings := make(map[string]int64)
  60. timings["a"] = 200
  61. timings["b"] = 300
  62. hitCounts := make(map[string]int)
  63. hitCounts["a"] = 0
  64. hitCounts["b"] = 0
  65. log.Printf("starting first run (a, b)")
  66. for i := 0; i < iterations; i += 1 {
  67. if i != 0 && i%100 == 0 {
  68. for _, h := range p.hosts {
  69. h.performDecay()
  70. }
  71. }
  72. hostR := p.Get()
  73. host := hostR.Host()
  74. hitCounts[host]++
  75. timing := timings[host]
  76. p.timer = &mockTimer{t: int(timing)}
  77. hostR.Mark(nil)
  78. }
  79. for host, _ := range hitCounts {
  80. log.Printf("host %s hit %d times (%0.2f percent)", host, hitCounts[host], (float64(hitCounts[host])/float64(iterations))*100.0)
  81. }
  82. assert.Equal(t, hitCounts["a"] > hitCounts["b"], true)
  83. hitCounts["a"] = 0
  84. hitCounts["b"] = 0
  85. log.Printf("starting second run (b, a)")
  86. timings["a"] = 500
  87. timings["b"] = 100
  88. for i := 0; i < iterations; i += 1 {
  89. if i != 0 && i%100 == 0 {
  90. for _, h := range p.hosts {
  91. h.performDecay()
  92. }
  93. }
  94. hostR := p.Get()
  95. host := hostR.Host()
  96. hitCounts[host]++
  97. timing := timings[host]
  98. p.timer = &mockTimer{t: int(timing)}
  99. hostR.Mark(nil)
  100. }
  101. for host, _ := range hitCounts {
  102. log.Printf("host %s hit %d times (%0.2f percent)", host, hitCounts[host], (float64(hitCounts[host])/float64(iterations))*100.0)
  103. }
  104. assert.Equal(t, hitCounts["b"] > hitCounts["a"], true)
  105. }
  106. func BenchmarkEpsilonGreedy(b *testing.B) {
  107. b.StopTimer()
  108. // Make up some response times
  109. zipfDist := rand.NewZipf(rand.New(rand.NewSource(0)), 1.1, 5, 5000)
  110. timings := make([]uint64, b.N)
  111. for i := 0; i < b.N; i++ {
  112. timings[i] = zipfDist.Uint64()
  113. }
  114. // Make the hostpool with a few hosts
  115. p := NewEpsilonGreedy([]string{"a", "b"}, 0, &LinearEpsilonValueCalculator{}).(*epsilonGreedyHostPool)
  116. b.StartTimer()
  117. for i := 0; i < b.N; i++ {
  118. if i != 0 && i%100 == 0 {
  119. for _, h := range p.hosts {
  120. h.performDecay()
  121. }
  122. }
  123. hostR := p.Get()
  124. p.timer = &mockTimer{t: int(timings[i])}
  125. hostR.Mark(nil)
  126. }
  127. }