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