Commit 11c95175 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent 042c5eeb
...@@ -102,7 +102,7 @@ func (S PPTreeSubSet) AddPath(path []zodb.Oid) { ...@@ -102,7 +102,7 @@ func (S PPTreeSubSet) AddPath(path []zodb.Oid) {
// normalize path: remove embedded bucket and check whether it was an // normalize path: remove embedded bucket and check whether it was an
// artificial empty tree. // artificial empty tree.
path = normPath(path) path = NormPath(path)
// go through path and add nodes to the set // go through path and add nodes to the set
parent := zodb.InvalidOid parent := zodb.InvalidOid
...@@ -132,11 +132,11 @@ func (S PPTreeSubSet) AddPath(path []zodb.Oid) { ...@@ -132,11 +132,11 @@ func (S PPTreeSubSet) AddPath(path []zodb.Oid) {
} }
} }
// normPath normalizes path. // NormPath normalizes path.
// //
// It removes embedded buckets and artificial empty trees. // It removes embedded buckets and artificial empty trees.
// Returned slice is subslice of path and aliases its memory. // Returned slice is subslice of path and aliases its memory.
func normPath(path []zodb.Oid) []zodb.Oid { func NormPath(path []zodb.Oid) []zodb.Oid {
l := len(path) l := len(path)
// don't keep track of artificial empty tree // don't keep track of artificial empty tree
......
...@@ -80,6 +80,7 @@ import ( ...@@ -80,6 +80,7 @@ import (
"lab.nexedi.com/kirr/neo/go/zodb" "lab.nexedi.com/kirr/neo/go/zodb"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xzodb" "lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xzodb"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xbtree/blib"
) )
const traceDiff = false const traceDiff = false
...@@ -113,7 +114,7 @@ func (δv ΔValue) String() string { ...@@ -113,7 +114,7 @@ func (δv ΔValue) String() string {
// for example for e.g. t₀->t₁->b₂ if δZ/T={t₀ b₂} -> δZ/TC=δZ/T+{t₁} // for example for e.g. t₀->t₁->b₂ if δZ/T={t₀ b₂} -> δZ/TC=δZ/T+{t₁}
// //
// δtopsByRoot = {} root -> {top changed nodes in that tree} // δtopsByRoot = {} root -> {top changed nodes in that tree}
func δZConnectTracked(δZv []zodb.Oid, T PPTreeSubSet) (δZTC setOid, δtopsByRoot map[zodb.Oid]setOid) { func δZConnectTracked(δZv []zodb.Oid, T blib.PPTreeSubSet) (δZTC setOid, δtopsByRoot map[zodb.Oid]setOid) {
δZ := setOid{}; for _, δ := range δZv { δZ.Add(δ) } δZ := setOid{}; for _, δ := range δZv { δZ.Add(δ) }
δZTC = setOid{} δZTC = setOid{}
δtopsByRoot = map[zodb.Oid]setOid{} δtopsByRoot = map[zodb.Oid]setOid{}
...@@ -338,18 +339,18 @@ func (rs rangeSplit) String() string { ...@@ -338,18 +339,18 @@ func (rs rangeSplit) String() string {
// δtops is set of top nodes for changed subtrees. // δtops is set of top nodes for changed subtrees.
// δZTC is connected(δZ/T) - connected closure for subset of δZ(old..new) that // δZTC is connected(δZ/T) - connected closure for subset of δZ(old..new) that
// touches tracked nodes of T. // touches tracked nodes of T.
func treediff(ctx context.Context, root zodb.Oid, δtops setOid, δZTC setOid, trackSet PPTreeSubSet, zconnOld, zconnNew *zodb.Connection) (δT map[Key]ΔValue, δtrack *ΔPPTreeSubSet, δtkeycov *RangedKeySet, err error) { func treediff(ctx context.Context, root zodb.Oid, δtops setOid, δZTC setOid, trackSet blib.PPTreeSubSet, zconnOld, zconnNew *zodb.Connection) (δT map[Key]ΔValue, δtrack *blib.ΔPPTreeSubSet, δtkeycov *blib.RangedKeySet, err error) {
defer xerr.Contextf(&err, "treediff %s..%s %s", zconnOld.At(), zconnNew.At(), root) defer xerr.Contextf(&err, "treediff %s..%s %s", zconnOld.At(), zconnNew.At(), root)
δT = map[Key]ΔValue{} δT = map[Key]ΔValue{}
δtrack = NewΔPPTreeSubSet() δtrack = blib.NewΔPPTreeSubSet()
δtkeycov = &RangedKeySet{} δtkeycov = &blib.RangedKeySet{}
tracefDiff("\ntreediff %s δtops: %v δZTC: %v\n", root, δtops, δZTC) tracefDiff("\ntreediff %s δtops: %v δZTC: %v\n", root, δtops, δZTC)
tracefDiff(" trackSet: %v\n", trackSet) tracefDiff(" trackSet: %v\n", trackSet)
defer tracefDiff("\n-> δT: %v\nδtrack: %v\nδtkeycov: %v\n", δT, δtrack, δtkeycov) defer tracefDiff("\n-> δT: %v\nδtrack: %v\nδtkeycov: %v\n", δT, δtrack, δtkeycov)
δtrackv := []*ΔPPTreeSubSet{} δtrackv := []*blib.ΔPPTreeSubSet{}
for top := range δtops { // XXX -> sorted? for top := range δtops { // XXX -> sorted?
a, err1 := zgetNodeOrNil(ctx, zconnOld, top) a, err1 := zgetNodeOrNil(ctx, zconnOld, top)
...@@ -401,7 +402,7 @@ func treediff(ctx context.Context, root zodb.Oid, δtops setOid, δZTC setOid, t ...@@ -401,7 +402,7 @@ func treediff(ctx context.Context, root zodb.Oid, δtops setOid, δZTC setOid, t
// consistent with b (= a + δ). // consistent with b (= a + δ).
// //
// δtkeycov represents how δtrack grows (always grows) tracking set key coverage. // δtkeycov represents how δtrack grows (always grows) tracking set key coverage.
func diffX(ctx context.Context, a, b Node, δZTC setOid, trackSet PPTreeSubSet) (δ map[Key]ΔValue, δtrack *ΔPPTreeSubSet, δtkeycov *RangedKeySet, err error) { func diffX(ctx context.Context, a, b Node, δZTC setOid, trackSet blib.PPTreeSubSet) (δ map[Key]ΔValue, δtrack *blib.ΔPPTreeSubSet, δtkeycov *blib.RangedKeySet, err error) {
if a==nil && b==nil { if a==nil && b==nil {
panic("BUG: both a & b == nil") // XXX -> not a bug e.g. for `ø ø T` sequence? panic("BUG: both a & b == nil") // XXX -> not a bug e.g. for `ø ø T` sequence?
} }
...@@ -438,11 +439,11 @@ func diffX(ctx context.Context, a, b Node, δZTC setOid, trackSet PPTreeSubSet) ...@@ -438,11 +439,11 @@ func diffX(ctx context.Context, a, b Node, δZTC setOid, trackSet PPTreeSubSet)
if isT { if isT {
return diffT(ctx, aT, bT, δZTC, trackSet) return diffT(ctx, aT, bT, δZTC, trackSet)
} else { } else {
var δtrack *ΔPPTreeSubSet var δtrack *blib.ΔPPTreeSubSet
δ, err := diffB(ctx, aB, bB) δ, err := diffB(ctx, aB, bB)
if δ != nil { if δ != nil {
δtrack = NewΔPPTreeSubSet() δtrack = blib.NewΔPPTreeSubSet()
δtkeycov = &RangedKeySet{} δtkeycov = &blib.RangedKeySet{}
} }
return δ, δtrack, δtkeycov, err return δ, δtrack, δtkeycov, err
} }
...@@ -452,13 +453,13 @@ func diffX(ctx context.Context, a, b Node, δZTC setOid, trackSet PPTreeSubSet) ...@@ -452,13 +453,13 @@ func diffX(ctx context.Context, a, b Node, δZTC setOid, trackSet PPTreeSubSet)
// //
// a, b point to top of subtrees @old and @new revisions. // a, b point to top of subtrees @old and @new revisions.
// δZTC is connected set of objects covering δZT (objects changed in this tree in old..new). // δZTC is connected set of objects covering δZT (objects changed in this tree in old..new).
func diffT(ctx context.Context, A, B *Tree, δZTC setOid, trackSet PPTreeSubSet) (δ map[Key]ΔValue, δtrack *ΔPPTreeSubSet, δtkeycov *RangedKeySet, err error) { func diffT(ctx context.Context, A, B *Tree, δZTC setOid, trackSet blib.PPTreeSubSet) (δ map[Key]ΔValue, δtrack *blib.ΔPPTreeSubSet, δtkeycov *blib.RangedKeySet, err error) {
tracefDiff(" diffT %s %s\n", xidOf(A), xidOf(B)) tracefDiff(" diffT %s %s\n", xidOf(A), xidOf(B))
defer xerr.Contextf(&err, "diffT %s %s", xidOf(A), xidOf(B)) defer xerr.Contextf(&err, "diffT %s %s", xidOf(A), xidOf(B))
δ = map[Key]ΔValue{} δ = map[Key]ΔValue{}
δtrack = NewΔPPTreeSubSet() δtrack = blib.NewΔPPTreeSubSet()
δtkeycov = &RangedKeySet{} δtkeycov = &blib.RangedKeySet{}
defer func() { defer func() {
tracefDiff(" -> δ: %v\n", δ) tracefDiff(" -> δ: %v\n", δ)
tracefDiff(" -> δtrack: %v\n", δtrack) tracefDiff(" -> δtrack: %v\n", δtrack)
...@@ -564,22 +565,22 @@ ABcov: ...@@ -564,22 +565,22 @@ ABcov:
Bv := rangeSplit{btop} // nodes expanded from B Bv := rangeSplit{btop} // nodes expanded from B
// for phase 2: // for phase 2:
Akqueue := &RangedKeySet{} // queue for keys in A to be processed for δ- Akqueue := &blib.RangedKeySet{} // queue for keys in A to be processed for δ-
Bkqueue := &RangedKeySet{} // ----//---- in B for δ+ Bkqueue := &blib.RangedKeySet{} // ----//---- in B for δ+
Akdone := &RangedKeySet{} // already processed keys in A Akdone := &blib.RangedKeySet{} // already processed keys in A
Bkdone := &RangedKeySet{} // ----//---- in B Bkdone := &blib.RangedKeySet{} // ----//---- in B
Aktodo := func(r KeyRange) { Aktodo := func(r blib.KeyRange) {
if !Akdone.HasRange(r) { if !Akdone.HasRange(r) {
δtodo := &RangedKeySet{} δtodo := &blib.RangedKeySet{}
δtodo.AddRange(r) δtodo.AddRange(r)
δtodo.DifferenceInplace(Akdone) δtodo.DifferenceInplace(Akdone)
debugfDiff(" Akq <- %s\n", δtodo) debugfDiff(" Akq <- %s\n", δtodo)
Akqueue.UnionInplace(δtodo) Akqueue.UnionInplace(δtodo)
} }
} }
Bktodo := func(r KeyRange) { Bktodo := func(r blib.KeyRange) {
if !Bkdone.HasRange(r) { if !Bkdone.HasRange(r) {
δtodo := &RangedKeySet{} δtodo := &blib.RangedKeySet{}
δtodo.AddRange(r) δtodo.AddRange(r)
δtodo.DifferenceInplace(Bkdone) δtodo.DifferenceInplace(Bkdone)
debugfDiff(" Bkq <- %s\n", δtodo) debugfDiff(" Bkq <- %s\n", δtodo)
...@@ -595,8 +596,8 @@ ABcov: ...@@ -595,8 +596,8 @@ ABcov:
} }
// δtkeycov will be = BAdd \ ADel // δtkeycov will be = BAdd \ ADel
δtkeycovADel := &RangedKeySet{} δtkeycovADel := &blib.RangedKeySet{}
δtkeycovBAdd := &RangedKeySet{} δtkeycovBAdd := &blib.RangedKeySet{}
// phase 1: expand A top->down driven by δZTC. // phase 1: expand A top->down driven by δZTC.
// by default a node contributes to δ- // by default a node contributes to δ-
...@@ -620,7 +621,7 @@ ABcov: ...@@ -620,7 +621,7 @@ ABcov:
// a is bucket -> δ- // a is bucket -> δ-
δA, err := diffB(ctx, a, nil); /*X*/if err != nil { return nil,nil,nil, err } δA, err := diffB(ctx, a, nil); /*X*/if err != nil { return nil,nil,nil, err }
err = δMerge(δ, δA); /*X*/if err != nil { return nil,nil,nil, err } err = δMerge(δ, δA); /*X*/if err != nil { return nil,nil,nil, err }
ar := KeyRange{ra.lo, ra.hi_} ar := blib.KeyRange{ra.lo, ra.hi_}
δtrack.Del.AddPath(ra.Path()) δtrack.Del.AddPath(ra.Path())
δtkeycovADel.AddRange(ar) δtkeycovADel.AddRange(ar)
debugfDiff(" δtrack - %s %v\n", ar, ra.Path()) debugfDiff(" δtrack - %s %v\n", ar, ra.Path())
...@@ -632,7 +633,7 @@ ABcov: ...@@ -632,7 +633,7 @@ ABcov:
case *Tree: case *Tree:
// empty tree - queue holes covered by it // empty tree - queue holes covered by it
if len(a.Entryv()) == 0 { if len(a.Entryv()) == 0 {
ar := KeyRange{ra.lo, ra.hi_} ar := blib.KeyRange{ra.lo, ra.hi_}
δtrack.Del.AddPath(ra.Path()) δtrack.Del.AddPath(ra.Path())
δtkeycovADel.AddRange(ar) δtkeycovADel.AddRange(ar)
debugfDiff(" δtrack - %s %v\n", ar, ra.Path()) debugfDiff(" δtrack - %s %v\n", ar, ra.Path())
...@@ -691,8 +692,8 @@ ABcov: ...@@ -691,8 +692,8 @@ ABcov:
} }
if found { if found {
// ac can be skipped if key coverage stays the same // ac can be skipped if key coverage stays the same
ar := KeyRange{ac.lo, ac.hi_} ar := blib.KeyRange{ac.lo, ac.hi_}
br := KeyRange{bc.lo, bc.hi_} br := blib.KeyRange{bc.lo, bc.hi_}
if ar == br { if ar == br {
// adjust trackSet since path to the node could have changed // adjust trackSet since path to the node could have changed
apath := ac.Path() apath := ac.Path()
...@@ -744,7 +745,7 @@ ABcov: ...@@ -744,7 +745,7 @@ ABcov:
} }
for _, r := range Bkqueue.AllRanges() { for _, r := range Bkqueue.AllRanges() {
lo := r.lo lo := r.Lo
for { for {
b, err := Bv.GetToLeaf(ctx, lo); /*X*/if err != nil { return nil,nil,nil, err } b, err := Bv.GetToLeaf(ctx, lo); /*X*/if err != nil { return nil,nil,nil, err }
debugfDiff(" B k%d -> %s\n", lo, b) debugfDiff(" B k%d -> %s\n", lo, b)
...@@ -758,7 +759,7 @@ ABcov: ...@@ -758,7 +759,7 @@ ABcov:
// δ <- δB // δ <- δB
err = δMerge(δ, δB); /*X*/if err != nil { return nil,nil,nil, err } err = δMerge(δ, δB); /*X*/if err != nil { return nil,nil,nil, err }
br := KeyRange{b.lo, b.hi_} br := blib.KeyRange{b.lo, b.hi_}
δtrack.Add.AddPath(b.Path()) δtrack.Add.AddPath(b.Path())
δtkeycovBAdd.AddRange(br) δtkeycovBAdd.AddRange(br)
debugfDiff(" δtrack + %s %v\n", br, b.Path()) debugfDiff(" δtrack + %s %v\n", br, b.Path())
...@@ -771,7 +772,7 @@ ABcov: ...@@ -771,7 +772,7 @@ ABcov:
} }
// continue with next right bucket until r coverage is complete // continue with next right bucket until r coverage is complete
if r.hi_ <= b.hi_ { if r.Hi_ <= b.hi_ {
break break
} }
lo = b.hi_ + 1 lo = b.hi_ + 1
...@@ -782,7 +783,7 @@ ABcov: ...@@ -782,7 +783,7 @@ ABcov:
debugfDiff("\n") debugfDiff("\n")
debugfDiff(" Akq: %s\n", Akqueue) debugfDiff(" Akq: %s\n", Akqueue)
for _, r := range Akqueue.AllRanges() { for _, r := range Akqueue.AllRanges() {
lo := r.lo lo := r.Lo
for { for {
a, err := Av.GetToLeaf(ctx, lo); /*X*/if err != nil { return nil,nil,nil, err } a, err := Av.GetToLeaf(ctx, lo); /*X*/if err != nil { return nil,nil,nil, err }
debugfDiff(" A k%d -> %s\n", lo, a) debugfDiff(" A k%d -> %s\n", lo, a)
...@@ -798,7 +799,7 @@ ABcov: ...@@ -798,7 +799,7 @@ ABcov:
err = δMerge(δ, δA); /*X*/if err != nil { return nil,nil,nil, err } err = δMerge(δ, δA); /*X*/if err != nil { return nil,nil,nil, err }
δtrack.Del.AddPath(a.Path()) δtrack.Del.AddPath(a.Path())
// NOTE adjust δtkeycovADel only if a was originally tracked // NOTE adjust δtkeycovADel only if a was originally tracked
ar := KeyRange{a.lo, a.hi_} ar := blib.KeyRange{a.lo, a.hi_}
_, tracked := trackSet[a.node.POid()] _, tracked := trackSet[a.node.POid()]
if tracked { if tracked {
δtkeycovADel.AddRange(ar) δtkeycovADel.AddRange(ar)
...@@ -815,7 +816,7 @@ ABcov: ...@@ -815,7 +816,7 @@ ABcov:
} }
// continue with next right bucket until r coverage is complete // continue with next right bucket until r coverage is complete
if r.hi_ <= a.hi_ { if r.Hi_ <= a.hi_ {
break break
} }
lo = a.hi_ + 1 lo = a.hi_ + 1
...@@ -1009,7 +1010,7 @@ func xidOf(obj zodb.IPersistent) string { ...@@ -1009,7 +1010,7 @@ func xidOf(obj zodb.IPersistent) string {
func (rn *nodeInRange) String() string { func (rn *nodeInRange) String() string {
done := " "; if rn.done { done = "*" } done := " "; if rn.done { done = "*" }
return fmt.Sprintf("%s%s%s", done, KeyRange{rn.lo, rn.hi_}, vnode(rn.node)) return fmt.Sprintf("%s%s%s", done, blib.KeyRange{rn.lo, rn.hi_}, vnode(rn.node))
} }
// push pushes element to node stack. // push pushes element to node stack.
......
...@@ -5,10 +5,8 @@ package xbtree ...@@ -5,10 +5,8 @@ package xbtree
import ( import (
"fmt" "fmt"
"math"
"lab.nexedi.com/kirr/neo/go/zodb" "lab.nexedi.com/kirr/neo/go/zodb"
"lab.nexedi.com/kirr/neo/go/zodb/btree"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/set" "lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/set"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xbtree/blib" "lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xbtree/blib"
......
// Copyright (C) 2020-2021 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
// Package xbtreetest/init (ex imported from package A) should be imported in
// addition to xbtreetest (from package A_test) to initialize xbtreetest at runtime.
package init
// ZBlk-related part of δbtail_test
import (
"context"
"fmt"
"lab.nexedi.com/kirr/go123/xerr"
"lab.nexedi.com/kirr/neo/go/zodb"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xbtree"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xzodb"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/zdata"
)
type Tree = xbtree.Tree
type Node = xbtree.Node
type Key = xbtree.Key
type ZBlk = zdata.ZBlk
// ztreeGetBlk returns ztree[k] and tree path that lead to this block.
// XXX +return blkRevMax and use it ?
func ztreeGetBlk(ctx context.Context, ztree *Tree, k Key) (zblk ZBlk, ok bool, path []Node, err error) {
path = []Node{}
xzblk, ok, err := ztree.VGet(ctx, k, func(node Node) {
path = append(path, node)
})
if err != nil {
return nil, false, nil, err
}
if ok {
zblk, ok = xzblk.(ZBlk)
if !ok {
return nil, false, nil, fmt.Errorf("expect ZBlk*; got %s", xzodb.TypeOf(xzblk)) // XXX errctx
}
}
return zblk, ok, path, nil
}
func init() {
xbtree.ZTreeGetBlkData = ZTreeGetBlkData
xbtree.ZGetBlkData = ZGetBlkData
}
// ZTreeGetBlkData returns block data from block pointed to by ztree[k].
func ZTreeGetBlkData(ctx context.Context, ztree *Tree, k Key) (data string, ok bool, path []Node, err error) {
defer xerr.Contextf(&err, "@%s: tree<%s>: get blkdata from [%d]", ztree.PJar().At(), ztree.POid(), k)
zblk, ok, path, err := ztreeGetBlk(ctx, ztree, k)
if err != nil || !ok {
return "", ok, path, err
}
bdata, _, err := zblk.LoadBlkData(ctx)
if err != nil {
return "", false, nil, err
}
return string(bdata), true, path, nil
}
// ZGetBlkData loads block data from ZBlk object specified by its oid.
func ZGetBlkData(ctx context.Context, zconn *zodb.Connection, zblkOid zodb.Oid) (data string, err error) {
defer xerr.Contextf(&err, "@%s: get blkdata from obj %s", zconn.At(), zblkOid)
xblk, err := zconn.Get(ctx, zblkOid)
if err != nil {
return "", err
}
zblk, ok := xblk.(ZBlk)
if !ok {
return "", fmt.Errorf("expect ZBlk*; got %s", xzodb.TypeOf(xblk))
}
bdata, _, err := zblk.LoadBlkData(ctx)
if err != nil {
return "", err
}
return string(bdata), nil
}
...@@ -31,27 +31,27 @@ import ( ...@@ -31,27 +31,27 @@ import (
// RTree represents Tree node covering [lo, hi_] key range in its parent tree. // RTree represents Tree node covering [lo, hi_] key range in its parent tree.
// XXX actually no coverage here -> kill? -> change to just `path []zodb.Oid` in RBucket? // XXX actually no coverage here -> kill? -> change to just `path []zodb.Oid` in RBucket?
type RTree struct { type RTree struct {
oid zodb.Oid Oid zodb.Oid
parent *RTree Parent *RTree
// XXX +children? // XXX +children?
} }
// RBucket represents Bucket node covering [lo, hi_] key range in its Tree. // RBucket represents Bucket node covering [lo, hi_] key range in its Tree.
// NOTE it is not [lo,hi) but [lo,hi_] instead to avoid overflow at KeyMax. // NOTE it is not [lo,hi) but [lo,hi_] instead to avoid overflow at KeyMax.
type RBucket struct { type RBucket struct {
oid zodb.Oid Oid zodb.Oid
parent *RTree Parent *RTree
lo, hi_ Key // XXX -> KeyRange ? Keycov blib.KeyRange
kv map[Key]string // bucket's k->v; values were ZBlk objects whose data is loaded instead. KV map[Key]string // bucket's k->v; values were ZBlk objects whose data is loaded instead.
} }
// Path returns path to this bucket from tree root. // Path returns path to this bucket from tree root.
func (rb *RBucket) Path() []zodb.Oid { func (rb *RBucket) Path() []zodb.Oid {
path := []zodb.Oid{rb.oid} path := []zodb.Oid{rb.Oid}
p := rb.parent p := rb.Parent
for p != nil { for p != nil {
path = append([]zodb.Oid{p.oid}, path...) path = append([]zodb.Oid{p.Oid}, path...)
p = p.parent p = p.Parent
} }
return path return path
} }
...@@ -63,15 +63,15 @@ type RBucketSet []*RBucket // k↑ ...@@ -63,15 +63,15 @@ type RBucketSet []*RBucket // k↑
// Get returns RBucket which covers key k. // Get returns RBucket which covers key k.
func (rbs RBucketSet) Get(k Key) *RBucket { func (rbs RBucketSet) Get(k Key) *RBucket {
i := sort.Search(len(rbs), func(i int) bool { i := sort.Search(len(rbs), func(i int) bool {
return k <= rbs[i].hi_ return k <= rbs[i].Keycov.Hi_
}) })
if i == len(rbs) { if i == len(rbs) {
panicf("BUG: key %v not covered; coverage: %s", k, rbs.coverage()) panicf("BUG: key %v not covered; coverage: %s", k, rbs.coverage())
} }
rb := rbs[i] rb := rbs[i]
if !(rb.lo <= k && k <= rb.hi_) { if !(rb.Keycov.Lo <= k && k <= rb.Keycov.Hi_) {
panicf("BUG: get(%v) -> [%v, %v]; coverage: %s", k, rb.lo, rb.hi_, rbs.coverage()) panicf("BUG: get(%v) -> %s; coverage: %s", k, rb.Keycov, rbs.coverage())
} }
return rb return rb
...@@ -87,7 +87,7 @@ func (rbs RBucketSet) coverage() string { ...@@ -87,7 +87,7 @@ func (rbs RBucketSet) coverage() string {
if s != "" { if s != "" {
s += " " s += " "
} }
s += fmt.Sprintf("[%v, %v]", rb.lo, rb.hi_) s += fmt.Sprintf("%s", rb.Keycov)
} }
return s return s
} }
...@@ -96,7 +96,7 @@ func (rbs RBucketSet) coverage() string { ...@@ -96,7 +96,7 @@ func (rbs RBucketSet) coverage() string {
func (xkv RBucketSet) Flatten() map[Key]string { func (xkv RBucketSet) Flatten() map[Key]string {
kv := make(map[Key]string) kv := make(map[Key]string)
for _, b := range xkv { for _, b := range xkv {
for k,v := range b.kv { for k,v := range b.KV {
kv[k] = v kv[k] = v
} }
} }
...@@ -104,5 +104,5 @@ func (xkv RBucketSet) Flatten() map[Key]string { ...@@ -104,5 +104,5 @@ func (xkv RBucketSet) Flatten() map[Key]string {
} }
func (b *RBucket) String() string { func (b *RBucket) String() string {
return fmt.Sprintf("%sB%s{%s}", blib.KeyRange{b.lo, b.hi_}, b.oid, kvtxt(b.kv)) return fmt.Sprintf("%sB%s{%s}", b.Keycov, b.Oid, kvtxt(b.KV))
} }
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
// See https://www.nexedi.com/licensing for rationale and options. // See https://www.nexedi.com/licensing for rationale and options.
package xbtreetest package xbtreetest
// TreeEnv + friends // T + friends
import ( import (
"context" "context"
...@@ -28,14 +28,15 @@ import ( ...@@ -28,14 +28,15 @@ import (
"lab.nexedi.com/kirr/neo/go/transaction" "lab.nexedi.com/kirr/neo/go/transaction"
"lab.nexedi.com/kirr/neo/go/zodb" "lab.nexedi.com/kirr/neo/go/zodb"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xbtree/blib"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xzodb" "lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xzodb"
) )
// tTreeEnv is tree-based testing environment. // T is tree-based testing environment.
// //
// It combines TreeSrv and client side access to ZODB with committed trees. // It combines TreeSrv and client side access to ZODB with committed trees.
// It should be created it via tNewTreeEnv(). // It should be created it via NewT().
type tTreeEnv struct { type T struct {
*testing.T *testing.T
work string // working directory work string // working directory
...@@ -44,13 +45,13 @@ type tTreeEnv struct { ...@@ -44,13 +45,13 @@ type tTreeEnv struct {
db *zodb.DB db *zodb.DB
// all committed trees // all committed trees
commitv []*tTreeCommit commitv []*Commit
} }
// tTreeCommit represent test commit changing a tree. // Commit represent test commit changing a tree.
type tTreeCommit struct { type Commit struct {
tree string // the tree in topology-encoding tree string // the tree in topology-encoding
prev *tTreeCommit // previous commit prev *Commit // previous commit
at zodb.Tid // commit revision at zodb.Tid // commit revision
δZ *zodb.EventCommit // raw ZODB changes; δZ.tid == at δZ *zodb.EventCommit // raw ZODB changes; δZ.tid == at
xkv RBucketSet // full tree state as of @at xkv RBucketSet // full tree state as of @at
...@@ -59,12 +60,12 @@ type tTreeCommit struct { ...@@ -59,12 +60,12 @@ type tTreeCommit struct {
// δzblkData map[zodb.Oid]Δstring // full diff for zblkData against parent XXX ? // δzblkData map[zodb.Oid]Δstring // full diff for zblkData against parent XXX ?
} }
// tNewTreeEnv creates new tTreeEnv. // NewT creates new T.
func tNewTreeEnv(t *testing.T) *tTreeEnv { func NewT(t *testing.T) *T {
X := exc.Raiseif X := exc.Raiseif
t.Helper() t.Helper()
tt := &tTreeEnv{T: t} tt := &T{T: t}
var err error var err error
work := t.TempDir() work := t.TempDir()
...@@ -91,7 +92,7 @@ func tNewTreeEnv(t *testing.T) *tTreeEnv { ...@@ -91,7 +92,7 @@ func tNewTreeEnv(t *testing.T) *tTreeEnv {
}) })
head := tt.treeSrv.head head := tt.treeSrv.head
t1 := &tTreeCommit{ t1 := &Commit{
tree: "T/B:", // treegen.py creates the tree as initially empty tree: "T/B:", // treegen.py creates the tree as initially empty
prev: nil, prev: nil,
at: head, at: head,
...@@ -100,7 +101,7 @@ func tNewTreeEnv(t *testing.T) *tTreeEnv { ...@@ -100,7 +101,7 @@ func tNewTreeEnv(t *testing.T) *tTreeEnv {
δZ: nil, δZ: nil,
δxkv: nil, δxkv: nil,
} }
tt.commitv = []*tTreeCommit{t1} tt.commitv = []*Commit{t1}
return tt return tt
} }
...@@ -115,17 +116,18 @@ func (_ *tZODBCacheEverything) PCacheClassify(_ zodb.IPersistent) zodb.PCachePol ...@@ -115,17 +116,18 @@ func (_ *tZODBCacheEverything) PCacheClassify(_ zodb.IPersistent) zodb.PCachePol
} }
// Root returns OID of root tree node. // Root returns OID of root tree node.
func (t *tTreeEnv) Root() zodb.Oid { func (t *T) Root() zodb.Oid {
return t.treeSrv.treeRoot return t.treeSrv.treeRoot
} }
// Head returns most-recently committed tree. // Head returns most-recently committed tree.
func (t *tTreeEnv) Head() *tTreeCommit { func (t *T) Head() *Commit {
return t.commitv[len(t.commitv)-1] return t.commitv[len(t.commitv)-1]
} }
// CommitTree calls t.treeSrv.Commit and returns tTreeCommit corresponding to committed transaction. // CommitTree calls t.treeSrv.Commit and returns Commit corresponding to committed transaction.
func (t *tTreeEnv) CommitTree(tree string) *tTreeCommit { // XXX naming -> Commit ?
func (t *T) CommitTree(tree string) *Commit {
// TODO X = FatalIf // TODO X = FatalIf
X := exc.Raiseif X := exc.Raiseif
defer exc.Contextf("commit %s", tree) defer exc.Contextf("commit %s", tree)
...@@ -164,19 +166,18 @@ func (t *tTreeEnv) CommitTree(tree string) *tTreeCommit { ...@@ -164,19 +166,18 @@ func (t *tTreeEnv) CommitTree(tree string) *tTreeCommit {
// it comes to ->T2. // it comes to ->T2.
xkv = RBucketSet{ xkv = RBucketSet{
&RBucket{ &RBucket{
oid: zodb.InvalidOid, Oid: zodb.InvalidOid,
parent: &RTree{ Parent: &RTree{
oid: t.Root(), // NOTE oid is not InvalidOid Oid: t.Root(), // NOTE oid is not InvalidOid
parent: nil, Parent: nil,
}, },
lo: KeyMin, Keycov: blib.KeyRange{KeyMin, KeyMax},
hi_: KeyMax, KV: map[Key]string{},
kv: map[Key]string{},
}, },
} }
} }
ttree := &tTreeCommit{ ttree := &Commit{
tree: tree, tree: tree,
at: δZ.Tid, at: δZ.Tid,
δZ: δZ, δZ: δZ,
...@@ -244,7 +245,7 @@ func xGetBlkDataTab(db *zodb.DB, at zodb.Tid) map[zodb.Oid]string { ...@@ -244,7 +245,7 @@ func xGetBlkDataTab(db *zodb.DB, at zodb.Tid) map[zodb.Oid]string {
// xgetBlkData loads blk data for ZBlk<oid> @t.at // xgetBlkData loads blk data for ZBlk<oid> @t.at
// //
// For speed the load is done via preloaded t.blkDataTab instead of access to the DB. // For speed the load is done via preloaded t.blkDataTab instead of access to the DB.
func (t *tTreeCommit) xgetBlkData(oid zodb.Oid) string { func (t *Commit) xgetBlkData(oid zodb.Oid) string {
if oid == VDEL { if oid == VDEL {
return DEL return DEL
} }
...@@ -282,15 +283,14 @@ func xGetTree(db *zodb.DB, at zodb.Tid, root zodb.Oid) RBucketSet { ...@@ -282,15 +283,14 @@ func xGetTree(db *zodb.DB, at zodb.Tid, root zodb.Oid) RBucketSet {
}) })
if len(rbucketv) == 0 { // empty tree -> [-∞,∞){} if len(rbucketv) == 0 { // empty tree -> [-∞,∞){}
etree := &RTree{ etree := &RTree{
oid: root, Oid: root,
parent: nil, Parent: nil,
} }
ebucket := &RBucket{ ebucket := &RBucket{
oid: zodb.InvalidOid, Oid: zodb.InvalidOid,
parent: etree, Parent: etree,
lo: KeyMin, Keycov: blib.KeyRange{KeyMin, KeyMax},
hi_: KeyMax, KV: map[Key]string{},
kv: map[Key]string{},
} }
rbucketv = RBucketSet{ebucket} rbucketv = RBucketSet{ebucket}
} }
...@@ -307,7 +307,7 @@ func _xwalkDFS(ctx context.Context, lo, hi_ Key, ztree *Tree, rparent *RTree, bv ...@@ -307,7 +307,7 @@ func _xwalkDFS(ctx context.Context, lo, hi_ Key, ztree *Tree, rparent *RTree, bv
err := ztree.PActivate(ctx); X(err) err := ztree.PActivate(ctx); X(err)
defer ztree.PDeactivate() defer ztree.PDeactivate()
rtree := &RTree{oid: ztree.POid(), parent: rparent} rtree := &RTree{Oid: ztree.POid(), Parent: rparent}
// [i].Key ≤ [i].Child.*.Key < [i+1].Key i ∈ [0, len([])) // [i].Key ≤ [i].Child.*.Key < [i+1].Key i ∈ [0, len([]))
// //
...@@ -346,7 +346,12 @@ func _xwalkDFS(ctx context.Context, lo, hi_ Key, ztree *Tree, rparent *RTree, bv ...@@ -346,7 +346,12 @@ func _xwalkDFS(ctx context.Context, lo, hi_ Key, ztree *Tree, rparent *RTree, bv
} }
b := &RBucket{oid: zbucket.POid(), parent: rtree, lo: xlo, hi_: xhi_, kv: bkv} b := &RBucket{
Oid: zbucket.POid(),
Parent: rtree,
Keycov: blib.KeyRange{xlo, xhi_},
KV: bkv,
}
bvisit(b) bvisit(b)
} }
} }
......
...@@ -23,24 +23,27 @@ package xbtreetest ...@@ -23,24 +23,27 @@ package xbtreetest
import ( import (
"fmt" "fmt"
"math"
"lab.nexedi.com/kirr/neo/go/zodb" "lab.nexedi.com/kirr/neo/go/zodb"
"lab.nexedi.com/kirr/neo/go/zodb/btree"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/set" "lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/set"
// "lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xbtree" "lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xbtree/blib"
) )
// XXX dup from xbtree (to avoid import cycle) // XXX instead of generics
type Tree = btree.LOBTree type Tree = blib.Tree
type Bucket = btree.LOBucket type Bucket = blib.Bucket
type Node = blib.Node
type TreeEntry = blib.TreeEntry
type BucketEntry = blib.BucketEntry
type Key = blib.Key
const KeyMax = blib.KeyMax
const KeyMin = blib.KeyMin
type Key = int64
const KeyMax Key = math.MaxInt64
const KeyMin Key = math.MinInt64
type setKey = set.I64 type setKey = set.I64
// XXX dup from xbtree (to avoid import cycle)
const VDEL = zodb.InvalidOid const VDEL = zodb.InvalidOid
......
// Copyright (C) 2020-2021 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package xbtreetest
// access to ZBlk data
import (
"context"
"lab.nexedi.com/kirr/go123/exc"
"lab.nexedi.com/kirr/neo/go/transaction"
"lab.nexedi.com/kirr/neo/go/zodb"
_ "lab.nexedi.com/kirr/neo/go/zodb/wks"
)
// ZBlk-related functions are imported at runtime by package xbtreetest/init
var (
ZTreeGetBlkData func(context.Context, *Tree, Key) (string, bool, []Node, error)
ZGetBlkData func(context.Context, *zodb.Connection, zodb.Oid) (string, error)
)
func zassertInitDone() {
if ZTreeGetBlkData == nil {
panic("xbtreetest/zdata not initialized -> import xbtreetest/init to fix")
}
}
// xzgetBlkData loads block data from ZBlk object specified by its oid.
func xzgetBlkData(ctx context.Context, zconn *zodb.Connection, zblkOid zodb.Oid) string {
zassertInitDone()
X := exc.Raiseif
if zblkOid == VDEL {
return DEL
}
data, err := ZGetBlkData(ctx, zconn, zblkOid); X(err)
return string(data)
}
// xzgetBlkDataAt loads block data from ZBlk object specified by oid@at.
func xzgetBlkDataAt(db *zodb.DB, zblkOid zodb.Oid, at zodb.Tid) string {
zassertInitDone()
X := exc.Raiseif
txn, ctx := transaction.New(context.Background())
defer txn.Abort()
zconn, err := db.Open(ctx, &zodb.ConnOptions{At: at}); X(err)
return xzgetBlkData(ctx, zconn, zblkOid)
}
...@@ -30,6 +30,7 @@ import ( ...@@ -30,6 +30,7 @@ import (
"lab.nexedi.com/kirr/neo/go/transaction" "lab.nexedi.com/kirr/neo/go/transaction"
"lab.nexedi.com/kirr/neo/go/zodb" "lab.nexedi.com/kirr/neo/go/zodb"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xbtree/blib"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xtail" "lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xtail"
) )
...@@ -96,7 +97,7 @@ type ΔBtail struct { ...@@ -96,7 +97,7 @@ type ΔBtail struct {
// For this set all vδT are fully computed. // For this set all vδT are fully computed.
// The set of nodes that were requested to be tracked, but were not yet // The set of nodes that were requested to be tracked, but were not yet
// taken into account, is kept in ΔTtail.trackNew & co. // taken into account, is kept in ΔTtail.trackNew & co.
trackSet PPTreeSubSet trackSet blib.PPTreeSubSet
// set of trees for which .trackNew is non-empty // set of trees for which .trackNew is non-empty
trackNewRoots setOid trackNewRoots setOid
...@@ -114,7 +115,7 @@ type ΔTtail struct { ...@@ -114,7 +115,7 @@ type ΔTtail struct {
// set of nodes that were requested to be tracked in this tree, but for // set of nodes that were requested to be tracked in this tree, but for
// which vδT was not yet rebuilt // which vδT was not yet rebuilt
trackNew PPTreeSubSet trackNew blib.PPTreeSubSet
// XXX + trackNewKeys RangedKeySet // XXX + trackNewKeys RangedKeySet
// {}k/v @tail for keys that are changed in (tail, head]. // {}k/v @tail for keys that are changed in (tail, head].
...@@ -158,7 +159,7 @@ func NewΔBtail(at0 zodb.Tid, db *zodb.DB) *ΔBtail { ...@@ -158,7 +159,7 @@ func NewΔBtail(at0 zodb.Tid, db *zodb.DB) *ΔBtail {
δZtail: zodb.NewΔTail(at0), δZtail: zodb.NewΔTail(at0),
vδBroots: nil, vδBroots: nil,
vδTbyRoot: map[zodb.Oid]*ΔTtail{}, vδTbyRoot: map[zodb.Oid]*ΔTtail{},
trackSet: PPTreeSubSet{}, trackSet: blib.PPTreeSubSet{},
trackNewRoots: setOid{}, trackNewRoots: setOid{},
db: db, db: db,
} }
...@@ -167,7 +168,7 @@ func NewΔBtail(at0 zodb.Tid, db *zodb.DB) *ΔBtail { ...@@ -167,7 +168,7 @@ func NewΔBtail(at0 zodb.Tid, db *zodb.DB) *ΔBtail {
// newΔTtail creates new empty ΔTtail object. // newΔTtail creates new empty ΔTtail object.
func newΔTtail() *ΔTtail { func newΔTtail() *ΔTtail {
return &ΔTtail{ return &ΔTtail{
trackNew: PPTreeSubSet{}, trackNew: blib.PPTreeSubSet{},
KVAtTail: make(map[Key]Value), KVAtTail: make(map[Key]Value),
lastRevOf: make(map[Key]zodb.Tid), lastRevOf: make(map[Key]zodb.Tid),
} }
...@@ -271,7 +272,7 @@ func (δBtail *ΔBtail) track(key Key, path []zodb.Oid) error { ...@@ -271,7 +272,7 @@ func (δBtail *ΔBtail) track(key Key, path []zodb.Oid) error {
// empty artificial tree. We need to do the normalization because we // empty artificial tree. We need to do the normalization because we
// later check whether leaf path[-1] ∈ trackSet and without // later check whether leaf path[-1] ∈ trackSet and without
// normalization path[-1] can be InvalidOid. // normalization path[-1] can be InvalidOid.
path = normPath(path) path = blib.NormPath(path)
if len(path) == 0 { if len(path) == 0 {
return nil // empty tree return nil // empty tree
} }
...@@ -334,7 +335,7 @@ func (δBtail *ΔBtail) rebuildAll() (err error) { ...@@ -334,7 +335,7 @@ func (δBtail *ΔBtail) rebuildAll() (err error) {
// - set of revisions for which new entries in .vδT have been created. // - set of revisions for which new entries in .vδT have been created.
// //
// XXX place // XXX place
func (δTtail *ΔTtail) rebuild(root zodb.Oid, δZtail *zodb.ΔTail, db *zodb.DB) (δtrackSet PPTreeSubSet, δrevSet setTid, err error) { func (δTtail *ΔTtail) rebuild(root zodb.Oid, δZtail *zodb.ΔTail, db *zodb.DB) (δtrackSet blib.PPTreeSubSet, δrevSet setTid, err error) {
defer xerr.Context(&err, "ΔTtail rebuild") defer xerr.Context(&err, "ΔTtail rebuild")
// XXX locking // XXX locking
...@@ -342,7 +343,7 @@ func (δTtail *ΔTtail) rebuild(root zodb.Oid, δZtail *zodb.ΔTail, db *zodb.DB ...@@ -342,7 +343,7 @@ func (δTtail *ΔTtail) rebuild(root zodb.Oid, δZtail *zodb.ΔTail, db *zodb.DB
tracefΔBtail("trackNew: %v\n", δTtail.trackNew) tracefΔBtail("trackNew: %v\n", δTtail.trackNew)
trackNew := δTtail.trackNew trackNew := δTtail.trackNew
δTtail.trackNew = PPTreeSubSet{} δTtail.trackNew = blib.PPTreeSubSet{}
if len(trackNew) == 0 { if len(trackNew) == 0 {
return nil, nil, nil return nil, nil, nil
...@@ -353,7 +354,7 @@ func (δTtail *ΔTtail) rebuild(root zodb.Oid, δZtail *zodb.ΔTail, db *zodb.DB ...@@ -353,7 +354,7 @@ func (δTtail *ΔTtail) rebuild(root zodb.Oid, δZtail *zodb.ΔTail, db *zodb.DB
// go backwards and merge vδT <- treediff(lo..hi/trackNew) // go backwards and merge vδT <- treediff(lo..hi/trackNew)
vδZ := δZtail.Data() vδZ := δZtail.Data()
for { for {
δtkeycov := &RangedKeySet{} // all keys coming into tracking set during this lo<-hi scan δtkeycov := &blib.RangedKeySet{} // all keys coming into tracking set during this lo<-hi scan
trackNewCur := trackNew.Clone() // trackNew adjusted as of when going to i<- entry trackNewCur := trackNew.Clone() // trackNew adjusted as of when going to i<- entry
for i := len(vδZ)-1; i>=0; i-- { for i := len(vδZ)-1; i>=0; i-- {
δZ := vδZ[i] δZ := vδZ[i]
...@@ -411,7 +412,7 @@ func (δTtail *ΔTtail) rebuild(root zodb.Oid, δZtail *zodb.ΔTail, db *zodb.DB ...@@ -411,7 +412,7 @@ func (δTtail *ΔTtail) rebuild(root zodb.Oid, δZtail *zodb.ΔTail, db *zodb.DB
// widenTrackNew widens trackNew to cover δtkeycov. // widenTrackNew widens trackNew to cover δtkeycov.
// XXX -> widenTrackSet? // XXX -> widenTrackSet?
func widenTrackNew(trackNew PPTreeSubSet, δtkeycov *RangedKeySet, root zodb.Oid, at zodb.Tid, db *zodb.DB) (err error) { func widenTrackNew(trackNew blib.PPTreeSubSet, δtkeycov *blib.RangedKeySet, root zodb.Oid, at zodb.Tid, db *zodb.DB) (err error) {
// XXX errctx, debug // XXX errctx, debug
defer xerr.Contextf(&err, "widenTrackNew tree<%s> @%s +%s", root, at, δtkeycov) defer xerr.Contextf(&err, "widenTrackNew tree<%s> @%s +%s", root, at, δtkeycov)
...@@ -430,13 +431,13 @@ func widenTrackNew(trackNew PPTreeSubSet, δtkeycov *RangedKeySet, root zodb.Oid ...@@ -430,13 +431,13 @@ func widenTrackNew(trackNew PPTreeSubSet, δtkeycov *RangedKeySet, root zodb.Oid
top := &nodeInRange{prefix: nil, lo: KeyMin, hi_: KeyMax, node: tree} top := &nodeInRange{prefix: nil, lo: KeyMin, hi_: KeyMax, node: tree}
V := rangeSplit{top} V := rangeSplit{top}
for _, r := range δtkeycov.AllRanges() { for _, r := range δtkeycov.AllRanges() {
lo := r.lo lo := r.Lo
for { for {
b, err := V.GetToLeaf(ctx, lo); /*X*/ if err != nil { return err } b, err := V.GetToLeaf(ctx, lo); /*X*/ if err != nil { return err }
trackNew.AddPath(b.Path()) trackNew.AddPath(b.Path())
// continue with next right bucket until r coverage is complete // continue with next right bucket until r coverage is complete
if r.hi_ <= b.hi_ { if r.Hi_ <= b.hi_ {
break break
} }
lo = b.hi_ + 1 lo = b.hi_ + 1
...@@ -450,7 +451,7 @@ func widenTrackNew(trackNew PPTreeSubSet, δtkeycov *RangedKeySet, root zodb.Oid ...@@ -450,7 +451,7 @@ func widenTrackNew(trackNew PPTreeSubSet, δtkeycov *RangedKeySet, root zodb.Oid
// //
// δtrackNew/δtkeycov represents how trackNew changes when going through `atPrev <- δZ.Rev` . // δtrackNew/δtkeycov represents how trackNew changes when going through `atPrev <- δZ.Rev` .
// newRevEntry indicates whether δZ.Rev was not there before in .vδT and new corresponding δT entry was created. // newRevEntry indicates whether δZ.Rev was not there before in .vδT and new corresponding δT entry was created.
func (δTtail *ΔTtail) rebuild1(atPrev zodb.Tid, δZ zodb.ΔRevEntry, trackNew PPTreeSubSet, db *zodb.DB) (δtrackNew *ΔPPTreeSubSet, δtkeycov *RangedKeySet, newRevEntry bool, err error) { func (δTtail *ΔTtail) rebuild1(atPrev zodb.Tid, δZ zodb.ΔRevEntry, trackNew blib.PPTreeSubSet, db *zodb.DB) (δtrackNew *blib.ΔPPTreeSubSet, δtkeycov *blib.RangedKeySet, newRevEntry bool, err error) {
defer xerr.Contextf(&err, "rebuild1 %s<-%s", atPrev, δZ.Rev) defer xerr.Contextf(&err, "rebuild1 %s<-%s", atPrev, δZ.Rev)
debugfΔBtail("\n rebuild1 @%s <- @%s\n", atPrev, δZ.Rev) debugfΔBtail("\n rebuild1 @%s <- @%s\n", atPrev, δZ.Rev)
...@@ -467,7 +468,7 @@ func (δTtail *ΔTtail) rebuild1(atPrev zodb.Tid, δZ zodb.ΔRevEntry, trackNew ...@@ -467,7 +468,7 @@ func (δTtail *ΔTtail) rebuild1(atPrev zodb.Tid, δZ zodb.ΔRevEntry, trackNew
// skip opening DB connections if there is no change to this tree // skip opening DB connections if there is no change to this tree
if len(δtopsByRoot) == 0 { if len(δtopsByRoot) == 0 {
return NewΔPPTreeSubSet(), &RangedKeySet{}, false, nil return blib.NewΔPPTreeSubSet(), &blib.RangedKeySet{}, false, nil
} }
if len(δtopsByRoot) != 1 { if len(δtopsByRoot) != 1 {
...@@ -560,7 +561,7 @@ func (δBtail *ΔBtail) Update(δZ *zodb.EventCommit) (_ ΔB, err error) { ...@@ -560,7 +561,7 @@ func (δBtail *ΔBtail) Update(δZ *zodb.EventCommit) (_ ΔB, err error) {
// δtkeycov1 != ø -> rebuild δTtail with trackNew ~= δtkeycov1 // δtkeycov1 != ø -> rebuild δTtail with trackNew ~= δtkeycov1
if !δT1.δtkeycov1.Empty() && δBtail.δZtail.Len() > 1 { if !δT1.δtkeycov1.Empty() && δBtail.δZtail.Len() > 1 {
trackNew := PPTreeSubSet{} trackNew := blib.PPTreeSubSet{}
err := widenTrackNew(trackNew, δT1.δtkeycov1, root, δBtail.Head(), δBtail.db) err := widenTrackNew(trackNew, δT1.δtkeycov1, root, δBtail.Head(), δBtail.db)
if err != nil { if err != nil {
return ΔB{}, err return ΔB{}, err
...@@ -611,8 +612,8 @@ type _ΔBUpdate1 struct { ...@@ -611,8 +612,8 @@ type _ΔBUpdate1 struct {
ByRoot map[zodb.Oid]*_ΔTUpdate1 ByRoot map[zodb.Oid]*_ΔTUpdate1
} }
type _ΔTUpdate1 struct { type _ΔTUpdate1 struct {
δtkeycov1 *RangedKeySet // {} root -> δtrackedKeys after first treediff (always grow) δtkeycov1 *blib.RangedKeySet // {} root -> δtrackedKeys after first treediff (always grow)
δtrack *ΔPPTreeSubSet // XXX kill (not used) δtrack *blib.ΔPPTreeSubSet // XXX kill (not used)
} }
func (δBtail *ΔBtail) _Update1(δZ *zodb.EventCommit) (δB1 _ΔBUpdate1, err error) { func (δBtail *ΔBtail) _Update1(δZ *zodb.EventCommit) (δB1 _ΔBUpdate1, err error) {
headOld := δBtail.Head() headOld := δBtail.Head()
......
...@@ -44,17 +44,11 @@ package xbtree ...@@ -44,17 +44,11 @@ package xbtree
// - to generate set of random tree topologies that all correspond to particular {k->v} dict. // - to generate set of random tree topologies that all correspond to particular {k->v} dict.
import ( import (
"bufio"
"context"
"flag" "flag"
"fmt" "fmt"
"io"
"math" "math"
"math/rand" "math/rand"
"os"
"os/exec"
"reflect" "reflect"
"regexp"
"sort" "sort"
"strings" "strings"
"testing" "testing"
...@@ -62,16 +56,18 @@ import ( ...@@ -62,16 +56,18 @@ import (
"lab.nexedi.com/kirr/go123/exc" "lab.nexedi.com/kirr/go123/exc"
"lab.nexedi.com/kirr/go123/xerr" "lab.nexedi.com/kirr/go123/xerr"
"lab.nexedi.com/kirr/neo/go/transaction"
"lab.nexedi.com/kirr/neo/go/zodb" "lab.nexedi.com/kirr/neo/go/zodb"
_ "lab.nexedi.com/kirr/neo/go/zodb/wks"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xbtree/blib"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xbtree/xbtreetest" "lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xbtree/xbtreetest"
// "lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xzodb" // "lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xzodb"
) )
type Δstring = xbtreetest.Δstring
// trackSet returns what should be ΔBtail.trackSet coverage for specified tracked key set. // trackSet returns what should be ΔBtail.trackSet coverage for specified tracked key set.
func (rbs RBucketSet) trackSet(tracked setKey) PPTreeSubSet { func (rbs xbtreetest.RBucketSet) trackSet(tracked setKey) blib.PPTreeSubSet {
// nil = don't compute keyCover // nil = don't compute keyCover
// (trackSet is called from inside hot inner loop of rebuild test) // (trackSet is called from inside hot inner loop of rebuild test)
trackSet := rbs._trackSetWithCov(tracked, nil) trackSet := rbs._trackSetWithCov(tracked, nil)
...@@ -79,23 +75,23 @@ func (rbs RBucketSet) trackSet(tracked setKey) PPTreeSubSet { ...@@ -79,23 +75,23 @@ func (rbs RBucketSet) trackSet(tracked setKey) PPTreeSubSet {
} }
// trackSetWithCov returns what should be ΔBtail.trackSet and its key coverage for specified tracked key set. // trackSetWithCov returns what should be ΔBtail.trackSet and its key coverage for specified tracked key set.
func (rbs RBucketSet) trackSetWithCov(tracked setKey) (trackSet PPTreeSubSet, keyCover *RangedKeySet) { func (rbs xbtreetest.RBucketSet) trackSetWithCov(tracked setKey) (trackSet blib.PPTreeSubSet, keyCover *blib.RangedKeySet) {
keyCover = &RangedKeySet{} keyCover = &blib.RangedKeySet{}
trackSet = rbs._trackSetWithCov(tracked, keyCover) trackSet = rbs._trackSetWithCov(tracked, keyCover)
return trackSet, keyCover return trackSet, keyCover
} }
func (rbs RBucketSet) _trackSetWithCov(tracked setKey, outKeyCover *RangedKeySet) (trackSet PPTreeSubSet) { func (rbs xbtreetest.RBucketSet) _trackSetWithCov(tracked setKey, outKeyCover *blib.RangedKeySet) (trackSet blib.PPTreeSubSet) {
trackSet = PPTreeSubSet{} trackSet = blib.PPTreeSubSet{}
for k := range tracked { for k := range tracked {
kb := rbs.Get(k) kb := rbs.Get(k)
if outKeyCover != nil { if outKeyCover != nil {
outKeyCover.AddRange(KeyRange{kb.lo, kb.hi_}) outKeyCover.AddRange(kb.Keycov)
} }
// trackSet explicitly records only regular buckets. // trackSet explicitly records only regular buckets.
// embedded buckets all have oid=zodb.InvalidOid and would lead to z // embedded buckets all have oid=zodb.InvalidOid and would lead to z
newNode := false newNode := false
if kb.oid != zodb.InvalidOid { if kb.Oid != zodb.InvalidOid {
track, already := trackSet[kb.oid] track, already := trackSet[kb.oid]
if !already { if !already {
track = &nodeInTree{parent: kb.parent.oid, nchild: 0} track = &nodeInTree{parent: kb.parent.oid, nchild: 0}
...@@ -138,7 +134,7 @@ func (rbs RBucketSet) _trackSetWithCov(tracked setKey, outKeyCover *RangedKeySet ...@@ -138,7 +134,7 @@ func (rbs RBucketSet) _trackSetWithCov(tracked setKey, outKeyCover *RangedKeySet
// XGetδKV translates {k -> δ<oid>} to {k -> δ(ZBlk(oid).data)} according to t1..t2 db snapshots. // XGetδKV translates {k -> δ<oid>} to {k -> δ(ZBlk(oid).data)} according to t1..t2 db snapshots.
func XGetδKV(t1, t2 *tTreeCommit, δkvOid map[Key]ΔValue) map[Key]Δstring { func XGetδKV(t1, t2 *xbtreetest.Commit, δkvOid map[Key]ΔValue) map[Key]Δstring {
δkv := make(map[Key]Δstring, len(δkvOid)) δkv := make(map[Key]Δstring, len(δkvOid))
for k, δvOid := range δkvOid { for k, δvOid := range δkvOid {
δkv[k] = Δstring{ δkv[k] = Δstring{
...@@ -217,7 +213,7 @@ func (kadjA KAdjMatrix) Mul(kadjB KAdjMatrix) KAdjMatrix { ...@@ -217,7 +213,7 @@ func (kadjA KAdjMatrix) Mul(kadjB KAdjMatrix) KAdjMatrix {
// This set of keys defaults to allTestKeys(t1,t2). // This set of keys defaults to allTestKeys(t1,t2).
// //
// KAdj itself is verified by testΔBTail on entries with .kadjOK set. // KAdj itself is verified by testΔBTail on entries with .kadjOK set.
func KAdj(t1, t2 *tTreeCommit, keysv ...setKey) (kadj KAdjMatrix) { func KAdj(t1, t2 *xbtreetest.Commit, keysv ...setKey) (kadj KAdjMatrix) {
// assert KAdj(A,B) == KAdj(B,A) // assert KAdj(A,B) == KAdj(B,A)
kadj12 := _KAdj(t1,t2, keysv...) kadj12 := _KAdj(t1,t2, keysv...)
kadj21 := _KAdj(t2,t1, keysv...) kadj21 := _KAdj(t2,t1, keysv...)
...@@ -235,7 +231,7 @@ func debugfKAdj(format string, argv ...interface{}) { ...@@ -235,7 +231,7 @@ func debugfKAdj(format string, argv ...interface{}) {
} }
} }
func _KAdj(t1, t2 *tTreeCommit, keysv ...setKey) (kadj KAdjMatrix) { func _KAdj(t1, t2 *xbtreetest.Commit, keysv ...setKey) (kadj KAdjMatrix) {
var keys setKey var keys setKey
switch len(keysv) { switch len(keysv) {
case 0: case 0:
...@@ -261,10 +257,10 @@ func _KAdj(t1, t2 *tTreeCommit, keysv ...setKey) (kadj KAdjMatrix) { ...@@ -261,10 +257,10 @@ func _KAdj(t1, t2 *tTreeCommit, keysv ...setKey) (kadj KAdjMatrix) {
adj1 := setKey{} adj1 := setKey{}
adj2 := setKey{} adj2 := setKey{}
q1 := &RangedKeySet{}; q1.Add(k) q1 := &blib.RangedKeySet{}; q1.Add(k)
q2 := &RangedKeySet{}; q2.Add(k) q2 := &blib.RangedKeySet{}; q2.Add(k)
done1 := &RangedKeySet{} done1 := &blib.RangedKeySet{}
done2 := &RangedKeySet{} done2 := &blib.RangedKeySet{}
debugfKAdj("\nk%s\n", kstr(k)) debugfKAdj("\nk%s\n", kstr(k))
for !q1.Empty() || !q2.Empty() { for !q1.Empty() || !q2.Empty() {
...@@ -281,10 +277,10 @@ func _KAdj(t1, t2 *tTreeCommit, keysv ...setKey) (kadj KAdjMatrix) { ...@@ -281,10 +277,10 @@ func _KAdj(t1, t2 *tTreeCommit, keysv ...setKey) (kadj KAdjMatrix) {
debugfKAdj(" adj1 += %s\t-> %s\n", kstr(k_), adj1) debugfKAdj(" adj1 += %s\t-> %s\n", kstr(k_), adj1)
} }
} }
b1r := KeyRange{b1.lo, b1.hi_} b1r := blib.KeyRange{b1.lo, b1.hi_}
done1.AddRange(b1r) done1.AddRange(b1r)
// q2 |= (b1.keyrange \ done2) // q2 |= (b1.keyrange \ done2)
δq2 := &RangedKeySet{} δq2 := &blib.RangedKeySet{}
δq2.AddRange(b1r) δq2.AddRange(b1r)
δq2.DifferenceInplace(done2) δq2.DifferenceInplace(done2)
q2.UnionInplace(δq2) q2.UnionInplace(δq2)
...@@ -310,10 +306,10 @@ func _KAdj(t1, t2 *tTreeCommit, keysv ...setKey) (kadj KAdjMatrix) { ...@@ -310,10 +306,10 @@ func _KAdj(t1, t2 *tTreeCommit, keysv ...setKey) (kadj KAdjMatrix) {
debugfKAdj(" adj2 += %s\t-> %s\n", kstr(k_), adj2) debugfKAdj(" adj2 += %s\t-> %s\n", kstr(k_), adj2)
} }
} }
b2r := KeyRange{b2.lo, b2.hi_} b2r := blib.KeyRange{b2.lo, b2.hi_}
done2.AddRange(b2r) done2.AddRange(b2r)
// q1 |= (b2.keyrange \ done1) // q1 |= (b2.keyrange \ done1)
δq1 := &RangedKeySet{} δq1 := &blib.RangedKeySet{}
δq1.AddRange(b2r) δq1.AddRange(b2r)
δq1.DifferenceInplace(done1) δq1.DifferenceInplace(done1)
q1.UnionInplace(δq1) q1.UnionInplace(δq1)
...@@ -343,7 +339,7 @@ func _KAdj(t1, t2 *tTreeCommit, keysv ...setKey) (kadj KAdjMatrix) { ...@@ -343,7 +339,7 @@ func _KAdj(t1, t2 *tTreeCommit, keysv ...setKey) (kadj KAdjMatrix) {
// the cycling phase of update, that is responsible to recompute older // the cycling phase of update, that is responsible to recompute older
// entries when key coverage grows, is exercised by // entries when key coverage grows, is exercised by
// xverifyΔBTail_rebuild. // xverifyΔBTail_rebuild.
func xverifyΔBTail_Update(t *testing.T, subj string, db *zodb.DB, treeRoot zodb.Oid, t1, t2 *tTreeCommit) { func xverifyΔBTail_Update(t *testing.T, subj string, db *zodb.DB, treeRoot zodb.Oid, t1, t2 *xbtreetest.Commit) {
// verify transition at1->at2 for all initial states of tracked {keys} from kv1 + kv2 + ∞ // verify transition at1->at2 for all initial states of tracked {keys} from kv1 + kv2 + ∞
t.Run(fmt.Sprintf("Update/%s→%s", t1.tree, t2.tree), func(t *testing.T) { t.Run(fmt.Sprintf("Update/%s→%s", t1.tree, t2.tree), func(t *testing.T) {
...@@ -370,7 +366,7 @@ func xverifyΔBTail_Update(t *testing.T, subj string, db *zodb.DB, treeRoot zodb ...@@ -370,7 +366,7 @@ func xverifyΔBTail_Update(t *testing.T, subj string, db *zodb.DB, treeRoot zodb
// xverifyΔBTail_Update1 verifies how ΔBTail handles ZODB update at1->at2 from initial // xverifyΔBTail_Update1 verifies how ΔBTail handles ZODB update at1->at2 from initial
// tracked state defined by initialTrackedKeys. // tracked state defined by initialTrackedKeys.
func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zodb.Oid, t1,t2 *tTreeCommit, initialTrackedKeys setKey, kadj KAdjMatrix) { func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zodb.Oid, t1,t2 *xbtreetest.Commit, initialTrackedKeys setKey, kadj KAdjMatrix) {
X := exc.Raiseif X := exc.Raiseif
//t.Logf("\n>>> Track=%s\n", initialTrackedKeys) //t.Logf("\n>>> Track=%s\n", initialTrackedKeys)
...@@ -423,7 +419,7 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod ...@@ -423,7 +419,7 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod
if oid2 == zodb.InvalidOid { // embedded bucket if oid2 == zodb.InvalidOid { // embedded bucket
oid2 = leaf2.parent.oid oid2 = leaf2.parent.oid
} }
if δZset.Has(oid1) || δZset.Has(oid2) || (KeyRange{leaf1.lo,leaf1.hi_} != KeyRange{leaf2.lo,leaf2.hi_}) { if δZset.Has(oid1) || δZset.Has(oid2) || (blib.KeyRange{leaf1.lo,leaf1.hi_} != blib.KeyRange{leaf2.lo,leaf2.hi_}) {
TrackedδZ.Add(k) TrackedδZ.Add(k)
} }
} }
...@@ -452,7 +448,7 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod ...@@ -452,7 +448,7 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod
} }
} }
ø := PPTreeSubSet{} ø := blib.PPTreeSubSet{}
// trackSet1 = xkv1[tracked1] // trackSet1 = xkv1[tracked1]
// trackSet2 = xkv2[tracked2] ( = xkv2[kadj[tracked1]] // trackSet2 = xkv2[tracked2] ( = xkv2[kadj[tracked1]]
...@@ -477,7 +473,7 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod ...@@ -477,7 +473,7 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod
// assert δtkeycov == δ(tkeyCov1, tkeyCov2) // assert δtkeycov == δ(tkeyCov1, tkeyCov2)
δtkeycovOK := tkeyCov2.Difference(tkeyCov1) δtkeycovOK := tkeyCov2.Difference(tkeyCov1)
δtkeycov := &RangedKeySet{} δtkeycov := &blib.RangedKeySet{}
if __, ok := δB1.ByRoot[treeRoot]; ok { if __, ok := δB1.ByRoot[treeRoot]; ok {
δtkeycov = __.δtkeycov1 δtkeycov = __.δtkeycov1
} }
...@@ -558,7 +554,7 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod ...@@ -558,7 +554,7 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod
// assertTrack verifies state of .trackSet and ΔTtail.trackNew. // assertTrack verifies state of .trackSet and ΔTtail.trackNew.
// it assumes that only one tree root is being tracked. // it assumes that only one tree root is being tracked.
// XXX place // XXX place
func (δBtail *ΔBtail) assertTrack(t *testing.T, subj string, trackSetOK PPTreeSubSet, trackNewOK PPTreeSubSet) { func (δBtail *ΔBtail) assertTrack(t *testing.T, subj string, trackSetOK blib.PPTreeSubSet, trackNewOK blib.PPTreeSubSet) {
t.Helper() t.Helper()
if !δBtail.trackSet.Equal(trackSetOK) { if !δBtail.trackSet.Equal(trackSetOK) {
t.Errorf("%s: trackSet:\n\thave: %v\n\twant: %v", subj, δBtail.trackSet, trackSetOK) t.Errorf("%s: trackSet:\n\thave: %v\n\twant: %v", subj, δBtail.trackSet, trackSetOK)
...@@ -605,7 +601,7 @@ func (δBtail *ΔBtail) assertTrack(t *testing.T, subj string, trackSetOK PPTree ...@@ -605,7 +601,7 @@ func (δBtail *ΔBtail) assertTrack(t *testing.T, subj string, trackSetOK PPTree
// t1->t2 further exercises incremental rebuild. // t1->t2 further exercises incremental rebuild.
// //
// It also exercises rebuild phase of ΔBtail.Update. // It also exercises rebuild phase of ΔBtail.Update.
func xverifyΔBTail_rebuild(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, t0, t1, t2 *tTreeCommit) { func xverifyΔBTail_rebuild(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, t0, t1, t2 *xbtreetest.Commit) {
t.Run(fmt.Sprintf("rebuild/%s→%s", t0.tree, t1.tree), func(t *testing.T) { t.Run(fmt.Sprintf("rebuild/%s→%s", t0.tree, t1.tree), func(t *testing.T) {
tAllKeys := allTestKeys(t0, t1, t2) tAllKeys := allTestKeys(t0, t1, t2)
tAllKeyv := tAllKeys.SortedElements() tAllKeyv := tAllKeys.SortedElements()
...@@ -628,7 +624,7 @@ func xverifyΔBTail_rebuild(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, t0, t1 ...@@ -628,7 +624,7 @@ func xverifyΔBTail_rebuild(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, t0, t1
// kadj210 = kadj10·kadj21 // kadj210 = kadj10·kadj21
kadj210 := kadj10.Mul(kadj21) kadj210 := kadj10.Mul(kadj21)
ø := PPTreeSubSet{} ø := blib.PPTreeSubSet{}
// verify t0 -> t1 Track(keys1) Rebuild -> t2 Track(keys2) Rebuild // verify t0 -> t1 Track(keys1) Rebuild -> t2 Track(keys2) Rebuild
// for all combinations of keys1 and keys2 // for all combinations of keys1 and keys2
...@@ -790,10 +786,10 @@ func xverifyΔBTail_rebuild(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, t0, t1 ...@@ -790,10 +786,10 @@ func xverifyΔBTail_rebuild(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, t0, t1
} }
// xverifyΔBTail_rebuild_U verifies ΔBtail state after Update(ti->tj). // xverifyΔBTail_rebuild_U verifies ΔBtail state after Update(ti->tj).
func xverifyΔBTail_rebuild_U(t *testing.T, δbtail *ΔBtail, treeRoot zodb.Oid, ti, tj *tTreeCommit, xat map[zodb.Tid]string, trackSet PPTreeSubSet, vδTok ...map[Key]Δstring) { func xverifyΔBTail_rebuild_U(t *testing.T, δbtail *ΔBtail, treeRoot zodb.Oid, ti, tj *xbtreetest.Commit, xat map[zodb.Tid]string, trackSet blib.PPTreeSubSet, vδTok ...map[Key]Δstring) {
t.Helper() t.Helper()
X := exc.Raiseif X := exc.Raiseif
ø := PPTreeSubSet{} ø := blib.PPTreeSubSet{}
subj := fmt.Sprintf("after Update(@%s→@%s)", xat[ti.at], xat[tj.at]) subj := fmt.Sprintf("after Update(@%s→@%s)", xat[ti.at], xat[tj.at])
...@@ -835,9 +831,9 @@ func xverifyΔBTail_rebuild_U(t *testing.T, δbtail *ΔBtail, treeRoot zodb.Oid, ...@@ -835,9 +831,9 @@ func xverifyΔBTail_rebuild_U(t *testing.T, δbtail *ΔBtail, treeRoot zodb.Oid,
} }
// xverifyΔBTail_rebuild_TR verifies ΔBtail state after Track(keys) + rebuild. // xverifyΔBTail_rebuild_TR verifies ΔBtail state after Track(keys) + rebuild.
func xverifyΔBTail_rebuild_TR(t *testing.T, δbtail *ΔBtail, tj *tTreeCommit, treeRoot zodb.Oid, xat map[zodb.Tid]string, keys setKey, trackSet PPTreeSubSet, trackNew, trackSetAfterRebuild PPTreeSubSet, vδTok ...map[Key]Δstring) { func xverifyΔBTail_rebuild_TR(t *testing.T, δbtail *ΔBtail, tj *xbtreetest.Commit, treeRoot zodb.Oid, xat map[zodb.Tid]string, keys setKey, trackSet blib.PPTreeSubSet, trackNew, trackSetAfterRebuild blib.PPTreeSubSet, vδTok ...map[Key]Δstring) {
t.Helper() t.Helper()
ø := PPTreeSubSet{} ø := blib.PPTreeSubSet{}
// Track(keys) // Track(keys)
xtrackKeys(δbtail, tj, keys) xtrackKeys(δbtail, tj, keys)
...@@ -858,14 +854,14 @@ func xverifyΔBTail_rebuild_TR(t *testing.T, δbtail *ΔBtail, tj *tTreeCommit, ...@@ -858,14 +854,14 @@ func xverifyΔBTail_rebuild_TR(t *testing.T, δbtail *ΔBtail, tj *tTreeCommit,
// assertΔTtail verifies state of ΔTtail that corresponds to treeRoot in δbtail. // assertΔTtail verifies state of ΔTtail that corresponds to treeRoot in δbtail.
// it also verifies that δbtail.vδBroots matches ΔTtail data. // it also verifies that δbtail.vδBroots matches ΔTtail data.
func assertΔTtail(t *testing.T, subj string, δbtail *ΔBtail, tj *tTreeCommit, treeRoot zodb.Oid, xat map[zodb.Tid]string, vδTok ...map[Key]Δstring) { func assertΔTtail(t *testing.T, subj string, δbtail *ΔBtail, tj *xbtreetest.Commit, treeRoot zodb.Oid, xat map[zodb.Tid]string, vδTok ...map[Key]Δstring) {
t.Helper() t.Helper()
// XXX +KVAtTail, +lastRevOf // XXX +KVAtTail, +lastRevOf
l := len(vδTok) l := len(vδTok)
var vatOK []zodb.Tid var vatOK []zodb.Tid
var vδTok_ []map[Key]Δstring var vδTok_ []map[Key]Δstring
at2t := map[zodb.Tid]*tTreeCommit{tj.at: tj} at2t := map[zodb.Tid]*xbtreetest.Commit{tj.at: tj}
t0 := tj t0 := tj
for i := 0; i<l; i++ { for i := 0; i<l; i++ {
// empty vδTok entries means they should be absent in vδT // empty vδTok entries means they should be absent in vδT
...@@ -933,7 +929,7 @@ func assertΔTtail(t *testing.T, subj string, δbtail *ΔBtail, tj *tTreeCommit, ...@@ -933,7 +929,7 @@ func assertΔTtail(t *testing.T, subj string, δbtail *ΔBtail, tj *tTreeCommit,
// xtrackKeys issues δbtail.Track requests for tree[keys]. // xtrackKeys issues δbtail.Track requests for tree[keys].
// XXX place // XXX place
func xtrackKeys(δbtail *ΔBtail, t *tTreeCommit, keys setKey) { func xtrackKeys(δbtail *ΔBtail, t *xbtreetest.Commit, keys setKey) {
X := exc.Raiseif X := exc.Raiseif
head := δbtail.Head() head := δbtail.Head()
if head != t.at { if head != t.at {
...@@ -960,7 +956,7 @@ func xtrackKeys(δbtail *ΔBtail, t *tTreeCommit, keys setKey) { ...@@ -960,7 +956,7 @@ func xtrackKeys(δbtail *ΔBtail, t *tTreeCommit, keys setKey) {
// XXX // XXX
// XXX kill // XXX kill
/* /*
func ___xverifyΔBTail_GetAt(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, vt ...*tTreeCommit) { func ___xverifyΔBTail_GetAt(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, vt ...*xbtreetest.Commit) {
subj := vt[0].tree subj := vt[0].tree
for _, t := range vt[1:] { for _, t := range vt[1:] {
subj += "→" + t.tree subj += "→" + t.tree
...@@ -993,7 +989,7 @@ func ___xverifyΔBTail_GetAt(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, vt .. ...@@ -993,7 +989,7 @@ func ___xverifyΔBTail_GetAt(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, vt ..
}) })
} }
func xverifyΔBTail_GetAt1(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, vt []*tTreeCommit, xat map[zodb.Tid]string, keys setKey) { func xverifyΔBTail_GetAt1(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, vt []*xbtreetest.Commit, xat map[zodb.Tid]string, keys setKey) {
X := exc.Raiseif X := exc.Raiseif
// t1->t2-> ... -> tn // t1->t2-> ... -> tn
...@@ -1081,9 +1077,9 @@ func ΔBTest(xtest interface{}) ΔBTestEntry { ...@@ -1081,9 +1077,9 @@ func ΔBTest(xtest interface{}) ΔBTestEntry {
// testΔBTail verifies ΔBTail on sequence of tree topologies coming from testq. // testΔBTail verifies ΔBTail on sequence of tree topologies coming from testq.
func testΔBTail(t_ *testing.T, testq chan ΔBTestEntry) { func testΔBTail(t_ *testing.T, testq chan ΔBTestEntry) {
t := tNewTreeEnv(t_) t := xbtreetest.NewT(t_)
var t0 *tTreeCommit var t0 *xbtreetest.Commit
for test := range testq { for test := range testq {
t1 := t.Head() t1 := t.Head()
t2 := t.CommitTree(test.tree) t2 := t.CommitTree(test.tree)
...@@ -1563,7 +1559,7 @@ func TestΔBTailAllStructs(t *testing.T) { ...@@ -1563,7 +1559,7 @@ func TestΔBTailAllStructs(t *testing.T) {
func TestΔBtailForget(t_ *testing.T) { func TestΔBtailForget(t_ *testing.T) {
t := tNewTreeEnv(t_) t := xbtreetest.NewT(t_)
X := exc.Raiseif X := exc.Raiseif
t0 := t.CommitTree("T/B:") t0 := t.CommitTree("T/B:")
...@@ -1602,7 +1598,7 @@ func TestΔBtailForget(t_ *testing.T) { ...@@ -1602,7 +1598,7 @@ func TestΔBtailForget(t_ *testing.T) {
func TestΔBtailClone(t_ *testing.T) { func TestΔBtailClone(t_ *testing.T) {
// ΔBtail.Clone had bug that aliased klon data to orig // ΔBtail.Clone had bug that aliased klon data to orig
t := tNewTreeEnv(t_) t := xbtreetest.NewT(t_)
X := exc.Raiseif X := exc.Raiseif
t0 := t.CommitTree("T2/B1:a-B2:b") t0 := t.CommitTree("T2/B1:a-B2:b")
...@@ -1678,7 +1674,7 @@ func TestIntSets(t *testing.T) { ...@@ -1678,7 +1674,7 @@ func TestIntSets(t *testing.T) {
// allTestKeys returns all keys from vt + ∞. // allTestKeys returns all keys from vt + ∞.
func allTestKeys(vt ...*tTreeCommit) setKey { func allTestKeys(vt ...*xbtreetest.Commit) setKey {
allKeys := setKey{}; allKeys.Add(KeyMax) // ∞ simulating ZBigFile.Size() query allKeys := setKey{}; allKeys.Add(KeyMax) // ∞ simulating ZBigFile.Size() query
for _, t := range vt { for _, t := range vt {
for _, b := range t.xkv { for _, b := range t.xkv {
...@@ -1738,34 +1734,3 @@ func δTEqual(δa, δb map[Key]Δstring) bool { ...@@ -1738,34 +1734,3 @@ func δTEqual(δa, δb map[Key]Δstring) bool {
} }
return true return true
} }
// ----------------------------------------
// ZBlk-related functions are imported at runtime by δbtail_x_test
var (
ZTreeGetBlkData func(context.Context, *Tree, Key) (string, bool, []Node, error)
ZGetBlkData func(context.Context, *zodb.Connection, zodb.Oid) (string, error)
)
// xzgetBlkData loads block data from ZBlk object specified by its oid.
func xzgetBlkData(ctx context.Context, zconn *zodb.Connection, zblkOid zodb.Oid) string {
X := exc.Raiseif
if zblkOid == VDEL {
return DEL
}
data, err := ZGetBlkData(ctx, zconn, zblkOid); X(err)
return string(data)
}
// xzgetBlkDataAt loads block data from ZBlk object specified by oid@at.
func xzgetBlkDataAt(db *zodb.DB, zblkOid zodb.Oid, at zodb.Tid) string {
X := exc.Raiseif
txn, ctx := transaction.New(context.Background())
defer txn.Abort()
zconn, err := db.Open(ctx, &zodb.ConnOptions{At: at}); X(err)
return xzgetBlkData(ctx, zconn, zblkOid)
}
...@@ -18,85 +18,7 @@ ...@@ -18,85 +18,7 @@
// See https://www.nexedi.com/licensing for rationale and options. // See https://www.nexedi.com/licensing for rationale and options.
package xbtree_test package xbtree_test
// ZBlk-related part of δbtail_test
import ( import (
"context" _ "lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xbtree/xbtreetest/init"
"fmt"
"lab.nexedi.com/kirr/go123/xerr"
"lab.nexedi.com/kirr/neo/go/zodb"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xbtree"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xzodb"
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/zdata"
) )
type Tree = xbtree.Tree
type Node = xbtree.Node
type Key = xbtree.Key
type ZBlk = zdata.ZBlk
// ztreeGetBlk returns ztree[k] and tree path that lead to this block.
// XXX +return blkRevMax and use it ?
func ztreeGetBlk(ctx context.Context, ztree *Tree, k Key) (zblk ZBlk, ok bool, path []Node, err error) {
path = []Node{}
xzblk, ok, err := ztree.VGet(ctx, k, func(node Node) {
path = append(path, node)
})
if err != nil {
return nil, false, nil, err
}
if ok {
zblk, ok = xzblk.(ZBlk)
if !ok {
return nil, false, nil, fmt.Errorf("expect ZBlk*; got %s", xzodb.TypeOf(xzblk)) // XXX errctx
}
}
return zblk, ok, path, nil
}
func init() {
xbtree.ZTreeGetBlkData = ZTreeGetBlkData
xbtree.ZGetBlkData = ZGetBlkData
}
// ZTreeGetBlkData returns block data from block pointed to by ztree[k].
func ZTreeGetBlkData(ctx context.Context, ztree *Tree, k Key) (data string, ok bool, path []Node, err error) {
defer xerr.Contextf(&err, "@%s: tree<%s>: get blkdata from [%d]", ztree.PJar().At(), ztree.POid(), k)
zblk, ok, path, err := ztreeGetBlk(ctx, ztree, k)
if err != nil || !ok {
return "", ok, path, err
}
bdata, _, err := zblk.LoadBlkData(ctx)
if err != nil {
return "", false, nil, err
}
return string(bdata), true, path, nil
}
// ZGetBlkData loads block data from ZBlk object specified by its oid.
func ZGetBlkData(ctx context.Context, zconn *zodb.Connection, zblkOid zodb.Oid) (data string, err error) {
defer xerr.Contextf(&err, "@%s: get blkdata from obj %s", zconn.At(), zblkOid)
xblk, err := zconn.Get(ctx, zblkOid)
if err != nil {
return "", err
}
zblk, ok := xblk.(ZBlk)
if !ok {
return "", fmt.Errorf("expect ZBlk*; got %s", xzodb.TypeOf(xblk))
}
bdata, _, err := zblk.LoadBlkData(ctx)
if err != nil {
return "", err
}
return string(bdata), nil
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment