forked from TrueCloudLab/frostfs-api-go
[#356] netmap: Fix potential double-processing of zero subnet
Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
parent
051b103df3
commit
4560e447e1
2 changed files with 72 additions and 29 deletions
|
@ -126,11 +126,24 @@ var errNoSubnets = errors.New("no subnets")
|
||||||
func IterateSubnets(node *NodeInfo, f func(refs.SubnetID) error) error {
|
func IterateSubnets(node *NodeInfo, f func(refs.SubnetID) error) error {
|
||||||
attrs := node.GetAttributes()
|
attrs := node.GetAttributes()
|
||||||
|
|
||||||
|
type zeroStatus uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ zeroStatus = iota
|
||||||
|
// missing attribute of zero subnet
|
||||||
|
zeroNoAttr
|
||||||
|
// with `False` attribute
|
||||||
|
zeroExit
|
||||||
|
// with `True` attribute
|
||||||
|
zeroEntry
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
id refs.SubnetID
|
id refs.SubnetID
|
||||||
metZero bool // if zero subnet's attribute was met in for-loop
|
|
||||||
entries uint
|
entries uint
|
||||||
|
|
||||||
|
stZero = zeroNoAttr
|
||||||
)
|
)
|
||||||
|
|
||||||
for i := 0; i < len(attrs); i++ { // range must not be used because of attrs mutation in body
|
for i := 0; i < len(attrs); i++ { // range must not be used because of attrs mutation in body
|
||||||
|
@ -144,14 +157,9 @@ func IterateSubnets(node *NodeInfo, f func(refs.SubnetID) error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check value
|
// check value
|
||||||
switch val := attrs[i].GetValue(); val {
|
val := attrs[i].GetValue()
|
||||||
default:
|
if val != attrSubnetValExit && val != attrSubnetValEntry {
|
||||||
return fmt.Errorf("invalid attribute value: %s", val)
|
return fmt.Errorf("invalid attribute value: %s", val)
|
||||||
case attrSubnetValExit:
|
|
||||||
// node is outside the subnet
|
|
||||||
continue
|
|
||||||
case attrSubnetValEntry:
|
|
||||||
// required to avoid default case
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// decode subnet ID
|
// decode subnet ID
|
||||||
|
@ -159,6 +167,25 @@ func IterateSubnets(node *NodeInfo, f func(refs.SubnetID) error) error {
|
||||||
return fmt.Errorf("invalid ID text: %w", err)
|
return fmt.Errorf("invalid ID text: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update status of zero subnet
|
||||||
|
isZero := refs.IsZeroSubnet(&id)
|
||||||
|
|
||||||
|
if stZero == zeroNoAttr { // in order to not reset if has been already set
|
||||||
|
if isZero {
|
||||||
|
if val == attrSubnetValEntry {
|
||||||
|
// clear True attribute for zero subnet is also possible
|
||||||
|
stZero = zeroEntry
|
||||||
|
} else {
|
||||||
|
stZero = zeroExit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// continue to process only the subnets to which the node belongs
|
||||||
|
if val == attrSubnetValExit {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// pass ID to the handler
|
// pass ID to the handler
|
||||||
err = f(id)
|
err = f(id)
|
||||||
|
|
||||||
|
@ -168,34 +195,23 @@ func IterateSubnets(node *NodeInfo, f func(refs.SubnetID) error) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !metZero { // in order to not reset if has been already set
|
|
||||||
metZero = refs.IsZeroSubnet(&id)
|
|
||||||
|
|
||||||
if !isRemoveErr {
|
|
||||||
// no handler's error and non-zero subnet
|
|
||||||
entries++
|
|
||||||
continue
|
|
||||||
} else if metZero {
|
|
||||||
// removal error and zero subnet.
|
|
||||||
// we don't remove attribute of zero subnet because it means entry
|
|
||||||
attrs[i].SetValue(attrSubnetValExit)
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isRemoveErr {
|
if isRemoveErr {
|
||||||
// removal error and non-zero subnet.
|
if isZero {
|
||||||
// we can set False or remove attribute, latter is more memory/network efficient.
|
// we can't remove attribute of zero subnet because it means entry
|
||||||
attrs = append(attrs[:i], attrs[i+1:]...)
|
attrs[i].SetValue(attrSubnetValExit)
|
||||||
i--
|
} else {
|
||||||
|
// we can set False or remove attribute, latter is more memory/network efficient.
|
||||||
|
attrs = append(attrs[:i], attrs[i+1:]...)
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
entries++
|
entries++
|
||||||
}
|
}
|
||||||
|
|
||||||
if !metZero {
|
if stZero == zeroNoAttr {
|
||||||
// missing attribute of zero subnet equivalent to entry
|
// missing attribute of zero subnet equivalent to entry
|
||||||
refs.MakeZeroSubnet(&id)
|
refs.MakeZeroSubnet(&id)
|
||||||
|
|
||||||
|
|
|
@ -264,4 +264,31 @@ func TestSubnets(t *testing.T) {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("zero subnet removal via attribute", func(t *testing.T) {
|
||||||
|
var (
|
||||||
|
node netmap.NodeInfo
|
||||||
|
|
||||||
|
attrZero, attrOther netmap.Attribute
|
||||||
|
)
|
||||||
|
|
||||||
|
attrZero.SetKey(subnetAttrKey("0"))
|
||||||
|
attrZero.SetValue("False")
|
||||||
|
|
||||||
|
attrOther.SetKey(subnetAttrKey("1"))
|
||||||
|
attrOther.SetValue("True")
|
||||||
|
|
||||||
|
node.SetAttributes([]*netmap.Attribute{&attrZero, &attrOther})
|
||||||
|
|
||||||
|
calledCount := 0
|
||||||
|
|
||||||
|
err := netmap.IterateSubnets(&node, func(id refs.SubnetID) error {
|
||||||
|
require.False(t, refs.IsZeroSubnet(&id))
|
||||||
|
calledCount++
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, 1, calledCount)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue