Esempio n. 1
1
func TestStatic(t *testing.T) {
	var (
		instances = []string{"foo", "bar", "baz"}
		endpoints = map[string]endpoint.Endpoint{
			"foo": func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil },
			"bar": func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil },
			"baz": func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil },
		}
		factory = func(instance string) (endpoint.Endpoint, error) {
			if e, ok := endpoints[instance]; ok {
				return e, nil
			}
			return nil, fmt.Errorf("%s: not found", instance)
		}
	)
	p := static.NewPublisher(instances, factory, log.NewNopLogger())
	have, err := p.Endpoints()
	if err != nil {
		t.Fatal(err)
	}
	want := []endpoint.Endpoint{endpoints["foo"], endpoints["bar"], endpoints["baz"]}
	if fmt.Sprint(want) != fmt.Sprint(have) {
		t.Fatalf("want %v, have %v", want, have)
	}
}
Esempio n. 2
0
func TestRoundRobinDistribution(t *testing.T) {
	var (
		ctx       = context.Background()
		counts    = []int{0, 0, 0}
		endpoints = []endpoint.Endpoint{
			func(context.Context, interface{}) (interface{}, error) { counts[0]++; return struct{}{}, nil },
			func(context.Context, interface{}) (interface{}, error) { counts[1]++; return struct{}{}, nil },
			func(context.Context, interface{}) (interface{}, error) { counts[2]++; return struct{}{}, nil },
		}
	)

	lb := loadbalancer.NewRoundRobin(static.NewPublisher(endpoints))

	for i, want := range [][]int{
		{1, 0, 0},
		{1, 1, 0},
		{1, 1, 1},
		{2, 1, 1},
		{2, 2, 1},
		{2, 2, 2},
		{3, 2, 2},
	} {
		e, err := lb.Endpoint()
		if err != nil {
			t.Fatal(err)
		}
		e(ctx, struct{}{})
		if have := counts; !reflect.DeepEqual(want, have) {
			t.Fatalf("%d: want %v, have %v", i, want, have)
		}

	}
}
Esempio n. 3
0
func TestRandomNoEndpoints(t *testing.T) {
	lb := loadbalancer.NewRandom(static.NewPublisher([]endpoint.Endpoint{}), 123)
	_, have := lb.Endpoint()
	if want := loadbalancer.ErrNoEndpoints; want != have {
		t.Errorf("want %q, have %q", want, have)
	}
}
Esempio n. 4
0
func TestRandomDistribution(t *testing.T) {
	var (
		n          = 3
		endpoints  = make([]endpoint.Endpoint, n)
		counts     = make([]int, n)
		seed       = int64(123)
		ctx        = context.Background()
		iterations = 100000
		want       = iterations / n
		tolerance  = want / 100 // 1%
	)

	for i := 0; i < n; i++ {
		i0 := i
		endpoints[i] = func(context.Context, interface{}) (interface{}, error) { counts[i0]++; return struct{}{}, nil }
	}

	lb := loadbalancer.NewRandom(static.NewPublisher(endpoints), seed)

	for i := 0; i < iterations; i++ {
		e, err := lb.Endpoint()
		if err != nil {
			t.Fatal(err)
		}
		e(ctx, struct{}{})
	}

	for i, have := range counts {
		if math.Abs(float64(want-have)) > float64(tolerance) {
			t.Errorf("%d: want %d, have %d", i, want, have)
		}
	}
}
Esempio n. 5
0
func TestRetryMaxTotalFail(t *testing.T) {
	var (
		endpoints = []endpoint.Endpoint{} // no endpoints
		p         = static.NewPublisher(endpoints)
		lb        = loadbalancer.NewRoundRobin(p)
		retry     = loadbalancer.Retry(999, time.Second, lb) // lots of retries
		ctx       = context.Background()
	)
	if _, err := retry(ctx, struct{}{}); err == nil {
		t.Errorf("expected error, got none") // should fail
	}
}
Esempio n. 6
0
func TestStatic(t *testing.T) {
	var (
		e1        = func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil }
		e2        = func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil }
		endpoints = []endpoint.Endpoint{e1, e2}
	)
	p := static.NewPublisher(endpoints)
	have, err := p.Endpoints()
	if err != nil {
		t.Fatal(err)
	}
	if want := endpoints; !reflect.DeepEqual(want, have) {
		t.Fatalf("want %#+v, have %#+v", want, have)
	}
}
Esempio n. 7
0
func TestRetryMaxSuccess(t *testing.T) {
	var (
		endpoints = []endpoint.Endpoint{
			func(context.Context, interface{}) (interface{}, error) { return nil, errors.New("error one") },
			func(context.Context, interface{}) (interface{}, error) { return nil, errors.New("error two") },
			func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil /* OK */ },
		}
		retries = len(endpoints) // exactly enough retries
		p       = static.NewPublisher(endpoints)
		lb      = loadbalancer.NewRoundRobin(p)
		ctx     = context.Background()
	)
	if _, err := loadbalancer.Retry(retries, time.Second, lb)(ctx, struct{}{}); err != nil {
		t.Error(err)
	}
}
Esempio n. 8
0
func TestRetryMaxPartialFail(t *testing.T) {
	var (
		endpoints = []endpoint.Endpoint{
			func(context.Context, interface{}) (interface{}, error) { return nil, errors.New("error one") },
			func(context.Context, interface{}) (interface{}, error) { return nil, errors.New("error two") },
			func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil /* OK */ },
		}
		retries = len(endpoints) - 1 // not quite enough retries
		p       = static.NewPublisher(endpoints)
		lb      = loadbalancer.NewRoundRobin(p)
		ctx     = context.Background()
	)
	if _, err := loadbalancer.Retry(retries, time.Second, lb)(ctx, struct{}{}); err == nil {
		t.Errorf("expected error, got none")
	}
}
Esempio n. 9
0
func proxyingMiddleware(proxyList string, ctx context.Context, logger log.Logger) ServiceMiddleware {
	if proxyList == "" {
		_ = logger.Log("proxy_to", "none")
		return func(next StringService) StringService { return next }
	}
	proxies := split(proxyList)
	_ = logger.Log("proxy_to", fmt.Sprint(proxies))

	return func(next StringService) StringService {
		var (
			qps         = 100 // max to each instance
			publisher   = static.NewPublisher(proxies, factory(ctx, qps), logger)
			lb          = loadbalancer.NewRoundRobin(publisher)
			maxAttempts = 3
			maxTime     = 100 * time.Millisecond
			endpoint    = loadbalancer.Retry(maxAttempts, maxTime, lb)
		)
		return proxymw{ctx, endpoint, next}
	}
}
Esempio n. 10
0
func TestStaticReplace(t *testing.T) {
	p := static.NewPublisher([]endpoint.Endpoint{
		func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil },
	})
	have, err := p.Endpoints()
	if err != nil {
		t.Fatal(err)
	}
	if want, have := 1, len(have); want != have {
		t.Fatalf("want %d, have %d", want, have)
	}
	p.Replace([]endpoint.Endpoint{})
	have, err = p.Endpoints()
	if err != nil {
		t.Fatal(err)
	}
	if want, have := 0, len(have); want != have {
		t.Fatalf("want %d, have %d", want, have)
	}
}
Esempio n. 11
0
func TestRetryTimeout(t *testing.T) {
	var (
		step    = make(chan struct{})
		e       = func(context.Context, interface{}) (interface{}, error) { <-step; return struct{}{}, nil }
		timeout = time.Millisecond
		retry   = loadbalancer.Retry(999, timeout, loadbalancer.NewRoundRobin(static.NewPublisher([]endpoint.Endpoint{e})))
		errs    = make(chan error, 1)
		invoke  = func() { _, err := retry(context.Background(), struct{}{}); errs <- err }
	)

	go func() { step <- struct{}{} }() // queue up a flush of the endpoint
	invoke()                           // invoke the endpoint and trigger the flush
	if err := <-errs; err != nil {     // that should succeed
		t.Error(err)
	}

	go func() { time.Sleep(10 * timeout); step <- struct{}{} }() // a delayed flush
	invoke()                                                     // invoke the endpoint
	if err := <-errs; err != context.DeadlineExceeded {          // that should not succeed
		t.Errorf("wanted %v, got none", context.DeadlineExceeded)
	}
}
Esempio n. 12
0
File: main.go Progetto: qband/down
func buildEndpoint(instances []string, factory loadbalancer.Factory, seed int64, logger log.Logger) endpoint.Endpoint {
	publisher := static.NewPublisher(instances, factory, logger)
	random := loadbalancer.NewRandom(publisher, seed)
	return loadbalancer.Retry(10, 10*time.Second, random)
}
Esempio n. 13
0
func buildEndpoint(tracer opentracing.Tracer, operationName string, instances []string, factory loadbalancer.Factory, seed int64, logger log.Logger) endpoint.Endpoint {
	publisher := static.NewPublisher(instances, factory, logger)
	random := loadbalancer.NewRandom(publisher, seed)
	endpoint := loadbalancer.Retry(10, 10*time.Second, random)
	return kitot.TraceClient(tracer, operationName)(endpoint)
}