Commit 7811163e authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent a20f81e1
......@@ -413,11 +413,12 @@ type Root struct {
zstor zodb.IStorage
// ZODB DB handle for zstor.
// keeps cache of connections for @<rev>/ accesses.
// only one connection is used for for each @<rev>.
// keeps cache of connections for @<rev>/ accesse.
// only one connection is used for each @<rev>.
zdb *zodb.DB
// directory + ZODB connection for head/
// (zhead is Resync'ed and is kept outside zdb pool)
head *Head
// directories + ZODB connections for @<rev>/
......
// Code generated by gen-δtail I64 int64; DO NOT EDIT.
// (from lab.nexedi.com/kirr/neo/go/zodb @ v1.9-2080-gd1f63f32)
// (from lab.nexedi.com/kirr/neo/go/zodb @ v1.9-2136-g1742e47b)
// Copyright (C) 2018-2019 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
......@@ -35,7 +35,7 @@ import (
//
// It semantically consists of
//
// [](rev↑, []id) XXX + head?
// [](rev↑, []id) ; rev ∈ (tail, head]
//
// and index
//
......@@ -43,15 +43,17 @@ import (
//
// where
//
// rev - is ZODB revision, and
// id - is an identifier of what has been changed(*)
// rev - is ZODB revision,
// id - is an identifier of what has been changed(*), and
// (tail, head] - is covered revision range
//
// It provides operations to
//
// - append information to the tail about next revision,
// - forget information in the tail past specified revision, and
// - query the tail about what is last revision that changed an id.
// - query the tail about what head/tail XXX?
// - forget information in the tail past specified revision,
// - query the tail for slice with rev ∈ (lo, hi],
// - query the tail about what is last revision that changed an id,
// - query the tail for len and (tail, head].
//
// ΔTailI64 is safe to access for multiple-readers / single writer.
//
......@@ -61,32 +63,89 @@ import (
// #blk - file block number, when ΔTailI64 represents changes to a file.
type ΔTailI64 struct {
head zodb.Tid
tailv []δRevEntryI64
tail zodb.Tid
tailv []ΔRevEntry // XXX -> revv ?
lastRevOf map[int64]zodb.Tid // index for LastRevOf queries
// TODO also add either tailv idx <-> rev index, or lastRevOf -> tailv idx
// (if linear back-scan of δRevEntryI64 starts to eat cpu).
// XXX -> lastRevOf = {} oid -> []rev↑ if linear scan in LastRevOf starts to eat cpu
}
// δRevEntryI64 represents information of what have been changed in one revision.
type δRevEntryI64 struct {
rev zodb.Tid
changev []int64
// ΔRevEntry represents information of what have been changed in one revision.
//
// XXX -> ΔRevEntry?
type ΔRevEntry struct {
Rev zodb.Tid
Changev []int64
}
// NewΔTailI64 creates new ΔTailI64 object.
func NewΔTailI64() *ΔTailI64 {
return &ΔTailI64{lastRevOf: make(map[int64]zodb.Tid)}
//
// Initial coverage of created ΔTailI64 is (at₀, at₀].
func NewΔTailI64(at0 zodb.Tid) *ΔTailI64 {
return &ΔTailI64{
head: at0,
tail: at0,
lastRevOf: make(map[int64]zodb.Tid),
}
}
// Head returns database state starting from which δtail has history coverage. XXX
// Len returns number of revisions.
func (δtail *ΔTailI64) Len() int {
return len(δtail.tailv)
}
// Head returns newest database state for which δtail has history coverage.
//
// For newly created ΔTailI64 Head returns 0.
// Head is ↑, in particular it does not go back to 0 when δtail becomes empty.
// Head is ↑ on Append, in particular it does not ↓ on Forget even if δtail becomes empty.
func (δtail *ΔTailI64) Head() zodb.Tid {
return δtail.head
}
// Tail returns oldest database state for which δtail has history coverage.
//
// Tail is ↑= on Forget, even if δtail becomes empty.
func (δtail *ΔTailI64) Tail() zodb.Tid {
return δtail.tail
}
// SliceByRev returns δtail slice of elements with .rev ∈ (low, high].
//
// it must be called with the following condition:
//
// tail ≤ low ≤ high ≤ head
//
// the caller must not modify returned slice.
//
// Note: contrary to regular go slicing, low is exclusive while high is inclusive.
func (δtail *ΔTailI64) SliceByRev(low, high zodb.Tid) /*readonly*/ []ΔRevEntry {
tail := δtail.Tail()
head := δtail.head
if !(tail <= low && low <= high && high <= head) {
panic(fmt.Sprintf("δtail.Slice: invalid query: (%s, %s]; (tail, head] = (%s, %s]", low, high, tail, head))
}
tailv := δtail.tailv
// ex (0,0] tail..head = 0..0
if len(tailv) == 0 {
return tailv
}
// find max j : [j].rev ≤ high XXX linear scan -> binary search
j := len(tailv)-1
for ; j >= 0 && tailv[j].Rev > high; j-- {}
if j < 0 {
return nil // ø
}
// find max i : [i].rev > low XXX linear scan -> binary search
i := j
for ; i >= 0 && tailv[i].Rev > low; i-- {}
i++
return tailv[i:j+1]
}
// XXX add way to extend coverage without appending changed data? (i.e. if a
// txn did not change file at all) -> but then it is simply .Append(rev, nil)?
......@@ -100,24 +159,29 @@ func (δtail *ΔTailI64) Append(rev zodb.Tid, changev []int64) {
}
δtail.head = rev
δtail.tailv = append(δtail.tailv, δRevEntryI64{rev, changev})
δtail.tailv = append(δtail.tailv, ΔRevEntry{rev, changev})
for _, id := range changev {
δtail.lastRevOf[id] = rev
}
}
// ForgetBefore discards all δtail entries with rev < revCut.
func (δtail *ΔTailI64) ForgetBefore(revCut zodb.Tid) {
// ForgetPast discards all δtail entries with rev ≤ revCut.
func (δtail *ΔTailI64) ForgetPast(revCut zodb.Tid) {
// revCut ≤ tail: nothing to do; don't let .tail go ↓
if revCut <= δtail.tail {
return
}
icut := 0
for i, δ := range δtail.tailv {
rev := δ.rev
if rev >= revCut {
rev := δ.Rev
if rev > revCut {
break
}
icut = i+1
// if forgotten revision was last for id, we have to update lastRevOf index
for _, id := range δ.changev {
for _, id := range δ.Changev {
if δtail.lastRevOf[id] == rev {
delete(δtail.lastRevOf, id)
}
......@@ -128,9 +192,11 @@ func (δtail *ΔTailI64) ForgetBefore(revCut zodb.Tid) {
// 1) growing underlying storage array indefinitely
// 2) keeping underlying storage after forget
l := len(δtail.tailv)-icut
tailv := make([]δRevEntryI64, l)
tailv := make([]ΔRevEntry, l)
copy(tailv, δtail.tailv[icut:])
δtail.tailv = tailv
δtail.tail = revCut
}
// LastRevOf tries to return what was the last revision that changed id as of at database state.
......@@ -144,14 +210,14 @@ func (δtail *ΔTailI64) ForgetBefore(revCut zodb.Tid) {
// LastRevOf(id, at) = at
//
// 2) if δtail has an entry corresponding to id change, it gives exactly the
// last revision that changed id:
// last revision that changed id:
//
// # at ∈ [min(rev ∈ δtail), max(rev ∈ δtail)]
// # ∃ rev ∈ δtail: rev changed id && rev ≤ at
// LastRevOf(id, at) = max(rev: rev changed id && rev ≤ at)
//
// 3) if δtail does not contain appropriate record with id - it returns δtail's
// lower bound as the estimate for the upper bound of the last id revision:
// lower bound as the estimate for the upper bound of the last id revision:
//
// # at ∈ [min(rev ∈ δtail), max(rev ∈ δtail)]
// # ∄ rev ∈ δtail: rev changed id && rev ≤ at
......@@ -165,8 +231,8 @@ func (δtail *ΔTailI64) LastRevOf(id int64, at zodb.Tid) (_ zodb.Tid, exact boo
if l == 0 {
return at, false
}
revMin := δtail.tailv[0].rev
revMax := δtail.tailv[l-1].rev
revMin := δtail.tailv[0].Rev
revMax := δtail.tailv[l-1].Rev
if !(revMin <= at && at <= revMax) {
return at, false
}
......@@ -174,7 +240,7 @@ func (δtail *ΔTailI64) LastRevOf(id int64, at zodb.Tid) (_ zodb.Tid, exact boo
// we have the coverage
rev, ok := δtail.lastRevOf[id]
if !ok {
return δtail.tailv[0].rev, false
return δtail.tailv[0].Rev, false
}
if rev <= at {
......@@ -182,20 +248,20 @@ func (δtail *ΔTailI64) LastRevOf(id int64, at zodb.Tid) (_ zodb.Tid, exact boo
}
// what's in index is after at - scan tailv back to find appropriate entry
// XXX linear scan
// XXX linear scan - see .lastRevOf comment.
for i := l - 1; i >= 0; i-- {
δ := δtail.tailv[i]
if δ.rev > at {
if δ.Rev > at {
continue
}
for _, δid := range δ.changev {
for _, δid := range δ.Changev {
if id == δid {
return δ.rev, true
return δ.Rev, true
}
}
}
// nothing found
return δtail.tailv[0].rev, false
return δtail.tailv[0].Rev, false
}
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