diff --git a/pkg/netmap/policy_test.go b/pkg/netmap/policy_test.go index 5db6d5b..9fca579 100644 --- a/pkg/netmap/policy_test.go +++ b/pkg/netmap/policy_test.go @@ -4,9 +4,62 @@ import ( "testing" "github.com/nspcc-dev/neofs-api-go/v2/netmap" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +func TestPlacementPolicy_CBFWithEmptySelector(t *testing.T) { + nodes := []NodeInfo{ + nodeInfoFromAttributes("ID", "1", "Attr", "Same"), + nodeInfoFromAttributes("ID", "2", "Attr", "Same"), + nodeInfoFromAttributes("ID", "3", "Attr", "Same"), + nodeInfoFromAttributes("ID", "4", "Attr", "Same"), + } + + p1 := newPlacementPolicy(0, + []*Replica{newReplica(2, "")}, + nil, // selectors + nil, // filters + ) + + p2 := newPlacementPolicy(3, + []*Replica{newReplica(2, "")}, + nil, // selectors + nil, // filters + ) + + p3 := newPlacementPolicy(3, + []*Replica{newReplica(2, "X")}, + []*Selector{newSelector("X", "", ClauseDistinct, 2, "*")}, + nil, // filters + ) + + p4 := newPlacementPolicy(3, + []*Replica{newReplica(2, "X")}, + []*Selector{newSelector("X", "Attr", ClauseSame, 2, "*")}, + nil, // filters + ) + + nm, err := NewNetmap(NodesFromInfo(nodes)) + require.NoError(t, err) + + v, err := nm.GetContainerNodes(p1, nil) + require.NoError(t, err) + assert.Len(t, v.Flatten(), 4) + + v, err = nm.GetContainerNodes(p2, nil) + require.NoError(t, err) + assert.Len(t, v.Flatten(), 4) + + v, err = nm.GetContainerNodes(p3, nil) + require.NoError(t, err) + assert.Len(t, v.Flatten(), 4) + + v, err = nm.GetContainerNodes(p4, nil) + require.NoError(t, err) + assert.Len(t, v.Flatten(), 4) +} + func TestPlacementPolicyFromV2(t *testing.T) { pV2 := new(netmap.PlacementPolicy) diff --git a/pkg/netmap/selector.go b/pkg/netmap/selector.go index ff83f1b..7a7f297 100644 --- a/pkg/netmap/selector.go +++ b/pkg/netmap/selector.go @@ -48,6 +48,7 @@ func GetNodesCount(_ *PlacementPolicy, s *Selector) (int, int) { } // getSelection returns nodes grouped by s.attribute. +// Last argument specifies if more buckets can be used to fullfill CBF. func (c *Context) getSelection(p *PlacementPolicy, s *Selector) ([]Nodes, error) { bucketCount, nodesInBucket := GetNodesCount(p, s) buckets := c.getSelectionBase(s) @@ -99,6 +100,17 @@ func (c *Context) getSelection(p *PlacementPolicy, s *Selector) ([]Nodes, error) hrw.SortSliceByWeightIndex(nodes, weights, c.pivotHash) } + if s.Attribute() == "" { + nodes, fallback = nodes[:bucketCount], nodes[bucketCount:] + for i := range fallback { + index := i % bucketCount + if len(nodes[index]) >= maxNodesInBucket { + break + } + nodes[index] = append(nodes[index], fallback[i]...) + } + } + return nodes[:bucketCount], nil } diff --git a/pkg/netmap/selector_test.go b/pkg/netmap/selector_test.go index 135a7b3..e434821 100644 --- a/pkg/netmap/selector_test.go +++ b/pkg/netmap/selector_test.go @@ -54,7 +54,11 @@ func TestPlacementPolicy_Minimal(t *testing.T) { } require.NoError(t, err) - require.EqualValues(t, rep, len(v.Flatten())) + count := int(rep * defaultCBF) + if count > len(nm.Nodes) { + count = len(nm.Nodes) + } + require.EqualValues(t, count, len(v.Flatten())) } t.Run("REP 1", func(t *testing.T) {