2019-07-19 13:17:51 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2019-11-13 11:15:29 +00:00
|
|
|
"fmt"
|
2019-07-19 13:17:51 +00:00
|
|
|
"sync"
|
|
|
|
"testing"
|
|
|
|
|
2022-06-15 09:19:17 +00:00
|
|
|
"github.com/ipfs-cluster/ipfs-cluster/api"
|
2019-07-19 13:17:51 +00:00
|
|
|
ma "github.com/multiformats/go-multiaddr"
|
|
|
|
)
|
|
|
|
|
2019-11-13 11:15:29 +00:00
|
|
|
func TestFailoverConcurrently(t *testing.T) {
|
2019-07-19 13:17:51 +00:00
|
|
|
// 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
|
|
|
|
cfgs := make([]*Config, 10)
|
|
|
|
|
2019-11-07 19:19:07 +00:00
|
|
|
// 5 clients with an invalid api address
|
2019-07-19 13:17:51 +00:00
|
|
|
for i := 0; i < 5; i++ {
|
2019-11-07 19:19:07 +00:00
|
|
|
maddr, _ := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
|
2019-07-19 13:17:51 +00:00
|
|
|
cfgs[i] = &Config{
|
|
|
|
APIAddr: maddr,
|
|
|
|
DisableKeepAlives: true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 5 clients with APIs
|
|
|
|
for i := 5; i < 10; i++ {
|
|
|
|
cfgs[i] = &Config{
|
|
|
|
APIAddr: apiMAddr(testAPI(t)),
|
|
|
|
DisableKeepAlives: true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run many requests at the same time
|
|
|
|
|
|
|
|
// With Failover strategy, it would go through first 5 empty clients
|
|
|
|
// and then 6th working client. Thus, all requests should always succeed.
|
Improve pin/unpin method signatures (#843)
* Improve pin/unpin method signatures:
These changes the following Cluster Go API methods:
* -> Cluster.Pin(ctx, cid, options) (pin, error)
* -> Cluster.Unpin(ctx, cid) (pin, error)
* -> Cluster.PinPath(ctx, path, opts) (pin,error)
Pin and Unpin now return the pinned object.
The signature of the methods now matches that of the API Client, is clearer as
to what options the user can set and is aligned with PinPath, UnpinPath, which
returned pin methods.
The REST API now returns the Pinned/Unpinned object rather than 204-Accepted.
This was necessary for a cleaner pin/update approach, which I'm working on in
another branch.
Most of the changes here are updating tests to the new signatures
* Adapt load-balancing client to new Pin/Unpin signatures
* cluster.go: Fix typo
Co-Authored-By: Kishan Sagathiya <kishansagathiya@gmail.com>
* cluster.go: Fix typo
Co-Authored-By: Kishan Sagathiya <kishansagathiya@gmail.com>
2019-07-22 13:39:11 +00:00
|
|
|
testRunManyRequestsConcurrently(t, cfgs, &Failover{}, 200, 6, true)
|
2019-07-19 13:17:51 +00:00
|
|
|
// First 5 clients are empty. Thus, all requests should fail.
|
Improve pin/unpin method signatures (#843)
* Improve pin/unpin method signatures:
These changes the following Cluster Go API methods:
* -> Cluster.Pin(ctx, cid, options) (pin, error)
* -> Cluster.Unpin(ctx, cid) (pin, error)
* -> Cluster.PinPath(ctx, path, opts) (pin,error)
Pin and Unpin now return the pinned object.
The signature of the methods now matches that of the API Client, is clearer as
to what options the user can set and is aligned with PinPath, UnpinPath, which
returned pin methods.
The REST API now returns the Pinned/Unpinned object rather than 204-Accepted.
This was necessary for a cleaner pin/update approach, which I'm working on in
another branch.
Most of the changes here are updating tests to the new signatures
* Adapt load-balancing client to new Pin/Unpin signatures
* cluster.go: Fix typo
Co-Authored-By: Kishan Sagathiya <kishansagathiya@gmail.com>
* cluster.go: Fix typo
Co-Authored-By: Kishan Sagathiya <kishansagathiya@gmail.com>
2019-07-22 13:39:11 +00:00
|
|
|
testRunManyRequestsConcurrently(t, cfgs, &Failover{}, 200, 5, false)
|
2019-07-19 13:17:51 +00:00
|
|
|
}
|
|
|
|
|
2019-11-13 11:15:29 +00:00
|
|
|
type dummyClient struct {
|
|
|
|
defaultClient
|
|
|
|
i int
|
|
|
|
}
|
|
|
|
|
|
|
|
// ID returns dummy client's serial number.
|
2022-03-19 01:52:46 +00:00
|
|
|
func (d *dummyClient) ID(ctx context.Context) (api.ID, error) {
|
|
|
|
return api.ID{
|
2019-11-13 11:15:29 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-07-19 13:17:51 +00:00
|
|
|
func testRunManyRequestsConcurrently(t *testing.T, cfgs []*Config, strategy LBStrategy, requests int, retries int, pass bool) {
|
|
|
|
c, err := NewLBClient(strategy, cfgs, retries)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
for i := 0; i < requests; i++ {
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
ctx := context.Background()
|
2019-11-07 19:19:07 +00:00
|
|
|
_, err := c.ID(ctx)
|
2019-07-19 13:17:51 +00:00
|
|
|
if err != nil && pass {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
if err == nil && !pass {
|
|
|
|
t.Error("request should fail with connection refusal")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
}
|