forked from TrueCloudLab/frostfs-sdk-go
[#167] netmap: Allow to select insufficient number of nodes
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
This commit is contained in:
parent
0550438b53
commit
555ccc63b2
5 changed files with 120 additions and 37 deletions
|
@ -40,6 +40,10 @@ type context struct {
|
||||||
// policy uses the UNIQUE flag. Nodes marked as used are not used in subsequent
|
// policy uses the UNIQUE flag. Nodes marked as used are not used in subsequent
|
||||||
// base selections.
|
// base selections.
|
||||||
usedNodes map[uint64]bool
|
usedNodes map[uint64]bool
|
||||||
|
|
||||||
|
// If true, returns an error when netmap does not contain enough nodes for selection.
|
||||||
|
// By default best effort is taken.
|
||||||
|
strict bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Various validation errors.
|
// Various validation errors.
|
||||||
|
|
95
netmap/json_tests/non_strict.json
Normal file
95
netmap/json_tests/non_strict.json
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
{
|
||||||
|
"name": "non-strict selections",
|
||||||
|
"comment": "These test specify loose selection behaviour, to allow fetching already PUT objects even when there is not enough nodes to select from.",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "Country",
|
||||||
|
"value": "Russia"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "Country",
|
||||||
|
"value": "Germany"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"attributes": [ ]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tests": {
|
||||||
|
"not enough nodes (backup factor)": {
|
||||||
|
"policy": {
|
||||||
|
"replicas": [
|
||||||
|
{
|
||||||
|
"count": 1,
|
||||||
|
"selector": "MyStore"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"containerBackupFactor": 2,
|
||||||
|
"selectors": [
|
||||||
|
{
|
||||||
|
"name": "MyStore",
|
||||||
|
"count": 2,
|
||||||
|
"clause": "DISTINCT",
|
||||||
|
"attribute": "Country",
|
||||||
|
"filter": "FromRU"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "FromRU",
|
||||||
|
"key": "Country",
|
||||||
|
"op": "EQ",
|
||||||
|
"value": "Russia",
|
||||||
|
"filters": [ ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"result": [
|
||||||
|
[
|
||||||
|
0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"not enough nodes (buckets)": {
|
||||||
|
"policy": {
|
||||||
|
"replicas": [
|
||||||
|
{
|
||||||
|
"count": 1,
|
||||||
|
"selector": "MyStore"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"containerBackupFactor": 1,
|
||||||
|
"selectors": [
|
||||||
|
{
|
||||||
|
"name": "MyStore",
|
||||||
|
"count": 2,
|
||||||
|
"clause": "DISTINCT",
|
||||||
|
"attribute": "Country",
|
||||||
|
"filter": "FromRU"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "FromRU",
|
||||||
|
"key": "Country",
|
||||||
|
"op": "EQ",
|
||||||
|
"value": "Russia",
|
||||||
|
"filters": [ ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"result": [
|
||||||
|
[
|
||||||
|
0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -104,7 +104,14 @@
|
||||||
"selectors": [],
|
"selectors": [],
|
||||||
"filters": []
|
"filters": []
|
||||||
},
|
},
|
||||||
"error": "not enough nodes"
|
"result": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
]
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
},
|
},
|
||||||
"error": "filter not found"
|
"error": "filter not found"
|
||||||
},
|
},
|
||||||
"not enough nodes (backup factor)": {
|
"not enough nodes (filter results in empty set)": {
|
||||||
"policy": {
|
"policy": {
|
||||||
"replicas": [
|
"replicas": [
|
||||||
{
|
{
|
||||||
|
@ -67,45 +67,15 @@
|
||||||
"count": 2,
|
"count": 2,
|
||||||
"clause": "DISTINCT",
|
"clause": "DISTINCT",
|
||||||
"attribute": "Country",
|
"attribute": "Country",
|
||||||
"filter": "FromRU"
|
"filter": "FromMoon"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"filters": [
|
"filters": [
|
||||||
{
|
{
|
||||||
"name": "FromRU",
|
"name": "FromMoon",
|
||||||
"key": "Country",
|
"key": "Country",
|
||||||
"op": "EQ",
|
"op": "EQ",
|
||||||
"value": "Russia",
|
"value": "Moon",
|
||||||
"filters": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"error": "not enough nodes"
|
|
||||||
},
|
|
||||||
"not enough nodes (buckets)": {
|
|
||||||
"policy": {
|
|
||||||
"replicas": [
|
|
||||||
{
|
|
||||||
"count": 1,
|
|
||||||
"selector": "MyStore"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"containerBackupFactor": 1,
|
|
||||||
"selectors": [
|
|
||||||
{
|
|
||||||
"name": "MyStore",
|
|
||||||
"count": 2,
|
|
||||||
"clause": "DISTINCT",
|
|
||||||
"attribute": "Country",
|
|
||||||
"filter": "FromRU"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"filters": [
|
|
||||||
{
|
|
||||||
"name": "FromRU",
|
|
||||||
"key": "Country",
|
|
||||||
"op": "EQ",
|
|
||||||
"value": "Russia",
|
|
||||||
"filters": []
|
"filters": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -60,7 +60,7 @@ func (c *context) getSelection(s netmap.Selector) ([]nodes, error) {
|
||||||
bucketCount, nodesInBucket := calcNodesCount(s)
|
bucketCount, nodesInBucket := calcNodesCount(s)
|
||||||
buckets := c.getSelectionBase(s)
|
buckets := c.getSelectionBase(s)
|
||||||
|
|
||||||
if len(buckets) < bucketCount {
|
if c.strict && len(buckets) < bucketCount {
|
||||||
return nil, fmt.Errorf("%w: '%s'", errNotEnoughNodes, s.GetName())
|
return nil, fmt.Errorf("%w: '%s'", errNotEnoughNodes, s.GetName())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ func (c *context) getSelection(s netmap.Selector) ([]nodes, error) {
|
||||||
if len(res) < bucketCount {
|
if len(res) < bucketCount {
|
||||||
// Fallback to using minimum allowed backup factor (1).
|
// Fallback to using minimum allowed backup factor (1).
|
||||||
res = append(res, fallback...)
|
res = append(res, fallback...)
|
||||||
if len(res) < bucketCount {
|
if c.strict && len(res) < bucketCount {
|
||||||
return nil, fmt.Errorf("%w: '%s'", errNotEnoughNodes, s.GetName())
|
return nil, fmt.Errorf("%w: '%s'", errNotEnoughNodes, s.GetName())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,13 @@ func (c *context) getSelection(s netmap.Selector) ([]nodes, error) {
|
||||||
hrw.SortHasherSliceByWeightValue(res, weights, c.hrwSeedHash)
|
hrw.SortHasherSliceByWeightValue(res, weights, c.hrwSeedHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(res) < bucketCount {
|
||||||
|
if len(res) == 0 {
|
||||||
|
return nil, errNotEnoughNodes
|
||||||
|
}
|
||||||
|
bucketCount = len(res)
|
||||||
|
}
|
||||||
|
|
||||||
if s.GetAttribute() == "" {
|
if s.GetAttribute() == "" {
|
||||||
res, fallback = res[:bucketCount], res[bucketCount:]
|
res, fallback = res[:bucketCount], res[bucketCount:]
|
||||||
for i := range fallback {
|
for i := range fallback {
|
||||||
|
|
Loading…
Reference in a new issue