2021-10-25 13:27:59 +01:00
|
|
|
// Copyright (C) 2020 Storj Labs, Inc.
|
2020-05-15 15:32:28 +01:00
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
2021-05-04 13:29:26 +01:00
|
|
|
package uploadselection_test
|
2020-05-15 15:32:28 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
|
|
|
"storj.io/common/storj"
|
|
|
|
"storj.io/common/testcontext"
|
|
|
|
"storj.io/common/testrand"
|
2021-05-04 13:29:26 +01:00
|
|
|
"storj.io/storj/satellite/nodeselection/uploadselection"
|
2020-05-15 15:32:28 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestSelectByID(t *testing.T) {
|
|
|
|
// create 3 nodes, 2 with same subnet
|
|
|
|
// perform many node selections that selects 2 nodes
|
|
|
|
// expect that the all node are selected ~33% of the time.
|
|
|
|
ctx := testcontext.New(t)
|
|
|
|
defer ctx.Cleanup()
|
|
|
|
|
|
|
|
// create 3 nodes, 2 with same subnet
|
|
|
|
lastNetDuplicate := "1.0.1"
|
2021-05-04 13:29:26 +01:00
|
|
|
subnetA1 := &uploadselection.Node{
|
2020-05-26 14:50:28 +01:00
|
|
|
NodeURL: storj.NodeURL{
|
|
|
|
ID: testrand.NodeID(),
|
|
|
|
Address: lastNetDuplicate + ".4:8080",
|
|
|
|
},
|
2020-05-15 15:32:28 +01:00
|
|
|
LastNet: lastNetDuplicate,
|
|
|
|
LastIPPort: lastNetDuplicate + ".4:8080",
|
|
|
|
}
|
2021-05-04 13:29:26 +01:00
|
|
|
subnetA2 := &uploadselection.Node{
|
2020-05-26 14:50:28 +01:00
|
|
|
NodeURL: storj.NodeURL{
|
|
|
|
ID: testrand.NodeID(),
|
|
|
|
Address: lastNetDuplicate + ".5:8080",
|
|
|
|
},
|
2020-05-15 15:32:28 +01:00
|
|
|
LastNet: lastNetDuplicate,
|
|
|
|
LastIPPort: lastNetDuplicate + ".5:8080",
|
|
|
|
}
|
|
|
|
|
|
|
|
lastNetSingle := "1.0.2"
|
2021-05-04 13:29:26 +01:00
|
|
|
subnetB1 := &uploadselection.Node{
|
2020-05-26 14:50:28 +01:00
|
|
|
NodeURL: storj.NodeURL{
|
|
|
|
ID: testrand.NodeID(),
|
|
|
|
Address: lastNetSingle + ".5:8080",
|
|
|
|
},
|
2020-05-15 15:32:28 +01:00
|
|
|
LastNet: lastNetSingle,
|
|
|
|
LastIPPort: lastNetSingle + ".5:8080",
|
|
|
|
}
|
|
|
|
|
2021-05-04 13:29:26 +01:00
|
|
|
nodes := []*uploadselection.Node{subnetA1, subnetA2, subnetB1}
|
|
|
|
selector := uploadselection.SelectByID(nodes)
|
2020-05-15 15:32:28 +01:00
|
|
|
|
|
|
|
const (
|
|
|
|
reqCount = 2
|
|
|
|
executionCount = 10000
|
|
|
|
)
|
|
|
|
|
|
|
|
var selectedNodeCount = map[storj.NodeID]int{}
|
|
|
|
|
|
|
|
// perform many node selections that selects 2 nodes
|
|
|
|
for i := 0; i < executionCount; i++ {
|
2021-10-25 13:27:59 +01:00
|
|
|
selectedNodes := selector.Select(reqCount, uploadselection.Criteria{})
|
2020-05-15 15:32:28 +01:00
|
|
|
require.Len(t, selectedNodes, reqCount)
|
|
|
|
for _, node := range selectedNodes {
|
|
|
|
selectedNodeCount[node.ID]++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
subnetA1Count := float64(selectedNodeCount[subnetA1.ID])
|
|
|
|
subnetA2Count := float64(selectedNodeCount[subnetA2.ID])
|
|
|
|
subnetB1Count := float64(selectedNodeCount[subnetB1.ID])
|
|
|
|
total := subnetA1Count + subnetA2Count + subnetB1Count
|
|
|
|
assert.Equal(t, total, float64(reqCount*executionCount))
|
|
|
|
|
|
|
|
const selectionEpsilon = 0.1
|
|
|
|
const percent = 1.0 / 3.0
|
|
|
|
assert.InDelta(t, subnetA1Count/total, percent, selectionEpsilon)
|
|
|
|
assert.InDelta(t, subnetA2Count/total, percent, selectionEpsilon)
|
|
|
|
assert.InDelta(t, subnetB1Count/total, percent, selectionEpsilon)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSelectBySubnet(t *testing.T) {
|
|
|
|
// create 3 nodes, 2 with same subnet
|
|
|
|
// perform many node selections that selects 2 nodes
|
|
|
|
// expect that the single node is selected 50% of the time
|
|
|
|
// expect the 2 nodes with same subnet should each be selected 25% of time
|
|
|
|
ctx := testcontext.New(t)
|
|
|
|
defer ctx.Cleanup()
|
|
|
|
|
|
|
|
// create 3 nodes, 2 with same subnet
|
|
|
|
lastNetDuplicate := "1.0.1"
|
2021-05-04 13:29:26 +01:00
|
|
|
subnetA1 := &uploadselection.Node{
|
2020-05-26 14:50:28 +01:00
|
|
|
NodeURL: storj.NodeURL{
|
|
|
|
ID: testrand.NodeID(),
|
|
|
|
Address: lastNetDuplicate + ".4:8080",
|
|
|
|
},
|
2020-05-15 15:32:28 +01:00
|
|
|
LastNet: lastNetDuplicate,
|
|
|
|
LastIPPort: lastNetDuplicate + ".4:8080",
|
|
|
|
}
|
2021-05-04 13:29:26 +01:00
|
|
|
subnetA2 := &uploadselection.Node{
|
2020-05-26 14:50:28 +01:00
|
|
|
NodeURL: storj.NodeURL{
|
|
|
|
ID: testrand.NodeID(),
|
|
|
|
Address: lastNetDuplicate + ".5:8080",
|
|
|
|
},
|
2020-05-15 15:32:28 +01:00
|
|
|
LastNet: lastNetDuplicate,
|
|
|
|
LastIPPort: lastNetDuplicate + ".5:8080",
|
|
|
|
}
|
|
|
|
|
|
|
|
lastNetSingle := "1.0.2"
|
2021-05-04 13:29:26 +01:00
|
|
|
subnetB1 := &uploadselection.Node{
|
2020-05-26 14:50:28 +01:00
|
|
|
NodeURL: storj.NodeURL{
|
|
|
|
ID: testrand.NodeID(),
|
|
|
|
Address: lastNetSingle + ".5:8080",
|
|
|
|
},
|
2020-05-15 15:32:28 +01:00
|
|
|
LastNet: lastNetSingle,
|
|
|
|
LastIPPort: lastNetSingle + ".5:8080",
|
|
|
|
}
|
|
|
|
|
2021-05-04 13:29:26 +01:00
|
|
|
nodes := []*uploadselection.Node{subnetA1, subnetA2, subnetB1}
|
|
|
|
selector := uploadselection.SelectBySubnetFromNodes(nodes)
|
2020-05-15 15:32:28 +01:00
|
|
|
|
|
|
|
const (
|
|
|
|
reqCount = 2
|
|
|
|
executionCount = 1000
|
|
|
|
)
|
|
|
|
|
|
|
|
var selectedNodeCount = map[storj.NodeID]int{}
|
|
|
|
|
|
|
|
// perform many node selections that selects 2 nodes
|
|
|
|
for i := 0; i < executionCount; i++ {
|
2021-10-25 13:27:59 +01:00
|
|
|
selectedNodes := selector.Select(reqCount, uploadselection.Criteria{})
|
2020-05-15 15:32:28 +01:00
|
|
|
require.Len(t, selectedNodes, reqCount)
|
|
|
|
for _, node := range selectedNodes {
|
|
|
|
selectedNodeCount[node.ID]++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
subnetA1Count := float64(selectedNodeCount[subnetA1.ID])
|
|
|
|
subnetA2Count := float64(selectedNodeCount[subnetA2.ID])
|
|
|
|
subnetB1Count := float64(selectedNodeCount[subnetB1.ID])
|
|
|
|
total := subnetA1Count + subnetA2Count + subnetB1Count
|
|
|
|
assert.Equal(t, total, float64(reqCount*executionCount))
|
|
|
|
|
|
|
|
// expect that the single node is selected 50% of the time
|
|
|
|
// expect the 2 nodes with same subnet should each be selected 25% of time
|
|
|
|
nodeID1total := subnetA1Count / total
|
|
|
|
nodeID2total := subnetA2Count / total
|
|
|
|
|
|
|
|
const (
|
|
|
|
selectionEpsilon = 0.1
|
|
|
|
uniqueSubnet = 0.5
|
|
|
|
)
|
|
|
|
|
|
|
|
// we expect that the 2 nodes from the same subnet should be
|
|
|
|
// selected roughly the same percent of the time
|
|
|
|
assert.InDelta(t, nodeID2total, nodeID1total, selectionEpsilon)
|
|
|
|
|
|
|
|
// the node from the unique subnet should be selected exactly half of the time
|
|
|
|
nodeID3total := subnetB1Count / total
|
|
|
|
assert.Equal(t, nodeID3total, uniqueSubnet)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSelectBySubnetOneAtATime(t *testing.T) {
|
|
|
|
// create 3 nodes, 2 with same subnet
|
|
|
|
// perform many node selections that selects 1 node
|
|
|
|
// expect that the single node is selected 50% of the time
|
|
|
|
// expect the 2 nodes with same subnet should each be selected 25% of time
|
|
|
|
ctx := testcontext.New(t)
|
|
|
|
defer ctx.Cleanup()
|
|
|
|
|
|
|
|
// create 3 nodes, 2 with same subnet
|
|
|
|
lastNetDuplicate := "1.0.1"
|
2021-05-04 13:29:26 +01:00
|
|
|
subnetA1 := &uploadselection.Node{
|
2020-05-26 14:50:28 +01:00
|
|
|
NodeURL: storj.NodeURL{
|
|
|
|
ID: testrand.NodeID(),
|
|
|
|
Address: lastNetDuplicate + ".4:8080",
|
|
|
|
},
|
2020-05-15 15:32:28 +01:00
|
|
|
LastNet: lastNetDuplicate,
|
|
|
|
LastIPPort: lastNetDuplicate + ".4:8080",
|
|
|
|
}
|
2021-05-04 13:29:26 +01:00
|
|
|
subnetA2 := &uploadselection.Node{
|
2020-05-26 14:50:28 +01:00
|
|
|
NodeURL: storj.NodeURL{
|
|
|
|
ID: testrand.NodeID(),
|
|
|
|
Address: lastNetDuplicate + ".5:8080",
|
|
|
|
},
|
2020-05-15 15:32:28 +01:00
|
|
|
LastNet: lastNetDuplicate,
|
|
|
|
LastIPPort: lastNetDuplicate + ".5:8080",
|
|
|
|
}
|
|
|
|
|
|
|
|
lastNetSingle := "1.0.2"
|
2021-05-04 13:29:26 +01:00
|
|
|
subnetB1 := &uploadselection.Node{
|
2020-05-26 14:50:28 +01:00
|
|
|
NodeURL: storj.NodeURL{
|
|
|
|
ID: testrand.NodeID(),
|
|
|
|
Address: lastNetSingle + ".5:8080",
|
|
|
|
},
|
2020-05-15 15:32:28 +01:00
|
|
|
LastNet: lastNetSingle,
|
|
|
|
LastIPPort: lastNetSingle + ".5:8080",
|
|
|
|
}
|
|
|
|
|
2021-05-04 13:29:26 +01:00
|
|
|
nodes := []*uploadselection.Node{subnetA1, subnetA2, subnetB1}
|
|
|
|
selector := uploadselection.SelectBySubnetFromNodes(nodes)
|
2020-05-15 15:32:28 +01:00
|
|
|
|
|
|
|
const (
|
|
|
|
reqCount = 1
|
|
|
|
executionCount = 1000
|
|
|
|
)
|
|
|
|
|
|
|
|
var selectedNodeCount = map[storj.NodeID]int{}
|
|
|
|
|
|
|
|
// perform many node selections that selects 1 node
|
|
|
|
for i := 0; i < executionCount; i++ {
|
2021-10-25 13:27:59 +01:00
|
|
|
selectedNodes := selector.Select(reqCount, uploadselection.Criteria{})
|
2020-05-15 15:32:28 +01:00
|
|
|
require.Len(t, selectedNodes, reqCount)
|
|
|
|
for _, node := range selectedNodes {
|
|
|
|
selectedNodeCount[node.ID]++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
subnetA1Count := float64(selectedNodeCount[subnetA1.ID])
|
|
|
|
subnetA2Count := float64(selectedNodeCount[subnetA2.ID])
|
|
|
|
subnetB1Count := float64(selectedNodeCount[subnetB1.ID])
|
|
|
|
total := subnetA1Count + subnetA2Count + subnetB1Count
|
|
|
|
assert.Equal(t, total, float64(reqCount*executionCount))
|
|
|
|
|
|
|
|
const (
|
|
|
|
selectionEpsilon = 0.1
|
|
|
|
uniqueSubnet = 0.5
|
|
|
|
)
|
|
|
|
|
|
|
|
// we expect that the 2 nodes from the same subnet should be
|
|
|
|
// selected roughly the same ~25% percent of the time
|
|
|
|
assert.InDelta(t, subnetA2Count/total, subnetA1Count/total, selectionEpsilon)
|
|
|
|
|
|
|
|
// expect that the single node is selected ~50% of the time
|
|
|
|
assert.InDelta(t, subnetB1Count/total, uniqueSubnet, selectionEpsilon)
|
|
|
|
}
|
2021-10-25 13:27:59 +01:00
|
|
|
|
|
|
|
func TestSelectFiltered(t *testing.T) {
|
|
|
|
|
|
|
|
ctx := testcontext.New(t)
|
|
|
|
defer ctx.Cleanup()
|
|
|
|
|
|
|
|
// create 3 nodes, 2 with same subnet
|
|
|
|
lastNetDuplicate := "1.0.1"
|
|
|
|
firstID := testrand.NodeID()
|
|
|
|
subnetA1 := &uploadselection.Node{
|
|
|
|
NodeURL: storj.NodeURL{
|
|
|
|
ID: firstID,
|
|
|
|
Address: lastNetDuplicate + ".4:8080",
|
|
|
|
},
|
|
|
|
LastNet: lastNetDuplicate,
|
|
|
|
LastIPPort: lastNetDuplicate + ".4:8080",
|
|
|
|
}
|
|
|
|
|
|
|
|
secondID := testrand.NodeID()
|
|
|
|
subnetA2 := &uploadselection.Node{
|
|
|
|
NodeURL: storj.NodeURL{
|
|
|
|
ID: secondID,
|
|
|
|
Address: lastNetDuplicate + ".5:8080",
|
|
|
|
},
|
|
|
|
LastNet: lastNetDuplicate,
|
|
|
|
LastIPPort: lastNetDuplicate + ".5:8080",
|
|
|
|
}
|
|
|
|
|
|
|
|
thirdID := testrand.NodeID()
|
|
|
|
lastNetSingle := "1.0.2"
|
|
|
|
subnetB1 := &uploadselection.Node{
|
|
|
|
NodeURL: storj.NodeURL{
|
|
|
|
ID: thirdID,
|
|
|
|
Address: lastNetSingle + ".5:8080",
|
|
|
|
},
|
|
|
|
LastNet: lastNetSingle,
|
|
|
|
LastIPPort: lastNetSingle + ".5:8080",
|
|
|
|
}
|
|
|
|
|
|
|
|
nodes := []*uploadselection.Node{subnetA1, subnetA2, subnetB1}
|
|
|
|
selector := uploadselection.SelectByID(nodes)
|
|
|
|
|
|
|
|
assert.Len(t, selector.Select(3, uploadselection.Criteria{}), 3)
|
|
|
|
assert.Len(t, selector.Select(3, uploadselection.Criteria{ExcludeNodeIDs: []storj.NodeID{firstID}}), 2)
|
|
|
|
assert.Len(t, selector.Select(3, uploadselection.Criteria{}), 3)
|
|
|
|
|
|
|
|
assert.Len(t, selector.Select(3, uploadselection.Criteria{ExcludeNodeIDs: []storj.NodeID{firstID, secondID}}), 1)
|
|
|
|
assert.Len(t, selector.Select(3, uploadselection.Criteria{
|
|
|
|
AutoExcludeSubnets: map[string]struct{}{},
|
|
|
|
}), 2)
|
|
|
|
assert.Len(t, selector.Select(3, uploadselection.Criteria{
|
|
|
|
ExcludeNodeIDs: []storj.NodeID{thirdID},
|
|
|
|
AutoExcludeSubnets: map[string]struct{}{},
|
|
|
|
}), 1)
|
|
|
|
}
|