func consistenHashAlgorithmTest(t *testing.T, wg *sync.WaitGroup, id string) { inst := allAlgorithms[id]() upstreams := testutils.GetRandomUpstreams(10, 100) inst.Set(upstreams) urlsToTest := 3000 + rand.Intn(1000) mapping := map[string]string{} for i := 0; i < urlsToTest; i++ { url := testutils.GenerateMeAString(rand.Int63(), 5+rand.Int63n(100)) if res1, err := inst.Get(url); err != nil { t.Errorf("Unexpected error when getting url %s from algorithm %s: %s", url, id, err) } else if res2, err := inst.Get(url); err != nil { t.Errorf("Unexpected error when getting url %s from algorithm %s for the second time: %s", url, id, err) } else if !reflect.DeepEqual(res1, res2) { t.Errorf("The two results for url %s by algorithm %s are different: %#v and %#v", url, id, res1, res2) } else { mapping[url] = res1.Host } } oldWeght := getTotalWeight(upstreams) checkDeviation := func(newUpstreams []*types.UpstreamAddress) { inst.Set(newUpstreams) var sameCount float64 for url, oldHost := range mapping { if res, err := inst.Get(url); err != nil { t.Errorf("Unexpected error when getting url %s from algorithm %s: %s", url, id, err) } else if res.Host == oldHost { sameCount++ } } total := float64(len(mapping)) newWeght := getTotalWeight(newUpstreams) weightDiff := math.Abs(oldWeght - newWeght) if math.Abs(weightDiff/oldWeght-(total-sameCount)/total) > 0.25 { t.Errorf("[%s] Same count is %f of %f (count diff %f%%); upstreams are %d out of %d (weight diff %f-%f=%f or %f%%); deviation from expected: %f\n\n", id, sameCount, total, (total-sameCount)*100/total, len(newUpstreams), len(upstreams), oldWeght, newWeght, oldWeght-newWeght, weightDiff*100/oldWeght, math.Abs(weightDiff/oldWeght-(total-sameCount)/total)*100) } } // Add an extra server at the start newUpstream := testutils.GetUpstream(len(upstreams)) checkDeviation(append([]*types.UpstreamAddress{newUpstream}, upstreams...)) // Remove a random server randomServer := rand.Intn(len(upstreams)) checkDeviation(append(upstreams[:randomServer], upstreams[randomServer+1:]...)) // Add an extra server at that point checkDeviation(append(upstreams[:randomServer], append([]*types.UpstreamAddress{newUpstream}, upstreams[randomServer:]...)...)) // Add an extra server at the end checkDeviation(append(upstreams, newUpstream)) wg.Done() }
func TestBasicOperations(t *testing.T) { t.Parallel() for id, algo := range allAlgorithms { inst := algo() if res, err := inst.Get("bogus1"); err == nil { t.Errorf("Expected to receive error for unitialized algorithm %s but received %#v", id, res) } upstream := testutils.GetUpstream(rand.Int()) inst.Set([]*types.UpstreamAddress{upstream}) if res, err := inst.Get("bogus2"); err != nil { t.Errorf("Received an unexpected error for algorithm %s: %s", id, err) } else if !reflect.DeepEqual(res, upstream) { t.Errorf("Algorithm %s returned '%#v' when it should have returned '%#v'", id, res, upstream) } inst.Set([]*types.UpstreamAddress{}) if res, err := inst.Get("bogus3"); err == nil { t.Errorf("Expected to receive error for empty algorithm %s but received %#v", id, res) } } }
// This test will probably only be useful if `go test -race` is used func TestRandomConcurrentUsage(t *testing.T) { t.Parallel() wg := sync.WaitGroup{} randomlyTestAlgorithm := func(id string, inst types.UpstreamBalancingAlgorithm) { inst.Set([]*types.UpstreamAddress{testutils.GetUpstream(rand.Int())}) // Prevent expected errors setters := 50 + rand.Intn(200) wg.Add(setters) for i := 0; i < setters; i++ { go func() { time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) inst.Set(testutils.GetRandomUpstreams(1, 100)) wg.Done() }() } getters := 100 + rand.Intn(500) wg.Add(getters) for i := 0; i < getters; i++ { go func() { time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) path := fmt.Sprintf("some/path/%d.jpn", rand.Int()) if _, err := inst.Get(path); err != nil { t.Errorf("Unexpected algorithm %s error for path %s: %s", id, path, err) } wg.Done() }() } } for id, constructor := range allAlgorithms { randomlyTestAlgorithm(id, constructor()) } wg.Wait() }