Commit bc3b5ec3 authored by Kirill Smelkov's avatar Kirill Smelkov

go/zodb: Fix ΔTail.LastRevOf based on WCFS experience

- the only valid range for at is [tail, head]. Don't try to return anything
  meaningful for queries outside of this range and just panic instead. This is
  consistent with SliceByRev, which also panics on invalid query, and it is also
  consistent with semantic model that ΔTail is a vector with data keyed by tid in
  range (tail, head]: if key is out of vector range, access to the vector should
  panic, isn't it?

- instead of returning revision of minimum entry on exact=n, always
  return (tail, exact=n) in that case.

The change in behaviour is consistent with ΔFtail and ΔBtail from WCFS
and is needed for ΔFtail to function correctly:

https://lab.nexedi.com/kirr/wendelin.core/blob/22f5f096/wcfs/internal/xbtree/δbtail.go
https://lab.nexedi.com/kirr/wendelin.core/blob/22f5f096/wcfs/internal/zdata/δftail.go
parent bb618ce1
// Copyright (C) 2018-2020 Nexedi SA and Contributors.
// Copyright (C) 2018-2021 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
......@@ -201,46 +201,35 @@ func (δtail *ΔTail) ForgetPast(revCut Tid) {
// LastRevOf tries to return what was the last revision that changed id as of at database state.
//
// Depending on current information in δtail it returns either exact result, or
// an upper-bound estimate for the last id revision, assuming id was changed ≤ at:
// it must be called with the following condition:
//
// 1) if δtail does not cover at, at is returned:
// tail ≤ at ≤ head
//
// # at ∉ [min(rev ∈ δtail), max(rev ∈ δtail)]
// LastRevOf(id, at) = at
// Depending on current information in δtail it returns either exact result, or
// an upper-bound estimate for the last id revision:
//
// 2) if δtail has an entry corresponding to id change, it gives exactly the
// 1) if δtail has an entry corresponding to id change, it gives exactly the
// 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)
// LastRevOf(id, at) = max(rev: rev changed id && rev ≤ at), true
//
// 3) if δtail does not contain appropriate record with id - it returns δtail's
// 2) 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:
//
// # at ∈ [min(rev ∈ δtail), max(rev ∈ δtail)]
// # ∄ rev ∈ δtail: rev changed id && rev ≤ at
// LastRevOf(id, at) = min(rev ∈ δtail)
// LastRevOf(id, at) = δtail.tail, false
//
// On return exact indicates whether returned revision is exactly the last
// revision of id, or only an upper-bound estimate of it.
func (δtail *ΔTail) LastRevOf(id Oid, at Tid) (_ Tid, exact bool) {
// check if we have no coverage at all
l := len(δtail.tailv)
if l == 0 {
return at, false
}
revMin := δtail.tailv[0].Rev
revMax := δtail.tailv[l-1].Rev
if !(revMin <= at && at <= revMax) {
return at, false
if !(δtail.tail <= at && at <= δtail.head) {
panic(fmt.Sprintf("at out of bounds: at: @%s, (tail, head] = (@%s, @%s]", at, δtail.tail, δtail.head))
}
// we have the coverage
rev, ok := δtail.lastRevOf[id]
if !ok {
return δtail.tailv[0].Rev, false
return δtail.tail, false
}
if rev <= at {
......@@ -249,7 +238,7 @@ func (δtail *ΔTail) LastRevOf(id Oid, at Tid) (_ Tid, exact bool) {
// what's in index is after at - scan tailv back to find appropriate entry
// XXX linear scan - see .lastRevOf comment.
for i := l - 1; i >= 0; i-- {
for i := len(δtail.tailv) - 1; i >= 0; i-- {
δ := δtail.tailv[i]
if δ.Rev > at {
continue
......@@ -263,5 +252,5 @@ func (δtail *ΔTail) LastRevOf(id Oid, at Tid) (_ Tid, exact bool) {
}
// nothing found
return δtail.tailv[0].Rev, false
return δtail.tail, false
}
// Copyright (C) 2018-2019 Nexedi SA and Contributors.
// Copyright (C) 2018-2021 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
......@@ -174,14 +174,13 @@ func TestΔTail(t *testing.T) {
δtail = NewΔTail(3)
δCheck(3,3)
δCheckLastUP(4, 12, 12) // δtail = ø
δCheckLastUP(3, 3, 3) // δtail = ø
δAppend(R(10, 3,5))
δCheck(3,10, R(10, 3,5))
δCheckLastUP(3, 2, 2) // at < δtail
δCheckLastUP(3, 12, 12) // at > δtail
δCheckLastUP(4, 10, 10) // id ∉ δtail
δCheckLastUP(3, 9, 3) // id ∈ δtail, but has no entry with rev ≤ at
δCheckLastUP(4, 10, 3) // id ∉ δtail
δAppend(R(11, 7))
δCheck(3,11, R(10, 3,5), R(11, 7))
......@@ -192,7 +191,7 @@ func TestΔTail(t *testing.T) {
δAppend(R(14, 3,8))
δCheck(3,14, R(10, 3,5), R(11, 7), R(12, 7), R(14, 3,8))
δCheckLastUP(8, 12, 10) // id ∈ δtail, but has no entry with rev ≤ at
δCheckLastUP(8, 12, 3) // id ∈ δtail, but has no entry with rev ≤ at
δtail.ForgetPast(9)
δCheck(9,14, R(10, 3,5), R(11, 7), R(12, 7), R(14, 3,8))
......
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