Esempio n. 1
0
func TestRandomNoEndpoints(t *testing.T) {
	lb := loadbalancer.NewRandom(fixed.NewPublisher([]endpoint.Endpoint{}), 123)
	_, have := lb.Endpoint()
	if want := loadbalancer.ErrNoEndpoints; want != have {
		t.Errorf("want %q, have %q", 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(fixed.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 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(fixed.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. 4
0
func TestRetryMaxTotalFail(t *testing.T) {
	var (
		endpoints = []endpoint.Endpoint{} // no endpoints
		p         = fixed.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. 5
0
// NewPublisher returns a static endpoint Publisher.
func NewPublisher(instances []string, factory loadbalancer.Factory, logger log.Logger) Publisher {
	logger = log.NewContext(logger).With("component", "Fixed Publisher")
	endpoints := []endpoint.Endpoint{}
	for _, instance := range instances {
		e, err := factory(instance)
		if err != nil {
			_ = logger.Log("instance", instance, "err", err)
			continue
		}
		endpoints = append(endpoints, e)
	}
	return Publisher{fixed.NewPublisher(endpoints)}
}
Esempio n. 6
0
func TestFixed(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 := fixed.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       = fixed.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       = fixed.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 TestFixedReplace(t *testing.T) {
	p := fixed.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. 10
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(fixed.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)
	}
}