Simplify in-memory pilorama implementation #288
3 changed files with 46 additions and 76 deletions
|
@ -12,7 +12,7 @@ import (
|
||||||
// memoryForest represents multiple replicating trees sharing a single storage.
|
// memoryForest represents multiple replicating trees sharing a single storage.
|
||||||
type memoryForest struct {
|
type memoryForest struct {
|
||||||
// treeMap maps tree identifier (container ID + name) to the replicated log.
|
// treeMap maps tree identifier (container ID + name) to the replicated log.
|
||||||
treeMap map[string]*state
|
treeMap map[string]*memoryTree
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Forest = (*memoryForest)(nil)
|
var _ Forest = (*memoryForest)(nil)
|
||||||
|
@ -21,7 +21,7 @@ var _ Forest = (*memoryForest)(nil)
|
||||||
// TODO: this function will eventually be removed and is here for debugging.
|
// TODO: this function will eventually be removed and is here for debugging.
|
||||||
func NewMemoryForest() ForestStorage {
|
func NewMemoryForest() ForestStorage {
|
||||||
return &memoryForest{
|
return &memoryForest{
|
||||||
treeMap: make(map[string]*state),
|
treeMap: make(map[string]*memoryTree),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ func (f *memoryForest) TreeMove(_ context.Context, d CIDDescriptor, treeID strin
|
||||||
fullID := d.CID.String() + "/" + treeID
|
fullID := d.CID.String() + "/" + treeID
|
||||||
s, ok := f.treeMap[fullID]
|
s, ok := f.treeMap[fullID]
|
||||||
if !ok {
|
if !ok {
|
||||||
s = newState()
|
s = newMemoryTree()
|
||||||
f.treeMap[fullID] = s
|
f.treeMap[fullID] = s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ func (f *memoryForest) TreeAddByPath(_ context.Context, d CIDDescriptor, treeID
|
||||||
fullID := d.CID.String() + "/" + treeID
|
fullID := d.CID.String() + "/" + treeID
|
||||||
s, ok := f.treeMap[fullID]
|
s, ok := f.treeMap[fullID]
|
||||||
if !ok {
|
if !ok {
|
||||||
s = newState()
|
s = newMemoryTree()
|
||||||
f.treeMap[fullID] = s
|
f.treeMap[fullID] = s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +89,7 @@ func (f *memoryForest) TreeAddByPath(_ context.Context, d CIDDescriptor, treeID
|
||||||
},
|
},
|
||||||
Child: s.findSpareID(),
|
Child: s.findSpareID(),
|
||||||
})
|
})
|
||||||
|
s.operations = append(s.operations, op)
|
||||||
lm[len(lm)-1] = op.Move
|
lm[len(lm)-1] = op.Move
|
||||||
return lm, nil
|
return lm, nil
|
||||||
}
|
}
|
||||||
|
@ -98,7 +99,7 @@ func (f *memoryForest) TreeApply(_ context.Context, cnr cid.ID, treeID string, o
|
||||||
fullID := cnr.String() + "/" + treeID
|
fullID := cnr.String() + "/" + treeID
|
||||||
s, ok := f.treeMap[fullID]
|
s, ok := f.treeMap[fullID]
|
||||||
if !ok {
|
if !ok {
|
||||||
s = newState()
|
s = newMemoryTree()
|
||||||
f.treeMap[fullID] = s
|
f.treeMap[fullID] = s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +132,7 @@ func (f *memoryForest) TreeGetByPath(_ context.Context, cid cid.ID, treeID strin
|
||||||
return nil, ErrTreeNotFound
|
return nil, ErrTreeNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.get(attr, path, latest), nil
|
return s.getByPath(attr, path, latest), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeGetMeta implements the Forest interface.
|
// TreeGetMeta implements the Forest interface.
|
||||||
|
@ -142,7 +143,7 @@ func (f *memoryForest) TreeGetMeta(_ context.Context, cid cid.ID, treeID string,
|
||||||
return Meta{}, 0, ErrTreeNotFound
|
return Meta{}, 0, ErrTreeNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.getMeta(nodeID), s.infoMap[nodeID].Parent, nil
|
return s.infoMap[nodeID].Meta, s.infoMap[nodeID].Parent, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TreeGetChildren implements the Forest interface.
|
// TreeGetChildren implements the Forest interface.
|
||||||
|
@ -153,11 +154,7 @@ func (f *memoryForest) TreeGetChildren(_ context.Context, cid cid.ID, treeID str
|
||||||
return nil, ErrTreeNotFound
|
return nil, ErrTreeNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
children, ok := s.childMap[nodeID]
|
children := s.tree.getChildren(nodeID)
|
||||||
if !ok {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
res := make([]Node, len(children))
|
res := make([]Node, len(children))
|
||||||
copy(res, children)
|
copy(res, children)
|
||||||
return res, nil
|
return res, nil
|
||||||
|
|
|
@ -322,6 +322,10 @@ func testForestTreeAddByPath(t *testing.T, s Forest) {
|
||||||
firstID := lm[2].Child
|
firstID := lm[2].Child
|
||||||
testMeta(t, s, cid, treeID, firstID, lm[2].Parent, Meta{Time: lm[2].Time, Items: meta})
|
testMeta(t, s, cid, treeID, firstID, lm[2].Parent, Meta{Time: lm[2].Time, Items: meta})
|
||||||
|
|
||||||
|
// TreeAddByPath must return operations in increasing time order.
|
||||||
|
require.True(t, lm[0].Time < lm[1].Time)
|
||||||
|
require.True(t, lm[1].Time < lm[2].Time)
|
||||||
|
|
||||||
meta[0].Value = []byte("YYY")
|
meta[0].Value = []byte("YYY")
|
||||||
lm, err = s.TreeAddByPath(context.Background(), d, treeID, AttributeFilename, []string{"path", "to"}, meta)
|
lm, err = s.TreeAddByPath(context.Background(), d, treeID, AttributeFilename, []string{"path", "to"}, meta)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -714,13 +718,6 @@ func compareForests(t *testing.T, expected, actual Forest, cid cidSDK.ID, treeID
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.Equal(t, se.operations, sa.operations)
|
require.Equal(t, se.operations, sa.operations)
|
||||||
require.Equal(t, se.infoMap, sa.infoMap)
|
require.Equal(t, se.infoMap, sa.infoMap)
|
||||||
|
|
||||||
require.Equal(t, len(se.childMap), len(sa.childMap))
|
|
||||||
for ck, la := range sa.childMap {
|
|
||||||
le, ok := se.childMap[ck]
|
|
||||||
require.True(t, ok)
|
|
||||||
require.ElementsMatch(t, le, la)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
require.Equal(t, expected, actual, i)
|
require.Equal(t, expected, actual, i)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package pilorama
|
package pilorama
|
||||||
|
|
||||||
|
import "sort"
|
||||||
|
|
||||||
// nodeInfo couples parent and metadata.
|
// nodeInfo couples parent and metadata.
|
||||||
type nodeInfo struct {
|
type nodeInfo struct {
|
||||||
Parent Node
|
Parent Node
|
||||||
|
@ -12,42 +14,25 @@ type move struct {
|
||||||
Old nodeInfo
|
Old nodeInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// state represents state being replicated.
|
// memoryTree represents memoryTree being replicated.
|
||||||
type state struct {
|
type memoryTree struct {
|
||||||
operations []move
|
operations []move
|
||||||
tree
|
tree
|
||||||
}
|
}
|
||||||
|
|
||||||
// newState constructs new empty tree.
|
// newMemoryTree constructs new empty tree.
|
||||||
func newState() *state {
|
func newMemoryTree() *memoryTree {
|
||||||
return &state{
|
return &memoryTree{
|
||||||
tree: *newTree(),
|
tree: tree{
|
||||||
|
infoMap: make(map[Node]nodeInfo),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// undo un-does op and changes s in-place.
|
// undo un-does op and changes s in-place.
|
||||||
func (s *state) undo(op *move) {
|
func (s *memoryTree) undo(op *move) {
|
||||||
children := s.tree.childMap[op.Parent]
|
|
||||||
for i := range children {
|
|
||||||
if children[i] == op.Child {
|
|
||||||
if len(children) > 1 {
|
|
||||||
s.tree.childMap[op.Parent] = append(children[:i], children[i+1:]...)
|
|
||||||
} else {
|
|
||||||
delete(s.tree.childMap, op.Parent)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if op.HasOld {
|
if op.HasOld {
|
||||||
s.tree.infoMap[op.Child] = op.Old
|
s.tree.infoMap[op.Child] = op.Old
|
||||||
oldChildren := s.tree.childMap[op.Old.Parent]
|
|
||||||
for i := range oldChildren {
|
|
||||||
if oldChildren[i] == op.Child {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.tree.childMap[op.Old.Parent] = append(oldChildren, op.Child)
|
|
||||||
} else {
|
} else {
|
||||||
delete(s.tree.infoMap, op.Child)
|
delete(s.tree.infoMap, op.Child)
|
||||||
}
|
}
|
||||||
|
@ -55,7 +40,7 @@ func (s *state) undo(op *move) {
|
||||||
|
|
||||||
// Apply puts op in log at a proper position, re-applies all subsequent operations
|
// Apply puts op in log at a proper position, re-applies all subsequent operations
|
||||||
// from log and changes s in-place.
|
// from log and changes s in-place.
|
||||||
func (s *state) Apply(op *Move) error {
|
func (s *memoryTree) Apply(op *Move) error {
|
||||||
var index int
|
var index int
|
||||||
for index = len(s.operations); index > 0; index-- {
|
for index = len(s.operations); index > 0; index-- {
|
||||||
if s.operations[index-1].Time <= op.Time {
|
if s.operations[index-1].Time <= op.Time {
|
||||||
|
@ -82,7 +67,7 @@ func (s *state) Apply(op *Move) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// do performs a single move operation on a tree.
|
// do performs a single move operation on a tree.
|
||||||
func (s *state) do(op *Move) move {
|
func (s *memoryTree) do(op *Move) move {
|
||||||
lm := move{
|
lm := move{
|
||||||
Move: Move{
|
Move: Move{
|
||||||
Parent: op.Parent,
|
Parent: op.Parent,
|
||||||
|
@ -104,36 +89,23 @@ func (s *state) do(op *Move) move {
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
p.Meta.Time = op.Time
|
p.Meta.Time = op.Time
|
||||||
} else {
|
|
||||||
s.removeChild(op.Child, p.Parent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p.Meta = op.Meta
|
p.Meta = op.Meta
|
||||||
p.Parent = op.Parent
|
p.Parent = op.Parent
|
||||||
s.tree.infoMap[op.Child] = p
|
s.tree.infoMap[op.Child] = p
|
||||||
s.tree.childMap[op.Parent] = append(s.tree.childMap[op.Parent], op.Child)
|
|
||||||
|
|
||||||
return lm
|
return lm
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state) removeChild(child, parent Node) {
|
func (s *memoryTree) timestamp(pos, size int) Timestamp {
|
||||||
oldChildren := s.tree.childMap[parent]
|
|
||||||
for i := range oldChildren {
|
|
||||||
if oldChildren[i] == child {
|
|
||||||
s.tree.childMap[parent] = append(oldChildren[:i], oldChildren[i+1:]...)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) timestamp(pos, size int) Timestamp {
|
|
||||||
if len(s.operations) == 0 {
|
if len(s.operations) == 0 {
|
||||||
return nextTimestamp(0, uint64(pos), uint64(size))
|
return nextTimestamp(0, uint64(pos), uint64(size))
|
||||||
}
|
}
|
||||||
return nextTimestamp(s.operations[len(s.operations)-1].Time, uint64(pos), uint64(size))
|
return nextTimestamp(s.operations[len(s.operations)-1].Time, uint64(pos), uint64(size))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state) findSpareID() Node {
|
func (s *memoryTree) findSpareID() Node {
|
||||||
id := uint64(1)
|
id := uint64(1)
|
||||||
for _, ok := s.infoMap[id]; ok; _, ok = s.infoMap[id] {
|
for _, ok := s.infoMap[id]; ok; _, ok = s.infoMap[id] {
|
||||||
id++
|
id++
|
||||||
|
@ -145,14 +117,22 @@ func (s *state) findSpareID() Node {
|
||||||
type tree struct {
|
type tree struct {
|
||||||
syncHeight uint64
|
syncHeight uint64
|
||||||
infoMap map[Node]nodeInfo
|
infoMap map[Node]nodeInfo
|
||||||
childMap map[Node][]Node
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTree() *tree {
|
func (t tree) getChildren(parent Node) []Node {
|
||||||
return &tree{
|
var children []Node
|
||||||
childMap: make(map[Node][]Node),
|
for c, info := range t.infoMap {
|
||||||
infoMap: make(map[Node]nodeInfo),
|
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.
|
// isAncestor returns true if parent is an ancestor of a child.
|
||||||
|
@ -176,7 +156,7 @@ func (t tree) getPathPrefix(attr string, path []string) (int, Node) {
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
for i := range path {
|
for i := range path {
|
||||||
children := t.childMap[curNode]
|
children := t.getChildren(curNode)
|
||||||
for j := range children {
|
for j := range children {
|
||||||
meta := t.infoMap[children[j]].Meta
|
meta := t.infoMap[children[j]].Meta
|
||||||
f := meta.GetAttr(attr)
|
f := meta.GetAttr(attr)
|
||||||
|
@ -191,9 +171,10 @@ loop:
|
||||||
return len(path), curNode
|
return len(path), curNode
|
||||||
}
|
}
|
||||||
|
|
||||||
// get returns list of nodes which have the specified path from root
|
// getByPath returns list of nodes which have the specified path from root
|
||||||
// descending by values of attr from meta.
|
// descending by values of attr from meta.
|
||||||
func (t tree) get(attr string, path []string, latest bool) []Node {
|
// 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 {
|
if len(path) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -206,7 +187,7 @@ func (t tree) get(attr string, path []string, latest bool) []Node {
|
||||||
var nodes []Node
|
var nodes []Node
|
||||||
var lastTs Timestamp
|
var lastTs Timestamp
|
||||||
|
|
||||||
children := t.childMap[curNode]
|
children := t.getChildren(curNode)
|
||||||
for i := range children {
|
for i := range children {
|
||||||
info := t.infoMap[children[i]]
|
info := t.infoMap[children[i]]
|
||||||
fileName := string(info.Meta.GetAttr(attr))
|
fileName := string(info.Meta.GetAttr(attr))
|
||||||
|
@ -223,8 +204,3 @@ func (t tree) get(attr string, path []string, latest bool) []Node {
|
||||||
|
|
||||||
return nodes
|
return nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
// getMeta returns meta information of node n.
|
|
||||||
func (t tree) getMeta(n Node) Meta {
|
|
||||||
return t.infoMap[n].Meta
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue