123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- package hostpool
- import (
- "errors"
- "github.com/bmizerany/assert"
- "io/ioutil"
- "log"
- "math/rand"
- "os"
- "testing"
- "time"
- )
- func TestHostPool(t *testing.T) {
- log.SetOutput(ioutil.Discard)
- defer log.SetOutput(os.Stdout)
- dummyErr := errors.New("Dummy Error")
- p := New([]string{"a", "b", "c"})
- assert.Equal(t, p.Get().Host(), "a")
- assert.Equal(t, p.Get().Host(), "b")
- assert.Equal(t, p.Get().Host(), "c")
- respA := p.Get()
- assert.Equal(t, respA.Host(), "a")
- respA.Mark(dummyErr)
- respB := p.Get()
- respB.Mark(dummyErr)
- respC := p.Get()
- assert.Equal(t, respC.Host(), "c")
- respC.Mark(nil)
- // get again, and verify that it's still c
- assert.Equal(t, p.Get().Host(), "c")
- // now try to mark b as success; should fail because already marked
- respB.Mark(nil)
- assert.Equal(t, p.Get().Host(), "c") // would be b if it were not dead
- // now restore a
- respA = &standardHostPoolResponse{host: "a", pool: p}
- respA.Mark(nil)
- assert.Equal(t, p.Get().Host(), "a")
- assert.Equal(t, p.Get().Host(), "c")
- // ensure that we get *something* back when all hosts fail
- for _, host := range []string{"a", "b", "c"} {
- response := &standardHostPoolResponse{host: host, pool: p}
- response.Mark(dummyErr)
- }
- resp := p.Get()
- assert.NotEqual(t, resp, nil)
- }
- type mockTimer struct {
- t int // the time it will always return
- }
- func (t *mockTimer) between(start time.Time, end time.Time) time.Duration {
- return time.Duration(t.t) * time.Millisecond
- }
- func TestEpsilonGreedy(t *testing.T) {
- log.SetOutput(ioutil.Discard)
- defer log.SetOutput(os.Stdout)
- rand.Seed(10)
- iterations := 12000
- p := NewEpsilonGreedy([]string{"a", "b"}, 0, &LinearEpsilonValueCalculator{}).(*epsilonGreedyHostPool)
- timings := make(map[string]int64)
- timings["a"] = 200
- timings["b"] = 300
- hitCounts := make(map[string]int)
- hitCounts["a"] = 0
- hitCounts["b"] = 0
- log.Printf("starting first run (a, b)")
- for i := 0; i < iterations; i += 1 {
- if i != 0 && i%100 == 0 {
- p.performEpsilonGreedyDecay()
- }
- hostR := p.Get()
- host := hostR.Host()
- hitCounts[host]++
- timing := timings[host]
- p.timer = &mockTimer{t: int(timing)}
- hostR.Mark(nil)
- }
- for host := range hitCounts {
- log.Printf("host %s hit %d times (%0.2f percent)", host, hitCounts[host], (float64(hitCounts[host])/float64(iterations))*100.0)
- }
- assert.Equal(t, hitCounts["a"] > hitCounts["b"], true)
- hitCounts["a"] = 0
- hitCounts["b"] = 0
- log.Printf("starting second run (b, a)")
- timings["a"] = 500
- timings["b"] = 100
- for i := 0; i < iterations; i += 1 {
- if i != 0 && i%100 == 0 {
- p.performEpsilonGreedyDecay()
- }
- hostR := p.Get()
- host := hostR.Host()
- hitCounts[host]++
- timing := timings[host]
- p.timer = &mockTimer{t: int(timing)}
- hostR.Mark(nil)
- }
- for host := range hitCounts {
- log.Printf("host %s hit %d times (%0.2f percent)", host, hitCounts[host], (float64(hitCounts[host])/float64(iterations))*100.0)
- }
- assert.Equal(t, hitCounts["b"] > hitCounts["a"], true)
- }
- func BenchmarkEpsilonGreedy(b *testing.B) {
- b.StopTimer()
- // Make up some response times
- zipfDist := rand.NewZipf(rand.New(rand.NewSource(0)), 1.1, 5, 5000)
- timings := make([]uint64, b.N)
- for i := 0; i < b.N; i++ {
- timings[i] = zipfDist.Uint64()
- }
- // Make the hostpool with a few hosts
- p := NewEpsilonGreedy([]string{"a", "b"}, 0, &LinearEpsilonValueCalculator{}).(*epsilonGreedyHostPool)
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- if i != 0 && i%100 == 0 {
- p.performEpsilonGreedyDecay()
- }
- hostR := p.Get()
- p.timer = &mockTimer{t: int(timings[i])}
- hostR.Mark(nil)
- }
- }
|