frostfs-node/pkg/innerring/processors/netmap/cleanup_table.go

123 lines
2.5 KiB
Go

package netmap
import (
"bytes"
"sync"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
)
type (
cleanupTable struct {
sync.RWMutex
enabled bool
threshold uint64
lastAccess map[string]epochStampWithNodeInfo
}
epochStamp struct {
epoch uint64
removeFlag bool
}
epochStampWithNodeInfo struct {
epochStamp
binNodeInfo []byte
maintenance bool
}
)
func newCleanupTable(enabled bool, threshold uint64) cleanupTable {
return cleanupTable{
enabled: enabled,
threshold: threshold,
lastAccess: make(map[string]epochStampWithNodeInfo),
}
}
// Update cleanup table based on on-chain information about netmap.
func (c *cleanupTable) update(snapshot netmap.NetMap, now uint64) {
c.Lock()
defer c.Unlock()
nmNodes := snapshot.Nodes()
// replacing map is less memory efficient but faster
newMap := make(map[string]epochStampWithNodeInfo, len(nmNodes))
for i := range nmNodes {
binNodeInfo := nmNodes[i].Marshal()
keyString := netmap.StringifyPublicKey(nmNodes[i])
access, ok := c.lastAccess[keyString]
if ok {
access.removeFlag = false // reset remove Flag on each Update
} else {
access.epoch = now
}
access.binNodeInfo = binNodeInfo
access.maintenance = nmNodes[i].IsMaintenance()
newMap[keyString] = access
}
c.lastAccess = newMap
}
// updates last access time of the netmap node by string public key.
//
// Returns true if at least one condition is met:
// - node hasn't been accessed yet;
// - remove flag is set;
// - binary node info has changed.
func (c *cleanupTable) touch(keyString string, now uint64, binNodeInfo []byte) bool {
c.Lock()
defer c.Unlock()
access, ok := c.lastAccess[keyString]
result := !ok || access.removeFlag || !bytes.Equal(access.binNodeInfo, binNodeInfo)
access.removeFlag = false // reset remove flag on each touch
if now > access.epoch {
access.epoch = now
}
access.binNodeInfo = binNodeInfo // update binary node info
c.lastAccess[keyString] = access
return result
}
func (c *cleanupTable) flag(keyString string) {
c.Lock()
defer c.Unlock()
if access, ok := c.lastAccess[keyString]; ok {
access.removeFlag = true
c.lastAccess[keyString] = access
}
}
func (c *cleanupTable) forEachRemoveCandidate(epoch uint64, f func(string) error) error {
c.Lock()
defer c.Unlock()
for keyString, access := range c.lastAccess {
if !access.maintenance && epoch-access.epoch > c.threshold {
access.removeFlag = true // set remove flag
c.lastAccess[keyString] = access
if err := f(keyString); err != nil {
return err
}
}
}
return nil
}