Commit 423f77be authored by Kirill Smelkov's avatar Kirill Smelkov

X wcfs: Goodby holeIdx

As a consequence rework treediff to work on key ranges instead of
individial keys.

Adjacency coverage gets wider, but that is ok, because in practice if
keys are added to a BTree - that means that something is added to
ZBigFile, and it is very likely that just added data will be accessed
again. In other words it means that wcfs will very likely track those
data on nearby future read access, so we don't loose anything if we
start to track those keys right at the beginning.

* t2: (74 commits)
  .
  .
  .
  .
  .
  .
  X kadj must be taken into account as kadj^δZ
  .
  .
  .
  .
  .
  .
  .
  .
  .
  .
  .
  .
  .
  ...
parents b0ca031f 006f001a
...@@ -26,7 +26,7 @@ import ( ...@@ -26,7 +26,7 @@ import (
"lab.nexedi.com/kirr/neo/go/zodb" "lab.nexedi.com/kirr/neo/go/zodb"
) )
const debugPPSet = false const tracePPSet = false
// PPTreeSubSet represents PP-connected subset of tree node objects. // PPTreeSubSet represents PP-connected subset of tree node objects.
// //
...@@ -131,14 +131,23 @@ func (S PPTreeSubSet) AddPath(path []zodb.Oid) { ...@@ -131,14 +131,23 @@ func (S PPTreeSubSet) AddPath(path []zodb.Oid) {
} }
} }
// ---- Union/Difference ---- // ---- Union/Difference/Intersetctior ----
// Union returns U = PP(A.leafs | B.leafs)
//
// In other words it returns sum of A and B.
func (A PPTreeSubSet) Union(B PPTreeSubSet) PPTreeSubSet {
U := A.Clone()
U.UnionInplace(B)
return U
}
// UnionInplace sets A = PP(A.leafs | B.leafs) // UnionInplace sets A = PP(A.leafs | B.leafs)
// //
// In other words it adds B nodes to A. // In other words it adds B nodes to A.
func (A PPTreeSubSet) UnionInplace(B PPTreeSubSet) { func (A PPTreeSubSet) UnionInplace(B PPTreeSubSet) {
if debugPPSet { if tracePPSet {
fmt.Printf("\n\nUnionInplace:\n") fmt.Printf("\n\nUnion:\n")
fmt.Printf(" A: %s\n", A) fmt.Printf(" A: %s\n", A)
fmt.Printf(" B: %s\n", B) fmt.Printf(" B: %s\n", B)
defer fmt.Printf("->U: %s\n", A) defer fmt.Printf("->U: %s\n", A)
...@@ -151,12 +160,22 @@ func (A PPTreeSubSet) UnionInplace(B PPTreeSubSet) { ...@@ -151,12 +160,22 @@ func (A PPTreeSubSet) UnionInplace(B PPTreeSubSet) {
A.xUnionInplace(B) A.xUnionInplace(B)
} }
// Difference returns D = PP(A.leafs \ B.leafs)
//
// In other words ... XXX
func (A PPTreeSubSet) Difference(B PPTreeSubSet) PPTreeSubSet {
D := A.Clone()
D.DifferenceInplace(B)
return D
}
// DifferenceInplace sets A = PP(A.leafs \ B.leafs) // DifferenceInplace sets A = PP(A.leafs \ B.leafs)
// //
// In other words it removes B nodes from A while still maintaining A as PP-connected. // In other words it removes B nodes from A while still maintaining A as PP-connected.
func (A PPTreeSubSet) DifferenceInplace(B PPTreeSubSet) { func (A PPTreeSubSet) DifferenceInplace(B PPTreeSubSet) {
if debugPPSet { if tracePPSet {
fmt.Printf("\n\nDifferenceInplace:\n") fmt.Printf("\n\nDifference:\n")
fmt.Printf(" A: %s\n", A) fmt.Printf(" A: %s\n", A)
fmt.Printf(" B: %s\n", B) fmt.Printf(" B: %s\n", B)
defer fmt.Printf("->D: %s\n", A) defer fmt.Printf("->D: %s\n", A)
...@@ -169,9 +188,11 @@ func (A PPTreeSubSet) DifferenceInplace(B PPTreeSubSet) { ...@@ -169,9 +188,11 @@ func (A PPTreeSubSet) DifferenceInplace(B PPTreeSubSet) {
A.xDifferenceInplace(B) A.xDifferenceInplace(B)
} }
// XXX Intersection
func (A PPTreeSubSet) xUnionInplace(B PPTreeSubSet) { func (A PPTreeSubSet) xUnionInplace(B PPTreeSubSet) {
if debugPPSet { if tracePPSet {
fmt.Printf("\n\n xUnionInplace:\n") fmt.Printf("\n\n xUnion:\n")
fmt.Printf(" a: %s\n", A) fmt.Printf(" a: %s\n", A)
fmt.Printf(" b: %s\n", B) fmt.Printf(" b: %s\n", B)
defer fmt.Printf(" ->u: %s\n", A) defer fmt.Printf(" ->u: %s\n", A)
...@@ -204,8 +225,8 @@ func (A PPTreeSubSet) xUnionInplace(B PPTreeSubSet) { ...@@ -204,8 +225,8 @@ func (A PPTreeSubSet) xUnionInplace(B PPTreeSubSet) {
} }
func (A PPTreeSubSet) xDifferenceInplace(B PPTreeSubSet) { func (A PPTreeSubSet) xDifferenceInplace(B PPTreeSubSet) {
if debugPPSet { if tracePPSet {
fmt.Printf("\n\n xDifferenceInplace:\n") fmt.Printf("\n\n xDifference:\n")
fmt.Printf(" a: %s\n", A) fmt.Printf(" a: %s\n", A)
fmt.Printf(" b: %s\n", B) fmt.Printf(" b: %s\n", B)
defer fmt.Printf(" ->d: %s\n", A) defer fmt.Printf(" ->d: %s\n", A)
...@@ -445,7 +466,7 @@ func (δ *ΔPPTreeSubSet) Reverse() { ...@@ -445,7 +466,7 @@ func (δ *ΔPPTreeSubSet) Reverse() {
// //
// See ΔPPTreeSubSet documentation for details. // See ΔPPTreeSubSet documentation for details.
func (S PPTreeSubSet) ApplyΔ(δ *ΔPPTreeSubSet) { func (S PPTreeSubSet) ApplyΔ(δ *ΔPPTreeSubSet) {
if debugPPSet { if tracePPSet {
fmt.Printf("\n\nApplyΔ\n") fmt.Printf("\n\nApplyΔ\n")
fmt.Printf(" A: %s\n", S) fmt.Printf(" A: %s\n", S)
fmt.Printf(" -: %s\n", δ.Del) fmt.Printf(" -: %s\n", δ.Del)
......
...@@ -33,8 +33,8 @@ func TestPPTreeSubSetOps(t *testing.T) { ...@@ -33,8 +33,8 @@ func TestPPTreeSubSetOps(t *testing.T) {
d d
ø = zodb.InvalidOid ø = zodb.InvalidOid
) )
type S = PPTreeSubSet type S = PPTreeSubSet
type testEntry struct { type testEntry struct {
A, B S A, B S
Union S Union S
Difference S Difference S
...@@ -82,10 +82,8 @@ func TestPPTreeSubSetOps(t *testing.T) { ...@@ -82,10 +82,8 @@ func TestPPTreeSubSetOps(t *testing.T) {
} }
for _, tt := range testv { for _, tt := range testv {
U := tt.A.Clone() U := tt.A.Union(tt.B)
U.UnionInplace(tt.B) D := tt.A.Difference(tt.B)
D := tt.A.Clone()
D.DifferenceInplace(tt.B)
if !U.Equal(tt.Union) { if !U.Equal(tt.Union) {
t.Errorf("Union:\n A: %s\n B: %s\n ->u: %s\n okU: %s\n", tt.A, tt.B, U, tt.Union) t.Errorf("Union:\n A: %s\n B: %s\n ->u: %s\n okU: %s\n", tt.A, tt.B, U, tt.Union)
......
// Copyright (C) 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 xbtree
// set of [lo,hi) Key ranges.
import (
"fmt"
"sort"
)
const traceRangeSet = false
const debugRangeSet = false
// KeyRange represents [lo,hi) Key range.
type KeyRange struct {
lo Key
hi_ Key // NOTE _not_ hi) to avoid overflow at ∞; hi = hi_ + 1
}
// RangedKeySet is set of Keys with adjacent keys coaleced into Ranges.
//
// Zero value represents empty set.
type RangedKeySet struct {
// TODO rework to use BTree lo->hi_ instead if in practice in treediff,
// and other usage places, N(ranges) turns out to be not small
// (i.e. performance turns out to be not acceptable)
rangev []KeyRange // lo↑
}
// Has returns whether key k belongs to the range.
func (r *KeyRange) Has(k Key) bool {
return (r.lo <= k && k <= r.hi_)
}
// Add adds key k to the set.
func (S *RangedKeySet) Add(k Key) {
S.AddRange(KeyRange{lo: k, hi_: k})
}
// Del removes key k from the set.
func (S *RangedKeySet) Del(k Key) {
S.DelRange(KeyRange{lo: k, hi_: k})
}
// Has returns whether key k belongs to the set.
func (S *RangedKeySet) Has(k Key) bool {
return S.HasRange(KeyRange{lo: k, hi_: k})
}
// AddRange adds range r to the set.
func (S *RangedKeySet) AddRange(r KeyRange) {
if traceRangeSet {
fmt.Printf("\n\nAddRange:\n")
fmt.Printf(" S: %s\n", S)
fmt.Printf(" r: %s\n", r)
defer fmt.Printf("->u: %s\n", S)
}
S.verify()
defer S.verify()
// find first ilo: r.lo < [ilo].hi
l := len(S.rangev)
ilo := sort.Search(l, func(i int) bool {
return r.lo <= S.rangev[i].hi_
})
debugfRSet("\tilo: %d\n", ilo)
if ilo == l { // not found
S.rangev = append(S.rangev, r)
l++
debugfRSet("\tappend %s\t-> %s\n", r, S)
}
// find last jhi: [jhi].lo < r.hi
jhi := ilo
for ;; jhi++ {
if jhi == l {
break
}
if S.rangev[jhi].lo <= r.hi_ {
continue
}
break
}
debugfRSet("\tjhi: %d\n", jhi)
// entries in [ilo:jhi) ∈ [r.lo,r.hi) and should be merged into one
if (jhi - ilo) > 1 {
lo := S.rangev[ilo].lo
hi_ := S.rangev[jhi-1].hi_
S.rangev = append(
S.rangev[:ilo], append([]KeyRange{
KeyRange{lo, hi_}},
S.rangev[jhi:]...)...)
debugfRSet("\tmerge S[%d:%d]\t-> %s\n", ilo, jhi, S)
}
jhi = -1 // no longer valid
// if [r.lo,r.hi) was outside of any entry - create new entry
if r.hi_ < S.rangev[ilo].lo {
S.rangev = append(
S.rangev[:ilo], append([]KeyRange{
r},
S.rangev[ilo:]...)...)
debugfRSet("\tinsert %s\t-> %s\n", r, S)
}
// now we have covered entries merged as needed into [ilo]
// extend this entry if r coverage is wider
if r.lo < S.rangev[ilo].lo {
S.rangev[ilo].lo = r.lo
debugfRSet("\textend left\t-> %s\n", S)
}
if r.hi_ > S.rangev[ilo].hi_ {
S.rangev[ilo].hi_ = r.hi_
debugfRSet("\textend right\t-> %s\n", S)
}
// and check if we should merge it with right/left neighbours
if ilo+1 < len(S.rangev) { // right
if S.rangev[ilo].hi_+1 == S.rangev[ilo+1].lo {
S.rangev = append(
S.rangev[:ilo], append([]KeyRange{
KeyRange{S.rangev[ilo].lo, S.rangev[ilo+1].hi_}},
S.rangev[ilo+2:]...)...)
debugfRSet("\tmerge right\t-> %s\n", S)
}
}
if ilo > 0 { // left
if S.rangev[ilo-1].hi_+1 == S.rangev[ilo].lo {
S.rangev = append(
S.rangev[:ilo-1], append([]KeyRange{
KeyRange{S.rangev[ilo-1].lo, S.rangev[ilo].hi_}},
S.rangev[ilo+1:]...)...)
debugfRSet("\tmerge left\t-> %s\n", S)
}
}
// done
}
// DelRange removes range r from the set.
func (S *RangedKeySet) DelRange(r KeyRange) {
if traceRangeSet {
fmt.Printf("\n\nDelRange:\n")
fmt.Printf(" S: %s\n", S)
fmt.Printf(" r: %s\n", r)
defer fmt.Printf("->d: %s\n", S)
}
S.verify()
defer S.verify()
// find first ilo: r.lo < [ilo].hi
l := len(S.rangev)
ilo := sort.Search(l, func(i int) bool {
return r.lo <= S.rangev[i].hi_
})
debugfRSet("\tilo: %d\n", ilo)
if ilo == l { // not found
debugfRSet("\tnon-overlap right\n")
return
}
// find last jhi: [jhi].lo < r.hi
jhi := ilo
for ;; jhi++ {
if jhi == l {
break
}
if S.rangev[jhi].lo <= r.hi_ {
continue
}
break
}
debugfRSet("\tjhi: %d\n", jhi)
if jhi == 0 {
debugfRSet("\tnon-overlap left\n")
return
}
// [ilo+1:jhi-1] should be deleted
// [ilo] and [jhi-1] overlap with [r.lo,r.hi) - they shuold be deleted, or shrinked,
// or split+shrinked if ilo==jhi-1 and r is inside [ilo]
if jhi-ilo == 1 && S.rangev[ilo].lo < r.lo && r.hi_ < S.rangev[ilo].hi_ {
x := S.rangev[ilo]
S.rangev = append(
S.rangev[:ilo], append([]KeyRange{
x, x},
S.rangev[ilo+1:]...)...)
jhi++
debugfRSet("\tpresplit copy %s\t-> %s\n", x, S)
}
if S.rangev[ilo].lo < r.lo { // shrink left
S.rangev = append(
S.rangev[:ilo], append([]KeyRange{
KeyRange{S.rangev[ilo].lo, r.lo-1}},
S.rangev[ilo+1:]...)...)
ilo++
debugfRSet("\tshrink [%d] left\t-> %s\n", ilo, S)
}
if r.hi_ < S.rangev[jhi-1].hi_ { // shrink right
S.rangev = append(
S.rangev[:jhi-1], append([]KeyRange{
KeyRange{r.hi_+1, S.rangev[jhi-1].hi_}},
S.rangev[jhi:]...)...)
jhi--
debugfRSet("\tshrink [%d] right\t-> %s\n", jhi-1, S)
}
if (jhi - ilo) > 0 {
S.rangev = append(
S.rangev[:ilo],
S.rangev[jhi:]...)
debugfRSet("\tdelete S[%d:%d]\t-> %s\n", ilo, jhi, S)
}
// done
}
// HasRange returns whether all keys from range r belong to the set.
func (S *RangedKeySet) HasRange(r KeyRange) (yes bool) {
if traceRangeSet {
fmt.Printf("\n\nHasRange:\n")
fmt.Printf(" S: %s\n", S)
fmt.Printf(" r: %s\n", r)
defer func() {
fmt.Printf("->·: %v\n", yes)
}()
}
S.verify()
// find first ilo: r.lo < [ilo].hi
l := len(S.rangev)
ilo := sort.Search(l, func(i int) bool {
return r.lo <= S.rangev[i].hi_
})
debugfRSet("\tilo: %d\n", ilo)
if ilo == l { // not found
return false
}
// all keys from r are in S if r ∈ [ilo]
return (S.rangev[ilo].lo <= r.lo && r.hi_ <= S.rangev[ilo].hi_)
}
// Union returns RangedKeySet(A.keys | B.keys).
func (A *RangedKeySet) Union(B *RangedKeySet) *RangedKeySet {
U := A.Clone()
U.UnionInplace(B)
return U
}
// Difference returns RangedKeySet(A.keys \ B.keys).
func (A *RangedKeySet) Difference(B *RangedKeySet) *RangedKeySet {
D := A.Clone()
D.DifferenceInplace(B)
return D
}
// XXX Intersection
func (A *RangedKeySet) UnionInplace(B *RangedKeySet) {
A.verify()
B.verify()
defer A.verify()
// XXX dumb
for _, r := range B.rangev {
A.AddRange(r)
}
}
func (A *RangedKeySet) DifferenceInplace(B *RangedKeySet) {
A.verify()
B.verify()
defer A.verify()
// XXX dumb
for _, r := range B.rangev {
if len(A.rangev) == 0 {
break
}
A.DelRange(r)
}
}
// --------
// verify check RangedKeySet for internal consistency:
// - ranges must be not overlapping nor adjacent and ↑
func (S *RangedKeySet) verify() {
// XXX !debug -> return ?
var badv []string
badf := func(format string, argv ...interface{}) {
badv = append(badv, fmt.Sprintf(format, argv...))
}
defer func() {
if badv != nil {
emsg := fmt.Sprintf("S.verify: fail:\n\n")
for _, bad := range badv {
emsg += fmt.Sprintf("- %s\n", bad)
}
emsg += fmt.Sprintf("\nS: %s\n", S)
panic(emsg)
}
}()
hi_Prev := KeyMin
for i, r := range S.rangev {
hiPrev := hi_Prev + 1
if i > 0 && !(hiPrev < r.lo) { // NOTE not ≤ - adjacent ranges must be merged
badf("[%d]: !(hiPrev < r.lo)", i)
}
if !(r.lo <= r.hi_) {
badf("[%d]: !(r.lo <= r.hi_)", i)
}
hi_Prev = r.hi_
}
}
// Clone returns copy of the set.
func (orig *RangedKeySet) Clone() *RangedKeySet {
klon := &RangedKeySet{}
klon.rangev = append(klon.rangev, orig.rangev...)
return klon
}
// Empty returns whether the set is empty.
func (S *RangedKeySet) Empty() bool {
return len(S.rangev) == 0
}
// Equal returns whether A == B.
func (A *RangedKeySet) Equal(B *RangedKeySet) bool {
if len(A.rangev) != len(B.rangev) {
return false
}
for i, ra := range A.rangev {
rb := B.rangev[i]
if ra != rb {
return false
}
}
return true
}
// Clear removes all elements from the set.
func (S *RangedKeySet) Clear() {
S.rangev = nil
}
// AllRanges returns slice of all key ranges in the set.
// XXX -> iter?
func (S *RangedKeySet) AllRanges() /*readonly*/[]KeyRange {
return S.rangev
}
func (S RangedKeySet) String() string {
s := "{"
for i, r := range S.rangev {
if i > 0 {
s += " "
}
s += r.String()
}
s += "}"
return s
}
func (r KeyRange) String() string {
hi := r.hi_
if hi < KeyMax {
hi += 1
}
return fmt.Sprintf("[%s,%s)", kstr(r.lo), kstr(hi))
}
func debugfRSet(format string, argv ...interface{}) {
if !debugRangeSet {
return
}
fmt.Printf(format, argv...)
}
// Copyright (C) 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 xbtree
import (
"testing"
)
func TestRangedKeySet(t *testing.T) {
const (
oo = KeyMax
noo = KeyMin
)
type testEntry struct {
A, B *RangedKeySet
Union *RangedKeySet
Difference *RangedKeySet
}
E := func(A, B, U, D *RangedKeySet) testEntry {
return testEntry{A, B, U, D}
}
// S is shorthand to create RangedKeySet, e.g. S(1,2, 4,5) will return {[1,2) [4,5)}
S := func(kv ...Key) *RangedKeySet {
l := len(kv)
if l % 2 != 0 {
panic("odd number of keys")
}
S := &RangedKeySet{}
for i := 0; i < l/2; i++ {
// construct .rangev directly, not via AddRange
lo := kv[2*i]
hi := kv[2*i+1]
hi_ := hi
if hi_ != oo {
hi_--
}
S.rangev = append(S.rangev, KeyRange{lo, hi_})
}
S.verify()
return S
}
testv := []testEntry{
E(
S(), // A
S(), // B
S(), // U
S()), // D
E(
S(1,2), // A
S(1,2), // B
S(1,2), // U
S()), // D
// adjacent [1,3) [3,5)
E(
S(1,3), // A
S(3,5), // B
S(1,5), // U
S(1,3)), // D
// overlapping [1,3) [2,4)
E(
S(1,3), // A
S(2,4), // B
S(1,4), // U
S(1,2)), // D
// [1,7) \ [3,5) -> [1,3) [5,7)
E(
S(1,7), // A
S(3,5), // B
S(1,7),
S(1,3, 5,7)),
// several ranges \ [-∞, ∞) -> ø
E(
S(1,3, 5,7, 11,100), // A
S(noo, oo), // B
S(noo, oo), // U
S()), // D
// [1,3) [5,7) + insert [3,5) -> [1,7)
E(
S(1,3, 5,7), // A
S(3,5), // B
S(1,7), // U
S(1,3, 5,7)), // D
// delete covering several ranges
// [-1,0) [1,3) [5,7) [9,11) [15,20) [100,200) \ [2,17)
E(
S(-1,0, 1,3, 5,7, 9,11, 15,20, 100,200), // A
S(2,17), // B
S(-1,0, 1,20, 100,200), // U
S(-1,0, 1,2, 17,20, 100,200)), // D
}
for _, tt := range testv {
A := tt.A
B := tt.B
U := A.Union(B)
D := A.Difference(B)
if !U.Equal(tt.Union) {
t.Errorf("Union:\n A: %s\n B: %s\n ->u: %s\n okU: %s\n", tt.A, tt.B, U, tt.Union)
}
if !D.Equal(tt.Difference) {
t.Errorf("Difference:\n A: %s\n B: %s\n ->d: %s\n okD: %s\n", tt.A, tt.B, D, tt.Difference)
}
// HasRange
assertHasRange(t, A.AllRanges(), A, true)
assertHasRange(t, B.AllRanges(), B, true)
assertHasRange(t, A.AllRanges(), U, true)
assertHasRange(t, B.AllRanges(), U, true)
Dab := D
Dba := B.Difference(A)
assertHasRange(t, Dab.AllRanges(), A, true)
assertHasRange(t, Dab.AllRanges(), B, false)
assertHasRange(t, Dba.AllRanges(), B, true)
assertHasRange(t, Dba.AllRanges(), A, false)
}
}
// assertHasRange asserts for all ranges from rangev that S.HasRange(r) == hasOK
func assertHasRange(t *testing.T, rangev []KeyRange, S *RangedKeySet, hasOK bool) {
t.Helper()
for _, r := range rangev {
has := S.HasRange(r)
if has != hasOK {
t.Errorf("HasRange:\n S: %s\n r: %s\n ->: %v\n ok: %v\n", S, r, has, hasOK)
}
}
}
...@@ -89,25 +89,6 @@ type ΔValue struct { ...@@ -89,25 +89,6 @@ type ΔValue struct {
} }
// treeSetKey represents ordered set of keys.
// it can be point-queried and range-accessed.
// TODO -> btree
type treeSetKey struct {
SetKey
}
// InRange returns
func (hi treeSetKey) GetInRange(lo, hi_ Key) SetKey {
// FIXME dumb O(n) -> TODO use cznic/b
ret := SetKey{}
for k := range hi.SetKey {
if lo <= k && k <= hi_ {
ret.Add(k)
}
}
return ret
}
// δZConnectTracked computes connected closure of δZ/T. // δZConnectTracked computes connected closure of δZ/T.
// //
// δZ - all changes in a ZODB transaction. // δZ - all changes in a ZODB transaction.
...@@ -175,7 +156,7 @@ func δZConnectTracked(δZv []zodb.Oid, T PPTreeSubSet) (δZTC SetOid, δtopsByR ...@@ -175,7 +156,7 @@ func δZConnectTracked(δZv []zodb.Oid, T PPTreeSubSet) (δZTC SetOid, δtopsByR
// nodeInRange represents a Node coming under [lo, hi_] key range in its tree. // nodeInRange represents a Node coming under [lo, hi_] key range in its tree.
type nodeInRange struct { type nodeInRange struct {
prefix []zodb.Oid // path to this node goes via this objects prefix []zodb.Oid // path to this node goes via this objects
lo, hi_ Key // [lo, hi_] NOTE _not_ hi) not to overflow at ∞ lo, hi_ Key // [lo, hi_] NOTE _not_ hi) not to overflow at ∞ XXX -> Range
node Node node Node
done bool // whether this node was already taken into account while computing diff done bool // whether this node was already taken into account while computing diff
} }
...@@ -192,7 +173,8 @@ func (n *nodeInRange) NodePath() []Node { ...@@ -192,7 +173,8 @@ func (n *nodeInRange) NodePath() []Node {
} }
*/ */
func (n *nodeInRange) Path() []zodb.Oid { func (n *nodeInRange) Path() []zodb.Oid {
return append(n.prefix, n.node.POid()) // return full copy - else .prefix can become aliased in between children of a node
return append([]zodb.Oid{}, append(n.prefix, n.node.POid())...)
} }
// rangeSplit represents set of nodes covering a range. // rangeSplit represents set of nodes covering a range.
...@@ -341,10 +323,7 @@ func (rs rangeSplit) String() string { ...@@ -341,10 +323,7 @@ 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, err error) {
// XXX holeIdx is updated XXX -> return similarly to δtrack
// XXX ^^^ -> but better kill holeIdx and do everything only via trackSet
func treediff(ctx context.Context, root zodb.Oid, δtops SetOid, δZTC SetOid, trackSet PPTreeSubSet, holeIdx treeSetKey, zconnOld, zconnNew *zodb.Connection) (δT map[Key]ΔValue, δtrack *ΔPPTreeSubSet, 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)
tracef("\ntreediff %s δtops: %v δZTC: %v\n", root, δtops, δZTC) tracef("\ntreediff %s δtops: %v δZTC: %v\n", root, δtops, δZTC)
...@@ -361,7 +340,7 @@ func treediff(ctx context.Context, root zodb.Oid, δtops SetOid, δZTC SetOid, t ...@@ -361,7 +340,7 @@ func treediff(ctx context.Context, root zodb.Oid, δtops SetOid, δZTC SetOid, t
return nil, nil, err return nil, nil, err
} }
δtop, δtrackTop, err := diffX(ctx, a, b, δZTC, trackSet, holeIdx) δtop, δtrackTop, err := diffX(ctx, a, b, δZTC, trackSet)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
...@@ -380,16 +359,6 @@ func treediff(ctx context.Context, root zodb.Oid, δtops SetOid, δZTC SetOid, t ...@@ -380,16 +359,6 @@ func treediff(ctx context.Context, root zodb.Oid, δtops SetOid, δZTC SetOid, t
δtrackv = append(δtrackv, δtrackTop) δtrackv = append(δtrackv, δtrackTop)
} }
// adjust holeIdx
for k, δv := range δT {
if δv.Old == VDEL {
holeIdx.Del(k)
}
if δv.New == VDEL {
holeIdx.Add(k)
}
}
// adjust trackSet by merge(δtrackTops) // adjust trackSet by merge(δtrackTops)
δtrack = &ΔPPTreeSubSet{Del: PPTreeSubSet{}, Add: PPTreeSubSet{}, δnchildNonLeafs: map[zodb.Oid]int{}} δtrack = &ΔPPTreeSubSet{Del: PPTreeSubSet{}, Add: PPTreeSubSet{}, δnchildNonLeafs: map[zodb.Oid]int{}}
for _, δ := range δtrackv { for _, δ := range δtrackv {
...@@ -410,7 +379,7 @@ func treediff(ctx context.Context, root zodb.Oid, δtops SetOid, δZTC SetOid, t ...@@ -410,7 +379,7 @@ func treediff(ctx context.Context, root zodb.Oid, δtops SetOid, δZTC SetOid, t
// //
// δtrack is trackSet δ that needs to be applied to trackSet to keep it // δtrack is trackSet δ that needs to be applied to trackSet to keep it
// consistent with b (= a + δ). // consistent with b (= a + δ).
func diffX(ctx context.Context, a, b Node, δZTC SetOid, trackSet PPTreeSubSet, holeIdx treeSetKey) (δ map[Key]ΔValue, δtrack *ΔPPTreeSubSet, err error) { func diffX(ctx context.Context, a, b Node, δZTC SetOid, trackSet PPTreeSubSet) (δ map[Key]ΔValue, δtrack *ΔPPTreeSubSet, 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?
} }
...@@ -445,7 +414,7 @@ func diffX(ctx context.Context, a, b Node, δZTC SetOid, trackSet PPTreeSubSet, ...@@ -445,7 +414,7 @@ func diffX(ctx context.Context, a, b Node, δZTC SetOid, trackSet PPTreeSubSet,
} }
if isT { if isT {
return diffT(ctx, aT, bT, δZTC, trackSet, holeIdx) return diffT(ctx, aT, bT, δZTC, trackSet)
} else { } else {
var δtrack *ΔPPTreeSubSet var δtrack *ΔPPTreeSubSet
δ, err := diffB(ctx, aB, bB) δ, err := diffB(ctx, aB, bB)
...@@ -460,7 +429,7 @@ func diffX(ctx context.Context, a, b Node, δZTC SetOid, trackSet PPTreeSubSet, ...@@ -460,7 +429,7 @@ 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, holeIdx treeSetKey) (δ map[Key]ΔValue, δtrack *ΔPPTreeSubSet, err error) { func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackSet PPTreeSubSet) (δ map[Key]ΔValue, δtrack *ΔPPTreeSubSet, err error) {
tracef(" diffT %s %s\n", xidOf(A), xidOf(B)) tracef(" 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))
...@@ -518,20 +487,26 @@ func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackSet PPTreeSubSet, ...@@ -518,20 +487,26 @@ func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackSet PPTreeSubSet,
Bv := rangeSplit{btop} // nodes expanded from B Bv := rangeSplit{btop} // nodes expanded from B
// for phase 2: // for phase 2:
Akqueue := SetKey{} // queue for keys in A to be processed for δ- Akqueue := &RangedKeySet{} // queue for keys in A to be processed for δ-
Bkqueue := SetKey{} // ----//---- in B for δ+ Bkqueue := &RangedKeySet{} // ----//---- in B for δ+
Akdone := SetKey{} // already processed keys in A Akdone := &RangedKeySet{} // already processed keys in A
Bkdone := SetKey{} // ----//---- in B Bkdone := &RangedKeySet{} // ----//---- in B
Aktodo := func(k Key) { Aktodo := func(r KeyRange) {
if !Akdone.Has(k) { if !Akdone.HasRange(r) {
tracef(" Akq <- %d\n", k) δtodo := &RangedKeySet{}
Akqueue.Add(k) δtodo.AddRange(r)
δtodo.DifferenceInplace(Akdone)
tracef(" Akq <- %s\n", δtodo)
Akqueue.UnionInplace(δtodo)
} }
} }
Bktodo := func(k Key) { Bktodo := func(r KeyRange) {
if !Bkdone.Has(k) { if !Bkdone.HasRange(r) {
tracef(" Bkq <- %d\n", k) δtodo := &RangedKeySet{}
Bkqueue.Add(k) δtodo.AddRange(r)
δtodo.DifferenceInplace(Bkdone)
tracef(" Bkq <- %s\n", δtodo)
Bkqueue.UnionInplace(δtodo)
} }
} }
...@@ -564,23 +539,14 @@ func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackSet PPTreeSubSet, ...@@ -564,23 +539,14 @@ func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackSet PPTreeSubSet,
err = δMerge(δ, δA); /*X*/if err != nil { return nil,nil, err } err = δMerge(δ, δA); /*X*/if err != nil { return nil,nil, err }
δtrack.Del.AddPath(ra.Path()) δtrack.Del.AddPath(ra.Path())
// Bkqueue <- δA // Bkqueue <- ra.range
for k := range δA { Bktodo(KeyRange{ra.lo, ra.hi_})
Akdone.Add(k)
Bktodo(k)
}
// Bkqueue <- holes(ra.range)
for k := range holeIdx.GetInRange(ra.lo, ra.hi_) {
Bktodo(k)
}
ra.done = true ra.done = true
case *Tree: case *Tree:
// empty tree - only queue holes covered by it // empty tree - only queue holes covered by it
if len(a.Entryv()) == 0 { if len(a.Entryv()) == 0 {
for k := range holeIdx.GetInRange(ra.lo, ra.hi_) { Bktodo(KeyRange{ra.lo, ra.hi_})
Bktodo(k)
}
continue continue
} }
...@@ -634,8 +600,6 @@ func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackSet PPTreeSubSet, ...@@ -634,8 +600,6 @@ func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackSet PPTreeSubSet,
} }
if found { if found {
// ac can be skipped // ac can be skipped
// XXX Bkqueue <- holes(ac.range \ bc.range) XXX test for this
// adjust trackSet since path to the node could have changed // adjust trackSet since path to the node could have changed
apath := trackSet.Path(acOid) apath := trackSet.Path(acOid)
bpath := BtrackSet.Path(acOid) bpath := BtrackSet.Path(acOid)
...@@ -672,9 +636,6 @@ func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackSet PPTreeSubSet, ...@@ -672,9 +636,6 @@ func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackSet PPTreeSubSet,
// * +B12, which queues A.2 and leads to // * +B12, which queues A.2 and leads to
// * -B23, which queues B.3 and leads to // * -B23, which queues B.3 and leads to
// * +B23, ... // * +B23, ...
//
// XXX inefficient: we process each key separately, while they can be
// processed in sorted batches.
tracef("\nphase 2:\n") tracef("\nphase 2:\n")
for { for {
tracef("\n") tracef("\n")
...@@ -683,69 +644,80 @@ func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackSet PPTreeSubSet, ...@@ -683,69 +644,80 @@ func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackSet PPTreeSubSet,
tracef("\n") tracef("\n")
tracef(" Bkq: %s\n", Bkqueue) tracef(" Bkq: %s\n", Bkqueue)
if len(Bkqueue) == 0 { if Bkqueue.Empty() {
break break
} }
for k := range Bkqueue { for _, r := range Bkqueue.AllRanges() {
b, err := Bv.GetToLeaf(ctx, k); /*X*/if err != nil { return nil,nil, err } lo := r.lo
tracef(" B k%d -> %s\n", k, b) for {
// +bucket if that bucket is reached for the first time b, err := Bv.GetToLeaf(ctx, lo); /*X*/if err != nil { return nil,nil, err }
if !b.done { tracef(" B k%d -> %s\n", lo, b)
var δB map[Key]ΔValue // +bucket if that bucket is reached for the first time
bbucket, ok := b.node.(*Bucket) if !b.done {
if ok { // !ok means ø tree var δB map[Key]ΔValue
δB, err = diffB(ctx, nil, bbucket); /*X*/if err != nil { return nil,nil, err } bbucket, ok := b.node.(*Bucket)
} if ok { // !ok means ø tree
δB, err = diffB(ctx, nil, bbucket); /*X*/if err != nil { return nil,nil, err }
}
// δ <- δB // δ <- δB
err = δMerge(δ, δB); /*X*/if err != nil { return nil,nil, err } err = δMerge(δ, δB); /*X*/if err != nil { return nil,nil, err }
δtrack.Add.AddPath(b.Path()) δtrack.Add.AddPath(b.Path())
// Akqueue <- δB // Akqueue <- δB
for k_ := range δB { br := KeyRange{b.lo, b.hi_}
Bkdone.Add(k_) Bkdone.AddRange(br)
Aktodo(k_) Aktodo(br)
b.done = true
} }
b.done = true // stop if r coverage is complete
if r.hi_ <= b.hi_ {
break
}
// continue with next right bucket
lo = b.hi_ + 1
} }
// XXX k is not there -> hole XXX test
} }
Bkqueue = SetKey{} Bkqueue.Clear()
tracef("\n") tracef("\n")
tracef(" Akq: %s\n", Akqueue) tracef(" Akq: %s\n", Akqueue)
for k := range Akqueue { for _, r := range Akqueue.AllRanges() {
a, err := Av.GetToLeaf(ctx, k); /*X*/if err != nil { return nil,nil, err } lo := r.lo
tracef(" A k%d -> %s\n", k, a) for {
// -bucket if that bucket is reached for the first time a, err := Av.GetToLeaf(ctx, lo); /*X*/if err != nil { return nil,nil, err }
if !a.done { tracef(" A k%d -> %s\n", lo, a)
var δA map[Key]ΔValue // -bucket if that bucket is reached for the first time
abucket, ok := a.node.(*Bucket) if !a.done {
if ok { // !ok means ø tree var δA map[Key]ΔValue
δA, err = diffB(ctx, abucket, nil); /*X*/if err != nil { return nil,nil, err } abucket, ok := a.node.(*Bucket)
} if ok { // !ok means ø tree
δA, err = diffB(ctx, abucket, nil); /*X*/if err != nil { return nil,nil, err }
}
// δ <- δA // δ <- δA
err = δMerge(δ, δA); /*X*/if err != nil { return nil,nil, err } err = δMerge(δ, δA); /*X*/if err != nil { return nil,nil, err }
δtrack.Del.AddPath(a.Path()) δtrack.Del.AddPath(a.Path())
// Bkqueue <- δA // Bkqueue <- a.range
for k_ := range δA { ar := KeyRange{a.lo, a.hi_}
Akdone.Add(k_) Akdone.AddRange(ar)
Bktodo(k_) Bktodo(ar)
}
// Bkqueue <- holes(a.range) a.done = true
for k_ := range holeIdx.GetInRange(a.lo, a.hi_) {
Bktodo(k_)
} }
a.done = true // continue with next right bucket until r coverage is complete
if r.hi_ <= a.hi_ {
break
}
lo = a.hi_ + 1
} }
} }
Akqueue = SetKey{} Akqueue.Clear()
} }
return δ, δtrack, nil return δ, δtrack, nil
...@@ -930,11 +902,13 @@ func xidOf(obj zodb.IPersistent) string { ...@@ -930,11 +902,13 @@ func xidOf(obj zodb.IPersistent) string {
return xid.String() return xid.String()
} }
func (rn nodeInRange) String() string { func (rn *nodeInRange) String() string {
slo := "-∞"; if rn.lo > KeyMin { slo = fmt.Sprintf("%v", rn.lo) }
shi := "∞"; if rn.hi_ < KeyMax { shi = fmt.Sprintf("%v", rn.hi_+1) }
done := " "; if rn.done { done = "*" } done := " "; if rn.done { done = "*" }
return fmt.Sprintf("%s[%s,%s)%s", done, slo, shi, vnode(rn.node)) hi := rn.hi_
if hi < KeyMax {
hi += 1
}
return fmt.Sprintf("%s[%s,%s)%s", done, kstr(rn.lo), kstr(hi), vnode(rn.node))
} }
// push pushes element to node stack. // push pushes element to node stack.
......
...@@ -68,3 +68,15 @@ func nodePathToPath(nodePath []Node) (path []zodb.Oid) { ...@@ -68,3 +68,15 @@ func nodePathToPath(nodePath []Node) (path []zodb.Oid) {
} }
return path return path
} }
// kstr formats key as string.
func kstr(k Key) string {
if k == KeyMin {
return "-∞"
}
if k == KeyMax {
return "∞"
}
return fmt.Sprintf("%d", k)
}
...@@ -101,11 +101,6 @@ type ΔBtail struct { ...@@ -101,11 +101,6 @@ type ΔBtail struct {
// set of node that were requested to be tracked, but for which vδB was not yet rebuilt // set of node that were requested to be tracked, but for which vδB was not yet rebuilt
trackNew PPTreeSubSet trackNew PPTreeSubSet
// XXX root -> tracked holes
// XXX move -> ΔTtail ?
holeIdxByRoot map[zodb.Oid]treeSetKey
} }
...@@ -149,7 +144,6 @@ func NewΔBtail(at0 zodb.Tid, db *zodb.DB) *ΔBtail { ...@@ -149,7 +144,6 @@ func NewΔBtail(at0 zodb.Tid, db *zodb.DB) *ΔBtail {
byRoot: map[zodb.Oid]*ΔTtail{}, byRoot: map[zodb.Oid]*ΔTtail{},
trackSet: PPTreeSubSet{}, trackSet: PPTreeSubSet{},
trackNew: PPTreeSubSet{}, trackNew: PPTreeSubSet{},
holeIdxByRoot: map[zodb.Oid]treeSetKey{},
db: db, db: db,
} }
} }
...@@ -184,14 +178,6 @@ func (orig *ΔBtail) clone() *ΔBtail { ...@@ -184,14 +178,6 @@ func (orig *ΔBtail) clone() *ΔBtail {
klon.byRoot[root] = klonΔTtail klon.byRoot[root] = klonΔTtail
} }
// holeIdxByRoot
klon.holeIdxByRoot = make(map[zodb.Oid]treeSetKey, len(orig.holeIdxByRoot))
for root, origHoleIdx := range orig.holeIdxByRoot {
klonHoleIdx := treeSetKey{make(SetKey, len(origHoleIdx.SetKey))}
klonHoleIdx.Update(origHoleIdx.SetKey)
klon.holeIdxByRoot[root] = klonHoleIdx
}
return klon return klon
} }
...@@ -224,6 +210,7 @@ func (δBtail *ΔBtail) Tail() zodb.Tid { return δBtail.δZtail.Tail() } ...@@ -224,6 +210,7 @@ func (δBtail *ΔBtail) Tail() zodb.Tid { return δBtail.δZtail.Tail() }
// XXX path -> []oid ? // XXX path -> []oid ?
// //
// XXX catch cycles on add? // XXX catch cycles on add?
// XXX no need to pass keyPresent since holeIdx was removed
func (δBtail *ΔBtail) Track(key Key, keyPresent bool, nodePath []Node) error { // XXX Tree|Bucket; path[0] = root func (δBtail *ΔBtail) Track(key Key, keyPresent bool, nodePath []Node) error { // XXX Tree|Bucket; path[0] = root
path := nodePathToPath(nodePath) path := nodePathToPath(nodePath)
...@@ -241,21 +228,6 @@ func (δBtail *ΔBtail) track(key Key, keyPresent bool, path []zodb.Oid) error { ...@@ -241,21 +228,6 @@ func (δBtail *ΔBtail) track(key Key, keyPresent bool, path []zodb.Oid) error {
// track is track of path[-1] (i.e. leaf) // track is track of path[-1] (i.e. leaf)
// remember missing keys in track of leaf node (bucket or top-level ø tree)
holeIdx := δBtail.holeIdxFor(root)
if !keyPresent {
holeIdx.Add(key)
//track.holes.Add(key)
} else {
/*
if track.holes.Has(key) {
panicf("[%v] was previously requested to be tracked as ø", key)
}
*/
if holeIdx.Has(key) {
panicf("[%v] was previously requested to be tracked as ø", key)
}
}
// XXX hack - until rebuild is implemented // XXX hack - until rebuild is implemented
if XXX_killWhenRebuildWorks { if XXX_killWhenRebuildWorks {
...@@ -270,15 +242,6 @@ if XXX_killWhenRebuildWorks { ...@@ -270,15 +242,6 @@ if XXX_killWhenRebuildWorks {
return nil return nil
} }
func (δBtail *ΔBtail) holeIdxFor(root zodb.Oid) treeSetKey {
holeIdx, ok := δBtail.holeIdxByRoot[root]
if !ok {
holeIdx = treeSetKey{SetKey{}}
δBtail.holeIdxByRoot[root] = holeIdx
}
return holeIdx
}
// rebuild rebuilds ΔBtail taking trackNew requests into account. // rebuild rebuilds ΔBtail taking trackNew requests into account.
// XXX place // XXX place
func (δBtail *ΔBtail) rebuild() (err error) { func (δBtail *ΔBtail) rebuild() (err error) {
...@@ -332,9 +295,8 @@ func (δBtail *ΔBtail) rebuild() (err error) { ...@@ -332,9 +295,8 @@ func (δBtail *ΔBtail) rebuild() (err error) {
} }
for root, δtops := range δtopsByRoot { for root, δtops := range δtopsByRoot {
holeIdx := treeSetKey{SetKey{}} // XXX stub
// diff backwards curr -> prev // diff backwards curr -> prev
δT, δtrack, err := treediff(ctx, root, δtops, δZTC, trackNew, holeIdx, zconnCurr, zconnPrev) δT, δtrack, err := treediff(ctx, root, δtops, δZTC, trackNew, zconnCurr, zconnPrev)
if err != nil { if err != nil {
return err return err
} }
...@@ -428,7 +390,6 @@ if XXX_killWhenRebuildWorks { ...@@ -428,7 +390,6 @@ if XXX_killWhenRebuildWorks {
tracef("Update @%s -> @%s\n", δBtail.Head(), δZ.Tid) tracef("Update @%s -> @%s\n", δBtail.Head(), δZ.Tid)
tracef("δZ:\t%v\n", δZ.Changev) tracef("δZ:\t%v\n", δZ.Changev)
tracef("trackSet: %v\n", δBtail.trackSet) tracef("trackSet: %v\n", δBtail.trackSet)
tracef("holeIdxByRoot: %v\n", δBtail.holeIdxByRoot)
// XXX dup wrt rebuild? // XXX dup wrt rebuild?
...@@ -457,8 +418,7 @@ if XXX_killWhenRebuildWorks { ...@@ -457,8 +418,7 @@ if XXX_killWhenRebuildWorks {
} }
for root, δtops := range δtopsByRoot { for root, δtops := range δtopsByRoot {
holeIdx := δBtail.holeIdxByRoot[root] δT, δtrack, err := treediff(ctx, root, δtops, δZTC, δBtail.trackSet, zconnOld, zconnNew)
δT, δtrack, err := treediff(ctx, root, δtops, δZTC, δBtail.trackSet, holeIdx, zconnOld, zconnNew)
if err != nil { if err != nil {
return ΔB{}, err return ΔB{}, err
} }
......
...@@ -47,9 +47,6 @@ import ( ...@@ -47,9 +47,6 @@ import (
"lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xzodb" "lab.nexedi.com/nexedi/wendelin.core/wcfs/internal/xzodb"
) )
const kInf Key = 10000 // inf key (TeX hack)
//const kInf Key = KeyMax
// XXX move infrastructure -> δbtail_treegen_test.go ? // XXX move infrastructure -> δbtail_treegen_test.go ?
// TreeGenSrv represents connection to running `treegen ...` server. // TreeGenSrv represents connection to running `treegen ...` server.
...@@ -263,7 +260,7 @@ type RTree struct { ...@@ -263,7 +260,7 @@ type RTree struct {
type RBucket struct { type RBucket struct {
oid zodb.Oid oid zodb.Oid
parent *RTree parent *RTree
lo, hi_ Key lo, hi_ Key // XXX -> 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.
} }
...@@ -303,18 +300,6 @@ func (rbs RBucketSet) coverage() string { ...@@ -303,18 +300,6 @@ func (rbs RBucketSet) coverage() string {
return s return s
} }
// holeIdx returns what should be ΔBtree.holeIdx for specified tracked key set.
func (rbs RBucketSet) holeIdx(tracked SetKey) SetKey {
holes := SetKey{}
for k := range tracked {
_, keyin := rbs.Get(k).kv[k]
if !keyin {
holes.Add(k)
}
}
return holes
}
// trackSet returns what should be ΔBtree.trackSet for specified tracked key set. // trackSet returns what should be ΔBtree.trackSet for specified tracked key set.
func (rbs RBucketSet) trackSet(tracked SetKey) PPTreeSubSet { func (rbs RBucketSet) trackSet(tracked SetKey) PPTreeSubSet {
trackSet := PPTreeSubSet{} trackSet := PPTreeSubSet{}
...@@ -385,7 +370,7 @@ func XGetTree(db *zodb.DB, at zodb.Tid, root zodb.Oid) RBucketSet { ...@@ -385,7 +370,7 @@ func XGetTree(db *zodb.DB, at zodb.Tid, root zodb.Oid) RBucketSet {
} }
rbucketv := RBucketSet{} rbucketv := RBucketSet{}
xwalkDFS(ctx, -kInf, kInf, ztree, func(rb *RBucket) { xwalkDFS(ctx, KeyMin, KeyMax, ztree, func(rb *RBucket) {
rbucketv = append(rbucketv, rb) rbucketv = append(rbucketv, rb)
}) })
if len(rbucketv) == 0 { // empty tree -> [-∞, ∞){} if len(rbucketv) == 0 { // empty tree -> [-∞, ∞){}
...@@ -396,8 +381,8 @@ func XGetTree(db *zodb.DB, at zodb.Tid, root zodb.Oid) RBucketSet { ...@@ -396,8 +381,8 @@ func XGetTree(db *zodb.DB, at zodb.Tid, root zodb.Oid) RBucketSet {
ebucket := &RBucket{ ebucket := &RBucket{
oid: zodb.InvalidOid, oid: zodb.InvalidOid,
parent: etree, parent: etree,
lo: -kInf, lo: KeyMin,
hi_: kInf, hi_: KeyMax,
kv: map[Key]string{}, kv: map[Key]string{},
} }
rbucketv = RBucketSet{ebucket} rbucketv = RBucketSet{ebucket}
...@@ -489,15 +474,15 @@ func XGetδKV(db *zodb.DB, at1, at2 zodb.Tid, δkvOid map[Key]ΔValue) map[Key] ...@@ -489,15 +474,15 @@ func XGetδKV(db *zodb.DB, at1, at2 zodb.Tid, δkvOid map[Key]ΔValue) map[Key]
// Av, Bv - topologies with values ex T/B1:a T2/B1:b-B3:c // Av, Bv - topologies with values ex T/B1:a T2/B1:b-B3:c
// //
// δ(Av, Bv) - full diff {k -> v} for changed keys; DEL = k -> ø // δ(Av, Bv) - full diff {k -> v} for changed keys; DEL = k -> ø
// ex δ(T/B1:a, T2/B1:b-B3:c) = {1:b 3:c} XXX include "was" value too? // ex δ(T/B1:a, T2/B1:b-B3:c) = {-1:a +1:b +3:c}
// //
// Δ(T, Av, Bv) - subset of δ(Av, Bv) corresponding to initial tracking set T // Δ(T, Av, Bv) - subset of δ(Av, Bv) corresponding to initial tracking set T
// ex Δ({1}, T/B1:a, T2/B1:b-B3:c) = {1:b} (no 3:c) // ex Δ({1}, T/B1:a, T2/B1:b-B3:c) = {-1:a +1:b} (no +3:c) XXX fix example
// //
// kadj(A,B) {} k -> {k'}: - adjacency matrix // kadj(A,B) {} k -> {k'}: - adjacency matrix
// ∃v1,v2: k'∈ Δ({k}, Av1, Bv2) // ∃v1,v2: k'∈ Δ({k}, Av1, Bv2)
// //
// ex kadj(T/B1, T2/B1-B3) = {1:{1} 3:{1,3} ∞:{1,3}} k ∈ A+B+{∞} // ex kadj(T/B1, T2/B1-B3) = {1:{1} 3:{1,3} ∞:{1,3}} k ∈ A+B+{∞} XXX adjust to new kadj
// + {0:{1} 2:{1,3} + ... all possible keys} // + {0:{1} 2:{1,3} + ... all possible keys}
// //
// Δ(T, Av, Bv) = δ(Av, Bv)/kadj(A,B)[T] // Δ(T, Av, Bv) = δ(Av, Bv)/kadj(A,B)[T]
...@@ -505,6 +490,8 @@ func XGetδKV(db *zodb.DB, at1, at2 zodb.Tid, δkvOid map[Key]ΔValue) map[Key] ...@@ -505,6 +490,8 @@ func XGetδKV(db *zodb.DB, at1, at2 zodb.Tid, δkvOid map[Key]ΔValue) map[Key]
// i.e. = δ(Av, Bv) for k: k ∈ U kadj(A,B)[·] // i.e. = δ(Av, Bv) for k: k ∈ U kadj(A,B)[·]
// ·∈T // ·∈T
// //
// XXX fix definition for "and changed"
//
// XXX adjacency matrix is symmetric: XXX KAdj verifies this at runtime // XXX adjacency matrix is symmetric: XXX KAdj verifies this at runtime
// //
// kadj(A,B) == kadj(B,A) // kadj(A,B) == kadj(B,A)
...@@ -534,12 +521,19 @@ func KAdj(t1, t2 *tTreeCommit, keysv ...SetKey) (kadj KAdjMatrix) { ...@@ -534,12 +521,19 @@ func KAdj(t1, t2 *tTreeCommit, keysv ...SetKey) (kadj KAdjMatrix) {
kadj12 := _KAdj(t1,t2, keysv...) kadj12 := _KAdj(t1,t2, keysv...)
kadj21 := _KAdj(t2,t1, keysv...) kadj21 := _KAdj(t2,t1, keysv...)
if !reflect.DeepEqual(kadj12, kadj21) { if !reflect.DeepEqual(kadj12, kadj21) {
panicf("KAdj not symmetric:\nt1: %s\bt2: %s\nkadj12: %v\nkadj21: %v", panicf("KAdj not symmetric:\nt1: %s\nt2: %s\nkadj12: %v\nkadj21: %v",
t1.tree, t2.tree, kadj12, kadj21) t1.tree, t2.tree, kadj12, kadj21)
} }
return kadj12 return kadj12
} }
const debugKAdj = false
func debugfKAdj(format string, argv ...interface{}) {
if debugKAdj {
fmt.Printf(format, argv...)
}
}
func _KAdj(t1, t2 *tTreeCommit, keysv ...SetKey) (kadj KAdjMatrix) { func _KAdj(t1, t2 *tTreeCommit, keysv ...SetKey) (kadj KAdjMatrix) {
var keys SetKey var keys SetKey
switch len(keysv) { switch len(keysv) {
...@@ -551,51 +545,90 @@ func _KAdj(t1, t2 *tTreeCommit, keysv ...SetKey) (kadj KAdjMatrix) { ...@@ -551,51 +545,90 @@ func _KAdj(t1, t2 *tTreeCommit, keysv ...SetKey) (kadj KAdjMatrix) {
panic("multiple key sets on the call") panic("multiple key sets on the call")
} }
debugfKAdj("\n\n_KAdj\n")
debugfKAdj("t1: %s\n", t1.tree)
debugfKAdj("t2: %s\n", t2.tree)
debugfKAdj("keys: %s\n", keys)
defer func() {
debugfKAdj("kadj -> %v\n", kadj)
}()
// kadj = {} k -> adjacent keys. // kadj = {} k -> adjacent keys.
// if k is tracked -> changes to adjacents must be in Update(t1->t2). // if k is tracked and covered by changed leaf -> changes to adjacents must be in Update(t1->t2).
kadj = KAdjMatrix{} kadj = KAdjMatrix{}
for k := range keys { for k := range keys {
adj1 := SetKey{} adj1 := SetKey{}
adj2 := SetKey{} adj2 := SetKey{}
q1 := []Key{k} q1 := &RangedKeySet{}; q1.Add(k)
q2 := []Key{k} q2 := &RangedKeySet{}; q2.Add(k)
done1 := &RangedKeySet{}
//tracef("\n") done2 := &RangedKeySet{}
for len(q1) > 0 || len(q2) > 0 {
//tracef("q1: %v\n", q1) debugfKAdj("\nk%s\n", kstr(k))
//tracef("q2: %v\n", q2) for !q1.Empty() || !q2.Empty() {
if l1 := len(q1); l1 > 0 { debugfKAdj("q1: %s\tdone1: %s\n", q1, done1)
k1 := q1[l1-1]; q1 = q1[:l1-1] debugfKAdj("q2: %s\tdone2: %s\n", q2, done2)
if !adj1.Has(k1) { for _, r1 := range q1.AllRanges() {
for k_ := range t1.xkv.Get(k1).kv { lo1 := r1.lo
adj1.Add(k_) for {
for k__ := range t2.xkv.Get(k_).kv { b1 := t1.xkv.Get(lo1)
if !adj2.Has(k__) { debugfKAdj(" b1: %s\n", b1)
q2 = append(q2, k__) for k_ := range keys {
} if b1.lo <= k_ && k_ <= b1.hi_ {
adj1.Add(k_)
debugfKAdj(" adj1 += %s\t-> %s\n", kstr(k_), adj1)
} }
} }
b1r := KeyRange{b1.lo, b1.hi_}
done1.AddRange(b1r)
// q2 |= (b1.keyrange \ done2)
δq2 := &RangedKeySet{}
δq2.AddRange(b1r)
δq2.DifferenceInplace(done2)
q2.UnionInplace(δq2)
debugfKAdj("q2 += %s\t-> %s\n", δq2, q2)
// continue with next right bucket until r1 coverage is complete
if r1.hi_ <= b1.hi_ {
break
}
lo1 = b1.hi_ + 1
} }
} }
q1.Clear()
if l2 := len(q2); l2 > 0 {
k2 := q2[l2-1]; q2 = q2[:l2-1] for _, r2 := range q2.AllRanges() {
if !adj2.Has(k2) { lo2 := r2.lo
for k_ := range t2.xkv.Get(k2).kv { for {
adj2.Add(k_) b2 := t2.xkv.Get(lo2)
for k__ := range t1.xkv.Get(k_).kv { debugfKAdj(" b2: %s\n", b2)
if !adj1.Has(k__) { for k_ := range keys {
q1 = append(q1, k__) if b2.lo <= k_ && k_ <= b2.hi_ {
} adj2.Add(k_)
debugfKAdj(" adj2 += %s\t-> %s\n", kstr(k_), adj2)
} }
} }
b2r := KeyRange{b2.lo, b2.hi_}
done2.AddRange(b2r)
// q1 |= (b2.keyrange \ done1)
δq1 := &RangedKeySet{}
δq1.AddRange(b2r)
δq1.DifferenceInplace(done1)
q1.UnionInplace(δq1)
debugfKAdj("q1 += %s\t-> %s\n", δq1, q1)
// continue with next right bucket until r2 coverage is complete
if r2.hi_ <= b2.hi_ {
break
}
lo2 = b2.hi_ + 1
} }
} }
q2.Clear()
} }
adj := SetKey{}; adj.Add(k); adj.Update(adj1); adj.Update(adj2) adj := SetKey{}; adj.Update(adj1); adj.Update(adj2)
kadj[k] = adj kadj[k] = adj
} }
...@@ -635,8 +668,13 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod ...@@ -635,8 +668,13 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod
X := exc.Raiseif X := exc.Raiseif
tracef("\n>>> Track=%s\n", initialTrackedKeys) tracef("\n>>> Track=%s\n", initialTrackedKeys)
var kadjTracked SetKey = nil var TrackedδZ SetKey = nil
var kadjTrackedδZ SetKey = nil
var δT, δTok map[Key]Δstring = nil, nil var δT, δTok map[Key]Δstring = nil, nil
δZset := SetOid{}
for _, oid := range δZ.Changev {
δZset.Add(oid)
}
// badf queues error message to be reported on return. // badf queues error message to be reported on return.
var badv []string var badv []string
...@@ -647,7 +685,10 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod ...@@ -647,7 +685,10 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod
if badv != nil { if badv != nil {
emsg := fmt.Sprintf("%s ; tracked=%v :\n\n", subj, initialTrackedKeys) emsg := fmt.Sprintf("%s ; tracked=%v :\n\n", subj, initialTrackedKeys)
emsg += fmt.Sprintf("d12: %v\nδTok: %v\nδT: %v\n\n", d12, δTok, δT) emsg += fmt.Sprintf("d12: %v\nδTok: %v\nδT: %v\n\n", d12, δTok, δT)
emsg += fmt.Sprintf("kadj[Tracked]: %v\nkadj: %v\n\n", kadjTracked, kadj) emsg += fmt.Sprintf("δZ: %v\n", δZset)
emsg += fmt.Sprintf("Tracked^δZ: %v\n", TrackedδZ)
emsg += fmt.Sprintf("kadj[Tracked^δZ]: %v\n", kadjTrackedδZ)
emsg += fmt.Sprintf("kadj: %v\n\n", kadj)
emsg += strings.Join(badv, "\n") emsg += strings.Join(badv, "\n")
emsg += "\n" emsg += "\n"
...@@ -669,7 +710,6 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod ...@@ -669,7 +710,6 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod
ztree = xtree.(*Tree) ztree = xtree.(*Tree)
} }
kadjTracked = SetKey{} // kadj[Tracked] (all keys adjacent to tracked keys)
for k := range initialTrackedKeys { for k := range initialTrackedKeys {
if ztree != nil { if ztree != nil {
_, ok, path, err := ZTreeGetBlkData(ctx, ztree, k); X(err) _, ok, path, err := ZTreeGetBlkData(ctx, ztree, k); X(err)
...@@ -686,42 +726,56 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod ...@@ -686,42 +726,56 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod
// T1->ø, we test what will happen on ø->T2. // T1->ø, we test what will happen on ø->T2.
err = δbtail.track(k, false, []zodb.Oid{treeRoot}); X(err) err = δbtail.track(k, false, []zodb.Oid{treeRoot}); X(err)
} }
kadjTracked.Update(kadj[k])
} }
// assert Tracked ∈ kadj[Tracked] except ∞ XXX -> initialTrackedKeys.Difference(kadjTracked) TrackedδZ = SetKey{} // Tracked ^ δZ
trackedNotInKadj := SetKey{}
for k := range initialTrackedKeys { for k := range initialTrackedKeys {
if !kadjTracked.Has(k) { leaf1 := xkv1.Get(k)
trackedNotInKadj.Add(k) oid1 := leaf1.oid
if oid1 == zodb.InvalidOid { // embedded bucket
oid1 = leaf1.parent.oid
}
leaf2 := xkv2.Get(k)
oid2 := leaf2.oid
if oid2 == zodb.InvalidOid { // embedded bucket
oid2 = leaf2.parent.oid
}
if δZset.Has(oid1) || δZset.Has(oid2) {
TrackedδZ.Add(k)
} }
} }
trackedNotInKadj.Del(kInf)
if len(trackedNotInKadj) > 0 { kadjTrackedδZ = SetKey{} // kadj[Tracked^δZ] (all keys adjacent to tracked^δZ)
badf("BUG: Tracked ∉ kadj[Tracked] ; extra=%v", trackedNotInKadj) for k := range TrackedδZ {
kadjTrackedδZ.Update(kadj[k])
}
// XXX why "except ∞" ?
// assert TrackedδZ ∈ kadj[TrackedδZ] except ∞ XXX -> initialTrackedKeys.Difference(kadjTrackedδZ)
trackNotInKadj := SetKey{}
for k := range TrackedδZ {
if !kadjTrackedδZ.Has(k) {
trackNotInKadj.Add(k)
}
}
trackNotInKadj.Del(KeyMax)
if len(trackNotInKadj) > 0 {
badf("BUG: Tracked^δZ ∉ kadj[Tracked^δZ] ; extra=%v", trackNotInKadj)
return return
} }
// k ∈ d12 // k ∈ d12
// k ∈ δT <=> // k ∈ δT <=>
// k ∈ U kadj[·] // k ∈ U kadj[·]
// ·∈tracking // ·∈tracking^δZ
δTok = map[Key]Δstring{} // d12[all keys that should be present in δT] δTok = map[Key]Δstring{} // d12[all keys that should be present in δT]
for k,δv := range d12 { for k,δv := range d12 {
if kadjTracked.Has(k) { if kadjTrackedδZ.Has(k) {
δTok[k] = δv δTok[k] = δv
} }
} }
// verify δbtail.holeIdx against @at1
// holes1 = tracked1 \ kv1
holes1 := xkv1.holeIdx(initialTrackedKeys)
holeIdx := δbtail.holeIdxFor(treeRoot)
if !reflect.DeepEqual(holes1, holeIdx.SetKey) {
badf("δbtail.holeIdx1 wrong ; holeIdx=%v holeIdxOK=%v", holeIdx, holes1)
}
ø := PPTreeSubSet{} ø := PPTreeSubSet{}
// verify δbtail.trackSet against @at1 // verify δbtail.trackSet against @at1
...@@ -746,16 +800,9 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod ...@@ -746,16 +800,9 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod
return return
} }
// verify δbtail.holeIdx against @at2
// holes2 = tracked2 \ kv2 ( = kadj[tracked1] \ kv2)
holes2 := xkv2.holeIdx(kadjTracked)
if !reflect.DeepEqual(holes2, holeIdx.SetKey) {
badf("δbtail.holeIdx2 wrong ; holeIdx=%v holeIdxOK=%v", holeIdx, holes2)
}
// verify δbtail.trackSet against @at2 // verify δbtail.trackSet against @at2
// trackSet2 = xkv2[tracked2] ( = xkv2[kadj[tracked1]] // trackSet2 = xkv2[tracked2] ( = xkv2[kadj[tracked1]]
trackSet2 := xkv2.trackSet(kadjTracked) trackSet2 := xkv2.trackSet(initialTrackedKeys.Union(kadjTrackedδZ))
// if !reflect.DeepEqual(trackSet2, δbtail.trackSet) { // if !reflect.DeepEqual(trackSet2, δbtail.trackSet) {
// badf("δbtail.trackSet2 wrong:\n\thave: %v\n\twant: %v", δbtail.trackSet, trackSet2) // badf("δbtail.trackSet2 wrong:\n\thave: %v\n\twant: %v", δbtail.trackSet, trackSet2)
// } // }
...@@ -841,8 +888,6 @@ func (δbtail *ΔBtail) assertTrack(t *testing.T, subj string, trackSetOK, track ...@@ -841,8 +888,6 @@ func (δbtail *ΔBtail) assertTrack(t *testing.T, subj string, trackSetOK, track
t.Helper() t.Helper()
assertTrack(t, subj + ": trackSet", δbtail.trackSet, trackSetOK) assertTrack(t, subj + ": trackSet", δbtail.trackSet, trackSetOK)
assertTrack(t, subj + ": trackNew", δbtail.trackNew, trackNewOK) assertTrack(t, subj + ": trackNew", δbtail.trackNew, trackNewOK)
// XXX also verify .holeIdx XXX or better get rid of .holeIdx
} }
// xverifyΔBTail_rebuild verifies δBtail.rebuild during t0->t1->t2 transition. // xverifyΔBTail_rebuild verifies δBtail.rebuild during t0->t1->t2 transition.
...@@ -1341,8 +1386,8 @@ func TestΔBTail(t *testing.T) { ...@@ -1341,8 +1386,8 @@ func TestΔBTail(t *testing.T) {
for _, k := range keyv { ks.Add(k) } for _, k := range keyv { ks.Add(k) }
return ks return ks
} }
// oo is shorthand for kInf // oo is shorthand for KeyMax
const oo = kInf const oo = KeyMax
// A is shorthand for KAdjMatrix // A is shorthand for KAdjMatrix
type A = KAdjMatrix type A = KAdjMatrix
// Δ is shorthand for ΔBTestEntry // Δ is shorthand for ΔBTestEntry
...@@ -1362,56 +1407,56 @@ func TestΔBTail(t *testing.T) { ...@@ -1362,56 +1407,56 @@ func TestΔBTail(t *testing.T) {
// +1 // +1
Δ("T/B1:a", Δ("T/B1:a",
A{1: K(1), A{1: K(1,oo),
oo: K(1,oo)}), oo: K(1,oo)}),
// +2 // +2
Δ("T/B1:a,2:b", Δ("T/B1:a,2:b",
A{1: K(1,2), A{1: K(1,2,oo),
2: K(1,2), 2: K(1,2,oo),
oo: K(1,2,oo)}), oo: K(1,2,oo)}),
// -1 // -1
Δ("T/B2:b", Δ("T/B2:b",
A{1: K(1,2), A{1: K(1,2,oo),
2: K(1,2), 2: K(1,2,oo),
oo: K(1,2,oo)}), oo: K(1,2,oo)}),
// 2: b->c // 2: b->c
Δ("T/B2:c", Δ("T/B2:c",
A{2: K(2), A{2: K(2,oo),
oo: K(2,oo)}), oo: K(2,oo)}),
// +1 in new bucket (to the left) // +1 in new bucket (to the left)
Δ("T2/B1:a-B2:c", Δ("T2/B1:a-B2:c",
A{1: K(1,2), A{1: K(1,2,oo),
2: K(2), 2: K(1,2,oo),
oo: K(2,oo)}), // NOTE no ∞ -> 1, because 1 is added to the left oo: K(1,2,oo)}),
// +3 in new bucket (to the right) // +3 in new bucket (to the right)
Δ("T2,3/B1:a-B2:c-B3:c", Δ("T2,3/B1:a-B2:c-B3:c",
A{1: K(1), A{1: K(1),
2: K(2), 2: K(2,3,oo),
3: K(2,3), 3: K(2,3,oo),
oo: K(2,3,oo)}), oo: K(2,3,oo)}),
// bucket split; +3 in new bucket // bucket split; +3 in new bucket
"T/B1:a,2:b", "T/B1:a,2:b",
Δ("T2/B1:a-B2:b,3:c", Δ("T2/B1:a-B2:b,3:c",
A{1: K(1,2,3), A{1: K(1,2,3,oo),
2: K(1,2,3), 2: K(1,2,3,oo),
3: K(1,2,3), 3: K(1,2,3,oo),
oo: K(1,2,3,oo)}), oo: K(1,2,3,oo)}),
// bucket split; +3 in new bucket; +4 +5 in another new bucket // bucket split; +3 in new bucket; +4 +5 in another new bucket
// which remain not tracked unless previously accessed. // everything becomes tracked because original bucket had [-∞,∞) coverage
"T/B1:a,2:b", "T/B1:a,2:b",
Δ("T2,4/B1:a-B2:b,3:c-B4:d,5:e", Δ("T2,4/B1:a-B2:b,3:c-B4:d,5:e",
A{1: K(1,2,3), A{1: K(1,2,3,4,5,oo),
2: K(1,2,3), 2: K(1,2,3,4,5,oo),
3: K(1,2,3), 3: K(1,2,3,4,5,oo),
4: K(1,2,3,4,5), 4: K(1,2,3,4,5,oo),
5: K(1,2,3,4,5), 5: K(1,2,3,4,5,oo),
oo: K(1,2,3,4,5,oo)}), oo: K(1,2,3,4,5,oo)}),
// reflow of keys: even if tracked={1}, changes to all B nodes need to be rescanned: // reflow of keys: even if tracked={1}, changes to all B nodes need to be rescanned:
...@@ -1419,13 +1464,13 @@ func TestΔBTail(t *testing.T) { ...@@ -1419,13 +1464,13 @@ func TestΔBTail(t *testing.T) {
// forces to look into +B34 and so on. // forces to look into +B34 and so on.
"T2,4,6/B1:a-B2:b,3:c-B4:d,5:e-B6:f,7:g", "T2,4,6/B1:a-B2:b,3:c-B4:d,5:e-B6:f,7:g",
Δ("T3,5,7/B1:g,2:f-B3:e,4:d-B5:c,6:b-B7:a", Δ("T3,5,7/B1:g,2:f-B3:e,4:d-B5:c,6:b-B7:a",
A{1: K(1,2,3,4,5,6,7), A{1: K(1,2,3,4,5,6,7,oo),
2: K(1,2,3,4,5,6,7), 2: K(1,2,3,4,5,6,7,oo),
3: K(1,2,3,4,5,6,7), 3: K(1,2,3,4,5,6,7,oo),
4: K(1,2,3,4,5,6,7), 4: K(1,2,3,4,5,6,7,oo),
5: K(1,2,3,4,5,6,7), 5: K(1,2,3,4,5,6,7,oo),
6: K(1,2,3,4,5,6,7), 6: K(1,2,3,4,5,6,7,oo),
7: K(1,2,3,4,5,6,7), 7: K(1,2,3,4,5,6,7,oo),
oo: K(1,2,3,4,5,6,7,oo)}), oo: K(1,2,3,4,5,6,7,oo)}),
// reflow of keys for rebuild: even if tracked1={}, tracked2={1}, changes to // reflow of keys for rebuild: even if tracked1={}, tracked2={1}, changes to
...@@ -1440,44 +1485,57 @@ func TestΔBTail(t *testing.T) { ...@@ -1440,44 +1485,57 @@ func TestΔBTail(t *testing.T) {
"T2,5,8/B1:a-B2:b,3:c-B5:e,6:f-B8:h,9:i", "T2,5,8/B1:a-B2:b,3:c-B5:e,6:f-B8:h,9:i",
// depth=2; bucket split; +3 in new bucket; left T remain // depth=2; bucket split; +3 in new bucket; left T remain
// _unchanged_ even though B under it is modified; right arm is // _unchanged_ even though B under it is modified.
// added to tracking set because 2 migrates there.
"T/T/B1:a,2:b", "T/T/B1:a,2:b",
Δ("T2/T-T/B1:a-B2:b,3:c", Δ("T2/T-T/B1:a-B2:b,3:c",
A{1: K(1,2,3), A{1: K(1,2,3,oo),
2: K(1,2,3), 2: K(1,2,3,oo),
3: K(1,2,3), 3: K(1,2,3,oo),
oo: K(1,2,3,oo)}), oo: K(1,2,3,oo)}),
// depth=2; like prev. case, but additional right arm with +4 // depth=2; like prev. case, but additional right arm with +4 +5 is added.
// +5 is added which remain not tracked unless previously accessed.
"T/T/B1:a,2:b", "T/T/B1:a,2:b",
Δ("T2,4/T-T-T/B1:a-B2:b,3:c-B4:d,5:e", Δ("T2,4/T-T-T/B1:a-B2:b,3:c-B4:d,5:e",
A{1: K(1,2,3), A{1: K(1,2,3,4,5,oo),
2: K(1,2,3), 2: K(1,2,3,4,5,oo),
3: K(1,2,3), 3: K(1,2,3,4,5,oo),
4: K(1,2,3,4,5), 4: K(1,2,3,4,5,oo),
5: K(1,2,3,4,5), 5: K(1,2,3,4,5,oo),
oo: K(1,2,3,4,5,oo)}), oo: K(1,2,3,4,5,oo)}),
// depth=2; bucket split; +3 in new bucket; t0 and t1 split; // depth=2; bucket split; +3 in new bucket; t0 and t1 split;
// +right arm (T7/B45-B89) which remain not tracked unless previously accessed. // +right arm (T7/B45-B89).
"T/T/B1:a,2:b", "T/T/B1:a,2:b",
Δ("T4/T2-T7/B1:a-B2:b,3:c-B4:d,5:e-B8:h,9:i", Δ("T4/T2-T7/B1:a-B2:b,3:c-B4:d,5:e-B8:h,9:i",
A{1: K(1,2,3), A{1: K(1,2,3,4,5,8,9,oo),
2: K(1,2,3), 2: K(1,2,3,4,5,8,9,oo),
3: K(1,2,3), 3: K(1,2,3,4,5,8,9,oo),
4: K(1,2,3,4,5), 4: K(1,2,3,4,5,8,9,oo),
5: K(1,2,3,4,5), 5: K(1,2,3,4,5,8,9,oo),
8: K(1,2,3, 8,9), 8: K(1,2,3,4,5,8,9,oo),
9: K(1,2,3, 8,9), 9: K(1,2,3,4,5,8,9,oo),
oo: K(1,2,3, 8,9,oo)}), oo: K(1,2,3,4,5,8,9,oo)}),
// 2 reflow to right B neighbour; 8 split into new B; δ=ø // 2 reflow to right B neighbour; 8 split into new B; δ=ø
"T3/B1:a,2:b-B4:d,8:h", "T3/B1:a,2:b-B4:d,8:h",
"T2,5/B1:a-B2:b,4:d-B8:h", // XXX add A "T2,5/B1:a-B2:b,4:d-B8:h", // XXX add A
// case where kadj does not grow too much as leafs coverage remains stable
"T4,8/B1:a,2:b-B5:d,6:e-B10:g,11:h",
Δ("T4,8/B2:b,3:c-B6:e,7:f-B11:h,12:i",
A{1: K(1,2,3),
2: K(1,2,3),
3: K(1,2,3),
5: K(5,6,7),
6: K(5,6,7),
7: K(5,6,7,),
10: K(10,11,12,oo),
11: K(10,11,12,oo),
12: K(10,11,12,oo),
oo: K(10,11,12,oo)}),
// tree deletion // tree deletion
// having ø in the middle of the test cases exercises all: // having ø in the middle of the test cases exercises all:
// * `ø -> Tree ...` (tree is created anew), // * `ø -> Tree ...` (tree is created anew),
...@@ -1565,8 +1623,8 @@ func TestΔBTail(t *testing.T) { ...@@ -1565,8 +1623,8 @@ func TestΔBTail(t *testing.T) {
"T2/B1:a-B3:c", "T2/B1:a-B3:c",
Δ("T2/T-T4/B1:b-B3:d-B99:h", Δ("T2/T-T4/B1:b-B3:d-B99:h",
A{1: K(1), A{1: K(1),
3: K(3), 3: K(3,99,oo),
99: K(3,99), 99: K(3,99,oo),
oo: K(3,99,oo)}), oo: K(3,99,oo)}),
// XXX -------- // XXX --------
...@@ -1824,7 +1882,7 @@ func (xkv RBucketSet) Flatten() map[Key]string { ...@@ -1824,7 +1882,7 @@ func (xkv RBucketSet) Flatten() map[Key]string {
// allTestKeys returns all keys from vt + ∞. // allTestKeys returns all keys from vt + ∞.
func allTestKeys(vt ...*tTreeCommit) SetKey { func allTestKeys(vt ...*tTreeCommit) SetKey {
allKeys := SetKey{}; allKeys.Add(kInf) // ∞ 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 {
for k := range b.kv { for k := range b.kv {
...@@ -1881,9 +1939,11 @@ func sortedKeys(kv map[Key]Δstring) []Key { ...@@ -1881,9 +1939,11 @@ func sortedKeys(kv map[Key]Δstring) []Key {
func (b *RBucket) String() string { func (b *RBucket) String() string {
// XXX dup wrt nodeInRange.String // XXX dup wrt nodeInRange.String
slo := "-∞"; if b.lo > KeyMin { slo = fmt.Sprintf("%v", b.lo) } hi := b.hi_
shi := "∞"; if b.hi_ < KeyMax { shi = fmt.Sprintf("%v", b.hi_+1) } if hi < KeyMax {
return fmt.Sprintf("[%s,%s)B%s{%s}", slo, shi, b.oid, kvtxt(b.kv)) hi += 1
}
return fmt.Sprintf("[%s,%s)B%s{%s}", kstr(b.lo), kstr(hi), b.oid, kvtxt(b.kv))
} }
......
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