test : add selector & balancer test (#1577)
* add selector test * add balancer test * add node and selector * add ewma test * fix datarace * fix dataracepull/1580/head
parent
7cd9503b95
commit
ae57ae9bde
@ -0,0 +1,36 @@ |
|||||||
|
package filter |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/go-kratos/kratos/v2/registry" |
||||||
|
"github.com/go-kratos/kratos/v2/selector" |
||||||
|
"github.com/stretchr/testify/assert" |
||||||
|
) |
||||||
|
|
||||||
|
func TestVersion(t *testing.T) { |
||||||
|
f := Version("v2.0.0") |
||||||
|
var nodes []selector.Node |
||||||
|
nodes = append(nodes, selector.NewNode( |
||||||
|
"127.0.0.1:9090", |
||||||
|
®istry.ServiceInstance{ |
||||||
|
ID: "127.0.0.1:9090", |
||||||
|
Name: "helloworld", |
||||||
|
Version: "v1.0.0", |
||||||
|
Endpoints: []string{"http://127.0.0.1:9090"}, |
||||||
|
})) |
||||||
|
|
||||||
|
nodes = append(nodes, selector.NewNode( |
||||||
|
"127.0.0.2:9090", |
||||||
|
®istry.ServiceInstance{ |
||||||
|
ID: "127.0.0.2:9090", |
||||||
|
Name: "helloworld", |
||||||
|
Version: "v2.0.0", |
||||||
|
Endpoints: []string{"http://127.0.0.2:9090"}, |
||||||
|
})) |
||||||
|
|
||||||
|
n := f(context.Background(), nodes) |
||||||
|
assert.Equal(t, 1, len(n)) |
||||||
|
assert.Equal(t, "127.0.0.2:9090", n[0].Address()) |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
package direct |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"testing" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/go-kratos/kratos/v2/registry" |
||||||
|
"github.com/go-kratos/kratos/v2/selector" |
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert" |
||||||
|
) |
||||||
|
|
||||||
|
func TestDirect(t *testing.T) { |
||||||
|
b := &Builder{} |
||||||
|
wn := b.Build(selector.NewNode( |
||||||
|
"127.0.0.1:9090", |
||||||
|
®istry.ServiceInstance{ |
||||||
|
ID: "127.0.0.1:9090", |
||||||
|
Name: "helloworld", |
||||||
|
Version: "v1.0.0", |
||||||
|
Endpoints: []string{"http://127.0.0.1:9090"}, |
||||||
|
Metadata: map[string]string{"weight": "10"}, |
||||||
|
})) |
||||||
|
|
||||||
|
done := wn.Pick() |
||||||
|
assert.NotNil(t, done) |
||||||
|
time.Sleep(time.Millisecond * 10) |
||||||
|
done(context.Background(), selector.DoneInfo{}) |
||||||
|
assert.Equal(t, float64(10), wn.Weight()) |
||||||
|
assert.Greater(t, time.Millisecond*15, wn.PickElapsed()) |
||||||
|
assert.Less(t, time.Millisecond*5, wn.PickElapsed()) |
||||||
|
} |
||||||
|
|
||||||
|
func TestDirectDefaultWeight(t *testing.T) { |
||||||
|
b := &Builder{} |
||||||
|
wn := b.Build(selector.NewNode( |
||||||
|
"127.0.0.1:9090", |
||||||
|
®istry.ServiceInstance{ |
||||||
|
ID: "127.0.0.1:9090", |
||||||
|
Name: "helloworld", |
||||||
|
Version: "v1.0.0", |
||||||
|
Endpoints: []string{"http://127.0.0.1:9090"}, |
||||||
|
})) |
||||||
|
|
||||||
|
done := wn.Pick() |
||||||
|
assert.NotNil(t, done) |
||||||
|
time.Sleep(time.Millisecond * 10) |
||||||
|
done(context.Background(), selector.DoneInfo{}) |
||||||
|
assert.Equal(t, float64(100), wn.Weight()) |
||||||
|
assert.Greater(t, time.Millisecond*15, wn.PickElapsed()) |
||||||
|
assert.Less(t, time.Millisecond*5, wn.PickElapsed()) |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
package ewma |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"testing" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/go-kratos/kratos/v2/registry" |
||||||
|
"github.com/go-kratos/kratos/v2/selector" |
||||||
|
"github.com/stretchr/testify/assert" |
||||||
|
) |
||||||
|
|
||||||
|
func TestDirect(t *testing.T) { |
||||||
|
b := &Builder{} |
||||||
|
wn := b.Build(selector.NewNode( |
||||||
|
"127.0.0.1:9090", |
||||||
|
®istry.ServiceInstance{ |
||||||
|
ID: "127.0.0.1:9090", |
||||||
|
Name: "helloworld", |
||||||
|
Version: "v1.0.0", |
||||||
|
Endpoints: []string{"http://127.0.0.1:9090"}, |
||||||
|
Metadata: map[string]string{"weight": "10"}, |
||||||
|
})) |
||||||
|
|
||||||
|
assert.Equal(t, float64(100), wn.Weight()) |
||||||
|
done := wn.Pick() |
||||||
|
assert.NotNil(t, done) |
||||||
|
done2 := wn.Pick() |
||||||
|
assert.NotNil(t, done2) |
||||||
|
|
||||||
|
time.Sleep(time.Millisecond * 10) |
||||||
|
done(context.Background(), selector.DoneInfo{}) |
||||||
|
assert.Less(t, float64(30000), wn.Weight()) |
||||||
|
assert.Greater(t, float64(60000), wn.Weight()) |
||||||
|
|
||||||
|
assert.Greater(t, time.Millisecond*15, wn.PickElapsed()) |
||||||
|
assert.Less(t, time.Millisecond*5, wn.PickElapsed()) |
||||||
|
} |
||||||
|
|
||||||
|
func TestDirectError(t *testing.T) { |
||||||
|
b := &Builder{} |
||||||
|
wn := b.Build(selector.NewNode( |
||||||
|
"127.0.0.1:9090", |
||||||
|
®istry.ServiceInstance{ |
||||||
|
ID: "127.0.0.1:9090", |
||||||
|
Name: "helloworld", |
||||||
|
Version: "v1.0.0", |
||||||
|
Endpoints: []string{"http://127.0.0.1:9090"}, |
||||||
|
Metadata: map[string]string{"weight": "10"}, |
||||||
|
})) |
||||||
|
|
||||||
|
for i := 0; i < 5; i++ { |
||||||
|
var err error |
||||||
|
if i != 0 { |
||||||
|
err = context.DeadlineExceeded |
||||||
|
} |
||||||
|
done := wn.Pick() |
||||||
|
assert.NotNil(t, done) |
||||||
|
time.Sleep(time.Millisecond * 20) |
||||||
|
done(context.Background(), selector.DoneInfo{Err: err}) |
||||||
|
} |
||||||
|
|
||||||
|
assert.Less(t, float64(30000), wn.Weight()) |
||||||
|
assert.Greater(t, float64(60000), wn.Weight()) |
||||||
|
} |
||||||
|
|
||||||
|
func TestDirectErrorHandler(t *testing.T) { |
||||||
|
b := &Builder{ |
||||||
|
ErrHandler: func(err error) bool { |
||||||
|
return err != nil |
||||||
|
}, |
||||||
|
} |
||||||
|
wn := b.Build(selector.NewNode( |
||||||
|
"127.0.0.1:9090", |
||||||
|
®istry.ServiceInstance{ |
||||||
|
ID: "127.0.0.1:9090", |
||||||
|
Name: "helloworld", |
||||||
|
Version: "v1.0.0", |
||||||
|
Endpoints: []string{"http://127.0.0.1:9090"}, |
||||||
|
Metadata: map[string]string{"weight": "10"}, |
||||||
|
})) |
||||||
|
|
||||||
|
for i := 0; i < 5; i++ { |
||||||
|
var err error |
||||||
|
if i != 0 { |
||||||
|
err = context.DeadlineExceeded |
||||||
|
} |
||||||
|
done := wn.Pick() |
||||||
|
assert.NotNil(t, done) |
||||||
|
time.Sleep(time.Millisecond * 20) |
||||||
|
done(context.Background(), selector.DoneInfo{Err: err}) |
||||||
|
} |
||||||
|
|
||||||
|
assert.Less(t, float64(30000), wn.Weight()) |
||||||
|
assert.Greater(t, float64(60000), wn.Weight()) |
||||||
|
} |
@ -0,0 +1,92 @@ |
|||||||
|
package p2c |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"fmt" |
||||||
|
"math/rand" |
||||||
|
"sync" |
||||||
|
"sync/atomic" |
||||||
|
"testing" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/go-kratos/kratos/v2/registry" |
||||||
|
"github.com/go-kratos/kratos/v2/selector" |
||||||
|
"github.com/go-kratos/kratos/v2/selector/filter" |
||||||
|
"github.com/stretchr/testify/assert" |
||||||
|
) |
||||||
|
|
||||||
|
func TestWrr3(t *testing.T) { |
||||||
|
p2c := New(WithFilter(filter.Version("v2.0.0"))) |
||||||
|
var nodes []selector.Node |
||||||
|
for i := 0; i < 3; i++ { |
||||||
|
addr := fmt.Sprintf("127.0.0.%d:8080", i) |
||||||
|
nodes = append(nodes, selector.NewNode( |
||||||
|
addr, |
||||||
|
®istry.ServiceInstance{ |
||||||
|
ID: addr, |
||||||
|
Version: "v2.0.0", |
||||||
|
Metadata: map[string]string{"weight": "10"}, |
||||||
|
})) |
||||||
|
} |
||||||
|
p2c.Apply(nodes) |
||||||
|
var count1, count2, count3 int64 |
||||||
|
group := &sync.WaitGroup{} |
||||||
|
var lk sync.Mutex |
||||||
|
for i := 0; i < 9000; i++ { |
||||||
|
group.Add(1) |
||||||
|
go func() { |
||||||
|
defer group.Done() |
||||||
|
lk.Lock() |
||||||
|
d := time.Duration(rand.Intn(500)) * time.Millisecond |
||||||
|
lk.Unlock() |
||||||
|
time.Sleep(d) |
||||||
|
n, done, err := p2c.Select(context.Background()) |
||||||
|
assert.Nil(t, err) |
||||||
|
assert.NotNil(t, done) |
||||||
|
assert.NotNil(t, n) |
||||||
|
time.Sleep(time.Millisecond * 10) |
||||||
|
done(context.Background(), selector.DoneInfo{}) |
||||||
|
if n.Address() == "127.0.0.0:8080" { |
||||||
|
atomic.AddInt64(&count1, 1) |
||||||
|
} else if n.Address() == "127.0.0.1:8080" { |
||||||
|
atomic.AddInt64(&count2, 1) |
||||||
|
} else if n.Address() == "127.0.0.2:8080" { |
||||||
|
atomic.AddInt64(&count3, 1) |
||||||
|
} |
||||||
|
}() |
||||||
|
} |
||||||
|
group.Wait() |
||||||
|
assert.Greater(t, count1, int64(2500)) |
||||||
|
assert.Less(t, count1, int64(3500)) |
||||||
|
assert.Greater(t, count2, int64(2500)) |
||||||
|
assert.Less(t, count2, int64(3500)) |
||||||
|
assert.Greater(t, count3, int64(2500)) |
||||||
|
assert.Less(t, count3, int64(3500)) |
||||||
|
} |
||||||
|
|
||||||
|
func TestEmpty(t *testing.T) { |
||||||
|
b := &Balancer{} |
||||||
|
_, _, err := b.Pick(context.Background(), []selector.WeightedNode{}) |
||||||
|
assert.NotNil(t, err) |
||||||
|
} |
||||||
|
|
||||||
|
func TestOne(t *testing.T) { |
||||||
|
p2c := New(WithFilter(filter.Version("v2.0.0"))) |
||||||
|
var nodes []selector.Node |
||||||
|
for i := 0; i < 1; i++ { |
||||||
|
addr := fmt.Sprintf("127.0.0.%d:8080", i) |
||||||
|
nodes = append(nodes, selector.NewNode( |
||||||
|
addr, |
||||||
|
®istry.ServiceInstance{ |
||||||
|
ID: addr, |
||||||
|
Version: "v2.0.0", |
||||||
|
Metadata: map[string]string{"weight": "10"}, |
||||||
|
})) |
||||||
|
} |
||||||
|
p2c.Apply(nodes) |
||||||
|
n, done, err := p2c.Select(context.Background()) |
||||||
|
assert.Nil(t, err) |
||||||
|
assert.NotNil(t, done) |
||||||
|
assert.NotNil(t, n) |
||||||
|
assert.Equal(t, "127.0.0.0:8080", n.Address()) |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
package random |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/go-kratos/kratos/v2/registry" |
||||||
|
"github.com/go-kratos/kratos/v2/selector" |
||||||
|
"github.com/go-kratos/kratos/v2/selector/filter" |
||||||
|
"github.com/stretchr/testify/assert" |
||||||
|
) |
||||||
|
|
||||||
|
func TestWrr(t *testing.T) { |
||||||
|
random := New(WithFilter(filter.Version("v2.0.0"))) |
||||||
|
var nodes []selector.Node |
||||||
|
nodes = append(nodes, selector.NewNode( |
||||||
|
"127.0.0.1:8080", |
||||||
|
®istry.ServiceInstance{ |
||||||
|
ID: "127.0.0.1:8080", |
||||||
|
Version: "v2.0.0", |
||||||
|
Metadata: map[string]string{"weight": "10"}, |
||||||
|
})) |
||||||
|
nodes = append(nodes, selector.NewNode( |
||||||
|
"127.0.0.1:9090", |
||||||
|
®istry.ServiceInstance{ |
||||||
|
ID: "127.0.0.1:9090", |
||||||
|
Version: "v2.0.0", |
||||||
|
Metadata: map[string]string{"weight": "20"}, |
||||||
|
})) |
||||||
|
random.Apply(nodes) |
||||||
|
var count1, count2 int |
||||||
|
for i := 0; i < 200; i++ { |
||||||
|
n, done, err := random.Select(context.Background()) |
||||||
|
assert.Nil(t, err) |
||||||
|
assert.NotNil(t, done) |
||||||
|
assert.NotNil(t, n) |
||||||
|
done(context.Background(), selector.DoneInfo{}) |
||||||
|
if n.Address() == "127.0.0.1:8080" { |
||||||
|
count1++ |
||||||
|
} else if n.Address() == "127.0.0.1:9090" { |
||||||
|
count2++ |
||||||
|
} |
||||||
|
} |
||||||
|
assert.Greater(t, count1, 80) |
||||||
|
assert.Less(t, count1, 120) |
||||||
|
assert.Greater(t, count2, 80) |
||||||
|
assert.Less(t, count2, 120) |
||||||
|
} |
||||||
|
|
||||||
|
func TestEmpty(t *testing.T) { |
||||||
|
b := &Balancer{} |
||||||
|
_, _, err := b.Pick(context.Background(), []selector.WeightedNode{}) |
||||||
|
assert.NotNil(t, err) |
||||||
|
} |
@ -0,0 +1,127 @@ |
|||||||
|
package selector |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"math/rand" |
||||||
|
"sync/atomic" |
||||||
|
"testing" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/go-kratos/kratos/v2/registry" |
||||||
|
"github.com/stretchr/testify/assert" |
||||||
|
) |
||||||
|
|
||||||
|
type mockWeightedNode struct { |
||||||
|
Node |
||||||
|
|
||||||
|
lastPick int64 |
||||||
|
} |
||||||
|
|
||||||
|
// Weight is the runtime calculated weight
|
||||||
|
func (n *mockWeightedNode) Weight() float64 { |
||||||
|
if n.InitialWeight() != nil { |
||||||
|
return float64(*n.InitialWeight()) |
||||||
|
} |
||||||
|
return 100 |
||||||
|
} |
||||||
|
|
||||||
|
// Pick the node
|
||||||
|
func (n *mockWeightedNode) Pick() DoneFunc { |
||||||
|
now := time.Now().UnixNano() |
||||||
|
atomic.StoreInt64(&n.lastPick, now) |
||||||
|
return func(ctx context.Context, di DoneInfo) {} |
||||||
|
} |
||||||
|
|
||||||
|
// PickElapsed is time elapsed since the latest pick
|
||||||
|
func (n *mockWeightedNode) PickElapsed() time.Duration { |
||||||
|
return time.Duration(time.Now().UnixNano() - atomic.LoadInt64(&n.lastPick)) |
||||||
|
} |
||||||
|
|
||||||
|
type mockWeightedNodeBuilder struct{} |
||||||
|
|
||||||
|
func (b *mockWeightedNodeBuilder) Build(n Node) WeightedNode { |
||||||
|
return &mockWeightedNode{Node: n} |
||||||
|
} |
||||||
|
|
||||||
|
func mockFilter(version string) Filter { |
||||||
|
return func(_ context.Context, nodes []Node) []Node { |
||||||
|
filters := make([]Node, 0, len(nodes)) |
||||||
|
for _, n := range nodes { |
||||||
|
if n.Version() == version { |
||||||
|
filters = append(filters, n) |
||||||
|
} |
||||||
|
} |
||||||
|
return filters |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
type mockBalancerBuilder struct{} |
||||||
|
|
||||||
|
func (b *mockBalancerBuilder) Build() Balancer { |
||||||
|
return &mockBalancer{} |
||||||
|
} |
||||||
|
|
||||||
|
type mockBalancer struct{} |
||||||
|
|
||||||
|
func (b *mockBalancer) Pick(ctx context.Context, nodes []WeightedNode) (selected WeightedNode, done DoneFunc, err error) { |
||||||
|
if len(nodes) == 0 { |
||||||
|
err = ErrNoAvailable |
||||||
|
return |
||||||
|
} |
||||||
|
cur := rand.Intn(len(nodes)) |
||||||
|
selected = nodes[cur] |
||||||
|
done = selected.Pick() |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func TestDefault(t *testing.T) { |
||||||
|
builder := DefaultBuilder{ |
||||||
|
Node: &mockWeightedNodeBuilder{}, |
||||||
|
Filters: []Filter{mockFilter("v2.0.0")}, |
||||||
|
Balancer: &mockBalancerBuilder{}, |
||||||
|
} |
||||||
|
selector := builder.Build() |
||||||
|
var nodes []Node |
||||||
|
nodes = append(nodes, NewNode( |
||||||
|
"127.0.0.1:8080", |
||||||
|
®istry.ServiceInstance{ |
||||||
|
ID: "127.0.0.1:8080", |
||||||
|
Name: "helloworld", |
||||||
|
Version: "v2.0.0", |
||||||
|
Endpoints: []string{"http://127.0.0.1:8080"}, |
||||||
|
Metadata: map[string]string{"weight": "10"}, |
||||||
|
})) |
||||||
|
nodes = append(nodes, NewNode( |
||||||
|
"127.0.0.1:9090", |
||||||
|
®istry.ServiceInstance{ |
||||||
|
ID: "127.0.0.1:9090", |
||||||
|
Name: "helloworld", |
||||||
|
Version: "v1.0.0", |
||||||
|
Endpoints: []string{"http://127.0.0.1:9090"}, |
||||||
|
Metadata: map[string]string{"weight": "10"}, |
||||||
|
})) |
||||||
|
selector.Apply(nodes) |
||||||
|
n, done, err := selector.Select(context.Background(), WithFilter(mockFilter("v2.0.0"))) |
||||||
|
assert.Nil(t, err) |
||||||
|
assert.NotNil(t, n) |
||||||
|
assert.NotNil(t, done) |
||||||
|
assert.Equal(t, "v2.0.0", n.Version()) |
||||||
|
assert.NotNil(t, n.Address()) |
||||||
|
assert.Equal(t, int64(10), *n.InitialWeight()) |
||||||
|
assert.NotNil(t, n.Metadata()) |
||||||
|
assert.Equal(t, "helloworld", n.ServiceName()) |
||||||
|
done(context.Background(), DoneInfo{}) |
||||||
|
|
||||||
|
// no v3.0.0 instance
|
||||||
|
n, done, err = selector.Select(context.Background(), WithFilter(mockFilter("v3.0.0"))) |
||||||
|
assert.Equal(t, ErrNoAvailable, err) |
||||||
|
assert.Nil(t, done) |
||||||
|
assert.Nil(t, n) |
||||||
|
|
||||||
|
// apply zero instance
|
||||||
|
selector.Apply([]Node{}) |
||||||
|
n, done, err = selector.Select(context.Background(), WithFilter(mockFilter("v2.0.0"))) |
||||||
|
assert.Equal(t, ErrNoAvailable, err) |
||||||
|
assert.Nil(t, done) |
||||||
|
assert.Nil(t, n) |
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
package wrr |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/go-kratos/kratos/v2/registry" |
||||||
|
"github.com/go-kratos/kratos/v2/selector" |
||||||
|
"github.com/go-kratos/kratos/v2/selector/filter" |
||||||
|
"github.com/stretchr/testify/assert" |
||||||
|
) |
||||||
|
|
||||||
|
func TestWrr(t *testing.T) { |
||||||
|
wrr := New(WithFilter(filter.Version("v2.0.0"))) |
||||||
|
var nodes []selector.Node |
||||||
|
nodes = append(nodes, selector.NewNode( |
||||||
|
"127.0.0.1:8080", |
||||||
|
®istry.ServiceInstance{ |
||||||
|
ID: "127.0.0.1:8080", |
||||||
|
Version: "v2.0.0", |
||||||
|
Metadata: map[string]string{"weight": "10"}, |
||||||
|
})) |
||||||
|
nodes = append(nodes, selector.NewNode( |
||||||
|
"127.0.0.1:9090", |
||||||
|
®istry.ServiceInstance{ |
||||||
|
ID: "127.0.0.1:9090", |
||||||
|
Version: "v2.0.0", |
||||||
|
Metadata: map[string]string{"weight": "20"}, |
||||||
|
})) |
||||||
|
wrr.Apply(nodes) |
||||||
|
var count1, count2 int |
||||||
|
for i := 0; i < 90; i++ { |
||||||
|
n, done, err := wrr.Select(context.Background()) |
||||||
|
assert.Nil(t, err) |
||||||
|
assert.NotNil(t, done) |
||||||
|
assert.NotNil(t, n) |
||||||
|
done(context.Background(), selector.DoneInfo{}) |
||||||
|
if n.Address() == "127.0.0.1:8080" { |
||||||
|
count1++ |
||||||
|
} else if n.Address() == "127.0.0.1:9090" { |
||||||
|
count2++ |
||||||
|
} |
||||||
|
} |
||||||
|
assert.Equal(t, 30, count1) |
||||||
|
assert.Equal(t, 60, count2) |
||||||
|
} |
||||||
|
|
||||||
|
func TestEmpty(t *testing.T) { |
||||||
|
b := &Balancer{} |
||||||
|
_, _, err := b.Pick(context.Background(), []selector.WeightedNode{}) |
||||||
|
assert.NotNil(t, err) |
||||||
|
} |
Loading…
Reference in new issue