Commit f3a5dd3c authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent 6dcfe755
......@@ -125,11 +125,11 @@ type ΔValue struct {
//
// δB:
// .rev↑
// {} root -> {}(key, value)
// {} root -> {}(key, value) XXX value -> δvalue
//
// and atTail keeps set of k/v @tail for keys changed in (tail, head]
//
// atTail:
// atTail: XXX no need for atTail as we have δvalue.Old
// {} root -> {}(key, value)
//
// It covers only changes to keys from tracked subset of BTrees parts.
......@@ -183,6 +183,7 @@ type ΔBtail struct {
type nodeTrack struct {
parent zodb.Oid // parent node | InvalidOid for root
holes SetKey // missing keys tracked under this node; nil for !leaf
// XXX move holes into separate ΔBtail..holeIdx
}
// ΔB represents a change in BTrees space.
......@@ -206,7 +207,7 @@ type ΔTtail struct {
vδT []ΔTree // changes to tree keys; rev↑. covers keys ∈ tracked subset
// {}k/v @tail for keys that are changed in (tail, head].
KVAtTail map[Key]Value
KVAtTail map[Key]Value // XXX not needed since vδT has ΔValue ?
// index for LastRevOf queries
lastRevOf map[Key]zodb.Tid // {} key -> last
......@@ -216,7 +217,7 @@ type ΔTtail struct {
// XXX -> ΔT ?
type ΔTree struct {
Rev zodb.Tid
KV map[Key]Value
KV map[Key]Value // XXX Value -> ΔValue ?
}
// NewΔBtail creates new empty ΔBtail object.
......@@ -682,32 +683,200 @@ func diffX(ctx context.Context, a, b Node, δZTC SetOid, trackIdx map[zodb.Oid]n
// δZTC is connected set of objects covering δZT (objects changed in this tree in old..new).
//
// XXX trackIdx -> just pass δBtail?
func diffT(ctx context.Context, a, b *Tree, δZTC SetOid, trackIdx map[zodb.Oid]nodeTrack) (δ map[Key]ΔValue, err error) {
tracef(" diffT %s %s\n", xidOf(a), xidOf(b))
defer xerr.Contextf(&err, "diffT %s %s", xidOf(a), xidOf(b))
func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackIdx map[zodb.Oid]nodeTrack) (δ map[Key]ΔValue, err error) {
tracef(" diffT %s %s\n", xidOf(A), xidOf(B))
defer xerr.Contextf(&err, "diffT %s %s", xidOf(A), xidOf(B))
if a == nil { panic("a is nil") }
if b == nil { panic("b is nil") }
if A == nil { panic("A is nil") }
if B == nil { panic("B is nil") }
δ = map[Key]ΔValue{}
defer tracef(" -> δ: %v\n", δ)
var av rangeSplit // nodes expanded from a
var bv rangeSplit // nodes expanded from b
// Aqueue := SetKey{} // "to process" keys on A
// Bqueue := SetKey{} // "to process" keys on B
// Adone := SetKey{} // "processed" keys on A
// Bdone := SetKey{} // "processed" keys on B
// XXX maybe walk till a from root to get more precise initial range?
// XXX precise range as for a ?
atop := &nodeInRange{lo: KeyMin, hi_: KeyMax, node: A} // [-∞, ∞)
btop := &nodeInRange{lo: KeyMin, hi_: KeyMax, node: B} // [-∞, ∞)
Av := rangeSplit{atop} // nodes expanded from A
Bv := rangeSplit{btop} // nodes expanded from B
// for phase 2:
Akqueue := SetKey{} // queue for keys in A to be processed for δ-
Bkqueue := SetKey{} // ----//---- in B for δ+
Akdone := SetKey{} // already processed keys in A
Bkdone := SetKey{} // ----//---- in B
// phase 1: expand A top-down driven by δZTC
// by default a node contributes to δ-
// a node ac does not contribute to δ- and can be skipped, if:
// - ac is not tracked, or
// - ac ∉ δZTC && ∃ bc from B: ac.oid == bc.oid (ac was not changed and stays in the tree)
Aq := [atop] // queue for A nodes that contribyte to δ-
for len(Aq) > 0 {
ra := Aq.pop()
err = ra.node.PActivate(ctx); /*X*/if err != nil { return nil, err }
defer ra.node.PDeactivate()
switch a := ra.node.(type) {
case *Bucket:
// a is bucket -> δ-
δA, err := diffB(ctx, a, nil); /*X*/if err != nil { return nil, err }
err = δMerge(δ, δA); /*X*/if err != nil { return nil, err }
// Bkqueue <- δA
for k := range δA {
Akdone.Add(k)
Bkqueue.Add(k)
}
// XXX ra.range -> Bqholes
ra.done = true
case *Tree:
// a is tree - expand it and queue children
// see for each children whether it can be skipped
// XXX if a is ø tree
av := a.Entryv()
for i, ae := range a.Entryv() {
ac := ae.Child()
_, tracked := trackIdx[ac.POid()]
if !tracked {
continue
}
if !δZTC.Has(ac.POid()) {
lo := av[i].Key()
hi_ := ra.hi_
if i+1 < len(av) {
hi_ = av[i+1].Key - 1
}
// XXX also check b's parents, as they could be already expanded?
bc, ok, err := Bv.tryGetToNode(ac.oid, lo, hi_, /*maxdepth*/2)
if err != nil { return nil, err }
if ok {
// ac can be skipped
// XXX ac.range \ bc.range -> Bqholes
continue
}
}
// ac cannot be skipped
Aq.push(ac)
}
}
}
// phase 2: reach consistency in between A and B.
// Every key removed in A has to be checked for whether it is present
// in B and contribute to δ+. In B, in turn, adding that key can add
// other keys to δ+. Those keys, in turn, have to be checked for
// whether they were present in A and contribute to δ-. For example:
//
// [ 2 4 ] [ 3 5 ]
// ↓ ↓ ↓ ↓ ↓ ↓
// |1| |23| |45| |12| |34| |56|
//
// if values for all keys change, tracked={1}, change to 1 adds
// * -B1, which queues B.1 and leads to
// * +B12, which queues A.2 and leads to
// * -B23, which queues B.3 and leads to
// * +B23, ...
for len(Bkqueue) > 0 {
for k := range Bkqueue {
b, err := Bv.GetToLeaf(ctx, k); /*X*/if err != nil { return nil, err }
// +bucket if that bucket is reached for the first time
if !b.done {
var δB map[Key]ΔValue
bbucket, ok := b.node.(*Bucket)
if ok { // !ok means ø tree
δB, err = diffB(ctx, nil, bbucket); /*X*/if err != nil { return nil, err }
}
// δ <- δB
err = δMerge(δ, δB); /*X*/if err != nil { return nil, err }
// Akqueue <- δB
for k_ := range δB {
Bkdone.Add(k_)
if !Akdone(k_) {
Akqueue.Add(k_)
}
}
b.done = true
}
// XXX k is not there -> hole
}
Bkqueue = SetKey{}
for k := range Akqueue {
a, err := Av.GetToLeaf(ctx, k); /*X*/if err != nil { return nil, err }
// -bucket if that bucket is reached for the first time
if !a.done {
var δA map[Key]ΔValue
abucket, ok := a.node.(*Bucket_
if !ok { // !ok means ø tree
δA, err := diffB(ctx, abucket, nil); /*X*/if err != nil { return nil, err }
}
// XXX also process holes?
// δ <- δA
err = δMerge(δ, δA); /*X*/if err != nil { return nil, err }
// Bkqueue <- δA
for k_ := range δA {
Akdone.Add(k_)
if !Bkdone.Has(k_) {
Bkqueue.Add(k_)
}
}
a.done = true
}
}
Akqueue = SetKey{}
}
Aq := [atop] // nodes in A that may contribute to δ
Bq := [] // ----//----
for len(Aq) > 0 {
a := Astk.pop()
// XXX activate(a)
baoverlap = []*nodeInRange // of all nodes in Bv that overlaps with a
for _, b := range baoverlap {
}
bn := Bv.Get(an.lo)
// XXX activate(bn)
bn.lo < an.lo // -> expand b if b is not bucket ; if b is bucket - b contributes; check also b followups that fit into a
(an.lo == bn.lo) && (an.hi_ == bn.hi_)
&& an.oid == bn.oid
// -> an/bn can contribute to δ only if an ∈ δZTC
}
// XXX precise range as for a ^^^ ?
btop := &nodeInRange{lo: KeyMin, hi_: KeyMax, node: b} // [-∞, ∞)
bv = rangeSplit{btop}
// initial phase: expand changed nodes in a till buckets;
// XXX changed buckets -> δ-
// XXX maybe walk till a from root to get more precise initial range?
atop := &nodeInRange{lo: KeyMin, hi_: KeyMax, node: a} // [-∞, ∞)
av = rangeSplit{atop}
Aqueue := []*nodeInRange{atop} // stack: "to process" nodes on A
Bqueue := []*nodeInRange{} // stack: "to process" nodes on B
......
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