frostfs-node/pkg/local_object_storage/pilorama/inmemory.go
Dmitrii Stepanov af82c2865e [] treesvc: Fix inmemory unit tests and nil meta items
Bolt forest saves empty slice of items. Now inmemory forest
does it the same way.

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2023-07-20 10:14:10 +03:00

210 lines
4.3 KiB
Go

package pilorama
import "sort"
// nodeInfo couples parent and metadata.
type nodeInfo struct {
Parent Node
Meta Meta
}
type move struct {
Move
HasOld bool
Old nodeInfo
}
// memoryTree represents memoryTree being replicated.
type memoryTree struct {
operations []move
tree
}
// newMemoryTree constructs new empty tree.
func newMemoryTree() *memoryTree {
return &memoryTree{
tree: tree{
infoMap: make(map[Node]nodeInfo),
},
}
}
// undo un-does op and changes s in-place.
func (s *memoryTree) undo(op *move) {
if op.HasOld {
s.tree.infoMap[op.Child] = op.Old
} else {
delete(s.tree.infoMap, op.Child)
}
}
// Apply puts op in log at a proper position, re-applies all subsequent operations
// from log and changes s in-place.
func (s *memoryTree) Apply(op *Move) error {
var index int
for index = len(s.operations); index > 0; index-- {
if s.operations[index-1].Time <= op.Time {
break
}
}
if index == len(s.operations) {
s.operations = append(s.operations, s.do(op))
return nil
}
s.operations = append(s.operations[:index+1], s.operations[index:]...)
for i := len(s.operations) - 1; i > index; i-- {
s.undo(&s.operations[i])
}
s.operations[index] = s.do(op)
for i := index + 1; i < len(s.operations); i++ {
s.operations[i] = s.do(&s.operations[i].Move)
}
return nil
}
// do performs a single move operation on a tree.
func (s *memoryTree) do(op *Move) move {
m := op.Meta
if m.Items == nil {
m.Items = []KeyValue{}
}
lm := move{
Move: Move{
Parent: op.Parent,
Meta: m,
Child: op.Child,
},
}
shouldPut := !s.tree.isAncestor(op.Child, op.Parent)
p, ok := s.tree.infoMap[op.Child]
if ok {
lm.HasOld = true
lm.Old = p
}
if !shouldPut {
return lm
}
if !ok {
p.Meta.Time = op.Time
}
p.Meta = m
p.Parent = op.Parent
s.tree.infoMap[op.Child] = p
return lm
}
func (s *memoryTree) timestamp(pos, size int) Timestamp {
if len(s.operations) == 0 {
return nextTimestamp(0, uint64(pos), uint64(size))
}
return nextTimestamp(s.operations[len(s.operations)-1].Time, uint64(pos), uint64(size))
}
func (s *memoryTree) findSpareID() Node {
id := uint64(1)
for _, ok := s.infoMap[id]; ok; _, ok = s.infoMap[id] {
id++
}
return id
}
// tree is a mapping from the child nodes to their parent and metadata.
type tree struct {
syncHeight uint64
infoMap map[Node]nodeInfo
}
func (t tree) getChildren(parent Node) []Node {
var children []Node
for c, info := range t.infoMap {
if info.Parent == parent {
children = append(children, c)
}
}
sort.Slice(children, func(i, j int) bool {
a := t.infoMap[children[i]]
b := t.infoMap[children[j]]
return a.Meta.Time < b.Meta.Time
})
return children
}
// isAncestor returns true if parent is an ancestor of a child.
// For convenience, also return true if parent == child.
func (t tree) isAncestor(parent, child Node) bool {
for c := child; c != parent; {
p, ok := t.infoMap[c]
if !ok {
return false
}
c = p.Parent
}
return true
}
// getPathPrefix descends by path constructed from values of attr until
// there is no node corresponding to a path element. Returns the amount of nodes
// processed and ID of the last node.
func (t tree) getPathPrefix(attr string, path []string) (int, Node) {
var curNode Node
loop:
for i := range path {
children := t.getChildren(curNode)
for j := range children {
meta := t.infoMap[children[j]].Meta
f := meta.GetAttr(attr)
if len(meta.Items) == 1 && string(f) == path[i] {
curNode = children[j]
continue loop
}
}
return i, curNode
}
return len(path), curNode
}
// getByPath returns list of nodes which have the specified path from root
// descending by values of attr from meta.
// If latest is true, only the latest node is returned.
func (t tree) getByPath(attr string, path []string, latest bool) []Node {
if len(path) == 0 {
return nil
}
i, curNode := t.getPathPrefix(attr, path[:len(path)-1])
if i < len(path)-1 {
return nil
}
var nodes []Node
var lastTs Timestamp
children := t.getChildren(curNode)
for i := range children {
info := t.infoMap[children[i]]
fileName := string(info.Meta.GetAttr(attr))
if fileName == path[len(path)-1] {
if latest {
if info.Meta.Time >= lastTs {
nodes = append(nodes[:0], children[i])
}
} else {
nodes = append(nodes, children[i])
}
}
}
return nodes
}