forked from TrueCloudLab/frostfs-node
262 lines
7.3 KiB
Go
262 lines
7.3 KiB
Go
|
package netmap
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/json"
|
||
|
"math/rand"
|
||
|
"sync"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/nspcc-dev/neofs-api-go/bootstrap"
|
||
|
"github.com/nspcc-dev/neofs-api-go/object"
|
||
|
"github.com/nspcc-dev/netmap"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
)
|
||
|
|
||
|
func TestNetMap_DataRace(t *testing.T) {
|
||
|
var (
|
||
|
nm = NewNetmap()
|
||
|
wg = new(sync.WaitGroup)
|
||
|
nodes = []bootstrap.NodeInfo{
|
||
|
{Address: "SPB1", Options: []string{"/Location:Europe/Country:USA"}},
|
||
|
{Address: "SPB2", Options: []string{"/Location:Europe/Country:Italy"}},
|
||
|
{Address: "MSK1", Options: []string{"/Location:Europe/Country:Germany"}},
|
||
|
{Address: "MSK2", Options: []string{"/Location:Europe/Country:Russia"}},
|
||
|
}
|
||
|
)
|
||
|
|
||
|
wg.Add(10)
|
||
|
for i := 0; i < 10; i++ {
|
||
|
go func(n int) {
|
||
|
for _, node := range nodes {
|
||
|
require.NoError(t, nm.Add(node.Address, node.PubKey, 0, node.Options...))
|
||
|
// t.Logf("%02d: add node %q", n, node.Address)
|
||
|
}
|
||
|
|
||
|
wg.Done()
|
||
|
}(i)
|
||
|
}
|
||
|
|
||
|
wg.Add(3 * 10)
|
||
|
for i := 0; i < 10; i++ {
|
||
|
go func(n int) {
|
||
|
nm.Copy()
|
||
|
// t.Logf("%02d: Copy", n)
|
||
|
wg.Done()
|
||
|
}(i)
|
||
|
go func(n int) {
|
||
|
nm.Items()
|
||
|
// t.Logf("%02d: Items", n)
|
||
|
wg.Done()
|
||
|
}(i)
|
||
|
go func(n int) {
|
||
|
nm.Root()
|
||
|
// t.Logf("%02d: Root", n)
|
||
|
wg.Done()
|
||
|
}(i)
|
||
|
}
|
||
|
|
||
|
wg.Wait()
|
||
|
}
|
||
|
|
||
|
func TestNetMapSuite(t *testing.T) {
|
||
|
var (
|
||
|
err error
|
||
|
nm1 = NewNetmap()
|
||
|
nodes = []bootstrap.NodeInfo{
|
||
|
{Address: "SPB1", Options: []string{"/Location:Europe/Country:USA"}, Status: 1},
|
||
|
{Address: "SPB2", Options: []string{"/Location:Europe/Country:Italy"}, Status: 2},
|
||
|
{Address: "MSK1", Options: []string{"/Location:Europe/Country:Germany"}, Status: 3},
|
||
|
{Address: "MSK2", Options: []string{"/Location:Europe/Country:Russia"}, Status: 4},
|
||
|
}
|
||
|
)
|
||
|
|
||
|
for _, node := range nodes {
|
||
|
err = nm1.Add(node.Address, nil, node.Status, node.Options...)
|
||
|
require.NoError(t, err)
|
||
|
}
|
||
|
|
||
|
t.Run("copy should work like expected", func(t *testing.T) {
|
||
|
nm2 := nm1.Copy()
|
||
|
require.Equal(t, nm1.root, nm2.root)
|
||
|
require.Equal(t, nm1.items, nm2.items)
|
||
|
})
|
||
|
|
||
|
t.Run("add node should not ignore options", func(t *testing.T) {
|
||
|
items := nm1.ItemsCopy()
|
||
|
|
||
|
nm2 := NewNetmap()
|
||
|
err = nm2.AddNode(&items[0], "/New/Option")
|
||
|
require.NoError(t, err)
|
||
|
require.Len(t, nm2.items, 1)
|
||
|
require.Equal(t, append(items[0].Options, "/New/Option"), nm2.items[0].Options)
|
||
|
})
|
||
|
|
||
|
t.Run("copyItems should work like expected", func(t *testing.T) {
|
||
|
require.Equal(t, nm1.items, nm1.ItemsCopy())
|
||
|
})
|
||
|
|
||
|
t.Run("marshal / unmarshal should be identical on same data", func(t *testing.T) {
|
||
|
var nm2 *NetMap
|
||
|
want, err := json.Marshal(nodes)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
actual, err := json.Marshal(nm1)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
require.Equal(t, want, actual)
|
||
|
|
||
|
err = json.Unmarshal(actual, &nm2)
|
||
|
require.NoError(t, err)
|
||
|
require.Equal(t, nm1.root, nm2.root)
|
||
|
require.Equal(t, nm1.items, nm2.items)
|
||
|
})
|
||
|
|
||
|
t.Run("unmarshal should override existing data", func(t *testing.T) {
|
||
|
var nm2 *NetMap
|
||
|
|
||
|
want, err := json.Marshal(nodes)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
actual, err := json.Marshal(nm1)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
require.Equal(t, want, actual)
|
||
|
|
||
|
nm2 = nm1.Copy()
|
||
|
err = nm2.Add("SOMEADDR", nil, 0, "/Location:Europe/Country:USA")
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
err = json.Unmarshal(actual, &nm2)
|
||
|
require.NoError(t, err)
|
||
|
require.Equal(t, nm1.root, nm2.root)
|
||
|
require.Equal(t, nm1.items, nm2.items)
|
||
|
})
|
||
|
|
||
|
t.Run("unmarshal should fail on bad data", func(t *testing.T) {
|
||
|
var nm2 *NetMap
|
||
|
require.Error(t, json.Unmarshal([]byte(`"some bad data"`), &nm2))
|
||
|
})
|
||
|
|
||
|
t.Run("unmarshal should fail on add nodes", func(t *testing.T) {
|
||
|
var nm2 *NetMap
|
||
|
require.Error(t, json.Unmarshal([]byte(`[{"address": "SPB1","options":["1-2-3-4"]}]`), &nm2))
|
||
|
})
|
||
|
|
||
|
t.Run("merge two netmaps", func(t *testing.T) {
|
||
|
newNodes := []bootstrap.NodeInfo{
|
||
|
{Address: "SPB3", Options: []string{"/Location:Europe/Country:France"}},
|
||
|
}
|
||
|
nm2 := NewNetmap()
|
||
|
for _, node := range newNodes {
|
||
|
err = nm2.Add(node.Address, nil, 0, node.Options...)
|
||
|
require.NoError(t, err)
|
||
|
}
|
||
|
|
||
|
err = nm2.Merge(nm1)
|
||
|
require.NoError(t, err)
|
||
|
require.Len(t, nm2.items, len(nodes)+len(newNodes))
|
||
|
|
||
|
ns := nm2.FindNodes([]byte("pivot"), netmap.SFGroup{
|
||
|
Filters: []Filter{{Key: "Country", F: FilterEQ("Germany")}},
|
||
|
Selectors: []Select{{Count: 1, Key: NodesBucket}},
|
||
|
})
|
||
|
require.Len(t, ns, 1)
|
||
|
})
|
||
|
|
||
|
t.Run("weighted netmaps", func(t *testing.T) {
|
||
|
strawNodes := []bootstrap.NodeInfo{
|
||
|
{Address: "SPB2", Options: []string{"/Location:Europe/Country:Italy", "/Capacity:10", "/Price:100"}},
|
||
|
{Address: "MSK1", Options: []string{"/Location:Europe/Country:Germany", "/Capacity:10", "/Price:1"}},
|
||
|
{Address: "MSK2", Options: []string{"/Location:Europe/Country:Russia", "/Capacity:5", "/Price:10"}},
|
||
|
{Address: "SPB1", Options: []string{"/Location:Europe/Country:France", "/Capacity:20", "/Price:2"}},
|
||
|
}
|
||
|
nm2 := NewNetmap()
|
||
|
for _, node := range strawNodes {
|
||
|
err = nm2.Add(node.Address, nil, 0, node.Options...)
|
||
|
require.NoError(t, err)
|
||
|
}
|
||
|
|
||
|
ns1 := nm1.FindNodes([]byte("pivot"), netmap.SFGroup{
|
||
|
Selectors: []Select{{Count: 2, Key: NodesBucket}},
|
||
|
})
|
||
|
require.Len(t, ns1, 2)
|
||
|
|
||
|
ns2 := nm2.FindNodes([]byte("pivot"), netmap.SFGroup{
|
||
|
Selectors: []Select{{Count: 2, Key: NodesBucket}},
|
||
|
})
|
||
|
require.Len(t, ns2, 2)
|
||
|
require.NotEqual(t, ns1, ns2)
|
||
|
require.Equal(t, []uint32{1, 3}, ns2)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestNetMap_Normalise(t *testing.T) {
|
||
|
const testCount = 5
|
||
|
|
||
|
nodes := []bootstrap.NodeInfo{
|
||
|
{Address: "SPB2", PubKey: []byte{4}, Options: []string{"/Location:Europe/Country:Italy", "/Capacity:10", "/Price:100"}},
|
||
|
{Address: "MSK1", PubKey: []byte{2}, Options: []string{"/Location:Europe/Country:Germany", "/Capacity:10", "/Price:1"}},
|
||
|
{Address: "MSK2", PubKey: []byte{3}, Options: []string{"/Location:Europe/Country:Russia", "/Capacity:5", "/Price:10"}},
|
||
|
{Address: "SPB1", PubKey: []byte{1}, Options: []string{"/Location:Europe/Country:France", "/Capacity:20", "/Price:2"}},
|
||
|
}
|
||
|
|
||
|
add := func(nm *NetMap, indices ...int) {
|
||
|
for _, i := range indices {
|
||
|
err := nm.Add(nodes[i].Address, nodes[i].PubKey, 0, nodes[i].Options...)
|
||
|
require.NoError(t, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
indices := []int{0, 1, 2, 3}
|
||
|
|
||
|
nm1 := NewNetmap()
|
||
|
add(nm1, indices...)
|
||
|
norm := nm1.Normalise()
|
||
|
|
||
|
for i := 0; i < testCount; i++ {
|
||
|
rand.Seed(time.Now().UnixNano())
|
||
|
rand.Shuffle(len(indices), func(i, j int) { indices[i], indices[j] = indices[j], indices[i] })
|
||
|
|
||
|
nm := NewNetmap()
|
||
|
add(nm, indices...)
|
||
|
require.Equal(t, norm, nm.Normalise())
|
||
|
}
|
||
|
|
||
|
t.Run("normalise removes duplicates", func(t *testing.T) {
|
||
|
before := NewNetmap()
|
||
|
add(before, indices...)
|
||
|
before.items = append(before.items, before.items...)
|
||
|
|
||
|
nm := before.Normalise()
|
||
|
require.Len(t, nm.items, len(indices))
|
||
|
|
||
|
loop:
|
||
|
for i := range nodes {
|
||
|
for j := range nm.items {
|
||
|
if bytes.Equal(nm.items[j].PubKey, nodes[i].PubKey) {
|
||
|
continue loop
|
||
|
}
|
||
|
}
|
||
|
require.Fail(t, "normalized netmap does not contain '%s' node", nodes[i].Address)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestNodeInfo_Price(t *testing.T) {
|
||
|
var info bootstrap.NodeInfo
|
||
|
|
||
|
// too small value
|
||
|
info = bootstrap.NodeInfo{Options: []string{"/Price:0.01048575"}}
|
||
|
require.Equal(t, uint64(0), info.Price())
|
||
|
|
||
|
// min value
|
||
|
info = bootstrap.NodeInfo{Options: []string{"/Price:0.01048576"}}
|
||
|
require.Equal(t, uint64(1), info.Price())
|
||
|
|
||
|
// big value
|
||
|
info = bootstrap.NodeInfo{Options: []string{"/Price:1000000000.666"}}
|
||
|
require.Equal(t, uint64(1000000000.666*1e8/object.UnitsMB), info.Price())
|
||
|
}
|