Avoid out of index error in failover (#951)

Fixes panic when using the load balancing client.
This commit is contained in:
Kishan Sagathiya 2019-11-13 16:45:29 +05:30 committed by Hector Sanjuan
parent 005bd9e2d8
commit a178e5988a
2 changed files with 50 additions and 5 deletions

View File

@ -24,7 +24,8 @@ type LBStrategy interface {
SetClients(clients []Client) SetClients(clients []Client)
} }
// RoundRobin is a load balancing strategy that would use clients in a sequence. // RoundRobin is a load balancing strategy that would use clients in a sequence
// for all methods, throughout the lifetime of the lb client.
type RoundRobin struct { type RoundRobin struct {
clients []Client clients []Client
counter uint32 counter uint32
@ -44,15 +45,16 @@ func (r *RoundRobin) SetClients(cl []Client) {
r.length = uint32(len(cl)) r.length = uint32(len(cl))
} }
// Failover is a load balancing strategy that would try the local cluster peer first. // Failover is a load balancing strategy that would try the first cluster peer
// If the local call fail it would try other client in a round robin fashion. // first. If the first call fails it would try other clients for that call in a
// round robin fashion.
type Failover struct { type Failover struct {
clients []Client clients []Client
} }
// Next returns the next client to be used. // Next returns the next client to be used.
func (f *Failover) Next(count int) Client { func (f *Failover) Next(count int) Client {
return f.clients[count] return f.clients[count%len(f.clients)]
} }
// SetClients sets a list of clients for this strategy. // SetClients sets a list of clients for this strategy.

View File

@ -2,13 +2,15 @@ package client
import ( import (
"context" "context"
"fmt"
"sync" "sync"
"testing" "testing"
"github.com/ipfs/ipfs-cluster/api"
ma "github.com/multiformats/go-multiaddr" ma "github.com/multiformats/go-multiaddr"
) )
func TestLBClient(t *testing.T) { func TestFailoverConcurrently(t *testing.T) {
// Create a load balancing client with 5 empty clients and 5 clients with APIs // Create a load balancing client with 5 empty clients and 5 clients with APIs
// say we want to retry the request for at most 5 times // say we want to retry the request for at most 5 times
cfgs := make([]*Config, 10) cfgs := make([]*Config, 10)
@ -39,6 +41,47 @@ func TestLBClient(t *testing.T) {
testRunManyRequestsConcurrently(t, cfgs, &Failover{}, 200, 5, false) testRunManyRequestsConcurrently(t, cfgs, &Failover{}, 200, 5, false)
} }
type dummyClient struct {
defaultClient
i int
}
// ID returns dummy client's serial number.
func (d *dummyClient) ID(ctx context.Context) (*api.ID, error) {
return &api.ID{
Peername: fmt.Sprintf("%d", d.i),
}, nil
}
func TestRoundRobin(t *testing.T) {
var clients []Client
// number of clients
n := 5
// create n dummy clients
for i := 0; i < n; i++ {
c := &dummyClient{
i: i,
}
clients = append(clients, c)
}
roundRobin := loadBalancingClient{
strategy: &RoundRobin{
clients: clients,
length: uint32(len(clients)),
},
}
// clients should be used in the sequence 1, 2,.., 4, 0.
for i := 0; i < n; i++ {
id, _ := roundRobin.ID(context.Background())
if id.Peername != fmt.Sprintf("%d", (i+1)%n) {
t.Errorf("clients are not being tried in sequence, expected client: %d, but found: %s", i, id.Peername)
}
}
}
func testRunManyRequestsConcurrently(t *testing.T, cfgs []*Config, strategy LBStrategy, requests int, retries int, pass bool) { func testRunManyRequestsConcurrently(t *testing.T, cfgs []*Config, strategy LBStrategy, requests int, retries int, pass bool) {
c, err := NewLBClient(strategy, cfgs, retries) c, err := NewLBClient(strategy, cfgs, retries)
if err != nil { if err != nil {