Commit ef10f820 authored by Kirill Smelkov's avatar Kirill Smelkov

wcfs: Adjust ΔFtail/ΔBtail to allow point-queries with at=tail

This is needed because when e.g. wcfs is just started the coverage of
ΔFtail is (head,head] i.e. empty, and if user wants to setup a watch
with at=head, it becomes watch with at=tail. Then that at is used in a
query and if point-queries with at=tail are disallowed it panics with
"at out of bounds".

This fixes crashes in test_wcfs_watch_setup (see 339f1884 "wcfs: tests:
Always start tDB with ZBigFile pre-created before WCFS startup") and in
test_wcfs_crash_old_data (see 97ce5105 "wcfs: tests: Add test do
demonstrate "at out of bounds" crash on readPinWatchers ->
ΔFtail.BlkRevAt")

For the reference zodb.ΔTail already allows point queries with at=tail:

https://lab.nexedi.com/kirr/neo/blob/1193c44e/go/zodb/δtail.go#L202-206
https://lab.nexedi.com/kirr/neo/blob/1193c44e/go/zodb/δtail.go#L225-228
parent 97ce5105
// Copyright (C) 2018-2021 Nexedi SA and Contributors. // Copyright (C) 2018-2022 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
...@@ -935,7 +935,7 @@ func (δTtail *_ΔTtail) _forgetPast(revCut zodb.Tid) { ...@@ -935,7 +935,7 @@ func (δTtail *_ΔTtail) _forgetPast(revCut zodb.Tid) {
// revExact: false // revExact: false
// //
// key must be tracked // key must be tracked
// at must ∈ (tail, head] // at must ∈ {tail} ∪ (tail, head]
func (δBtail *ΔBtail) GetAt(root zodb.Oid, key Key, at zodb.Tid) (value Value, rev zodb.Tid, valueExact, revExact bool, err error) { func (δBtail *ΔBtail) GetAt(root zodb.Oid, key Key, at zodb.Tid) (value Value, rev zodb.Tid, valueExact, revExact bool, err error) {
defer xerr.Contextf(&err, "ΔBtail: root<%s>: get %d @%s", root, key, at) defer xerr.Contextf(&err, "ΔBtail: root<%s>: get %d @%s", root, key, at)
...@@ -956,7 +956,7 @@ func (δBtail *ΔBtail) GetAt(root zodb.Oid, key Key, at zodb.Tid) (value Value, ...@@ -956,7 +956,7 @@ func (δBtail *ΔBtail) GetAt(root zodb.Oid, key Key, at zodb.Tid) (value Value,
tail := δBtail.Tail() tail := δBtail.Tail()
head := δBtail.Head() head := δBtail.Head()
if !(tail < at && at <= head) { if !(tail <= at && at <= head) {
panicf("at out of bounds: at: @%s, (tail, head] = (@%s, @%s]", at, tail, head) panicf("at out of bounds: at: @%s, (tail, head] = (@%s, @%s]", at, tail, head)
} }
......
// Copyright (C) 2020-2021 Nexedi SA and Contributors. // Copyright (C) 2020-2022 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
...@@ -1167,6 +1167,11 @@ func TestΔBtailGetAt(t_ *testing.T) { ...@@ -1167,6 +1167,11 @@ func TestΔBtailGetAt(t_ *testing.T) {
} }
// @at key value rev valueExact revExact // @at key value rev valueExact revExact
assertGetAt(at1, 1, ø, at1, false, false)
assertGetAt(at1, 2, ø, at1, false, false)
assertGetAt(at1, 3, "c", at1, true, false)
assertGetAt(at1, 4, ø, at1, true, false)
assertGetAt(at2, 1, ø, at1, false, false) assertGetAt(at2, 1, ø, at1, false, false)
assertGetAt(at2, 2, ø, at1, false, false) assertGetAt(at2, 2, ø, at1, false, false)
assertGetAt(at2, 3, "c", at1, true, false) assertGetAt(at2, 3, "c", at1, true, false)
......
// Copyright (C) 2019-2021 Nexedi SA and Contributors. // Copyright (C) 2019-2022 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
...@@ -1061,8 +1061,8 @@ func flipsign(x int64) int64 { ...@@ -1061,8 +1061,8 @@ func flipsign(x int64) int64 {
// //
// if exact=False - what is returned is only an upper bound for last block revision. // if exact=False - what is returned is only an upper bound for last block revision.
// //
// zfile must be any checkout from (tail, head] // zfile must be any checkout from {tail} ∪ (tail, head]
// at must ∈ (tail, head] // at must ∈ {tail} ∪ (tail, head]
// blk must be tracked // blk must be tracked
func (δFtail *ΔFtail) BlkRevAt(ctx context.Context, zfile *ZBigFile, blk int64, at zodb.Tid) (_ zodb.Tid, exact bool, err error) { func (δFtail *ΔFtail) BlkRevAt(ctx context.Context, zfile *ZBigFile, blk int64, at zodb.Tid) (_ zodb.Tid, exact bool, err error) {
foid := zfile.POid() foid := zfile.POid()
...@@ -1070,17 +1070,17 @@ func (δFtail *ΔFtail) BlkRevAt(ctx context.Context, zfile *ZBigFile, blk int64 ...@@ -1070,17 +1070,17 @@ func (δFtail *ΔFtail) BlkRevAt(ctx context.Context, zfile *ZBigFile, blk int64
//fmt.Printf("\nblkrev #%d @%s\n", blk, at) //fmt.Printf("\nblkrev #%d @%s\n", blk, at)
// assert at ∈ (tail, head] // assert at ∈ [tail, head]
tail := δFtail.Tail() tail := δFtail.Tail()
head := δFtail.Head() head := δFtail.Head()
if !(tail < at && at <= head) { if !(tail <= at && at <= head) {
panicf("at out of bounds: at: @%s, (tail, head] = (@%s, @%s]", at, tail, head) panicf("at out of bounds: at: @%s, (tail, head] = (@%s, @%s]", at, tail, head)
} }
// assert zfile.at ∈ (tail, head] // assert zfile.at ∈ [tail, head]
zconn := zfile.PJar() zconn := zfile.PJar()
zconnAt := zconn.At() zconnAt := zconn.At()
if !(tail < zconnAt && zconnAt <= head) { if !(tail <= zconnAt && zconnAt <= head) {
panicf("zconn.at out of bounds: zconn.at: @%s, (tail, head] = (@%s, @%s]", zconnAt, tail, head) panicf("zconn.at out of bounds: zconn.at: @%s, (tail, head] = (@%s, @%s]", zconnAt, tail, head)
} }
...@@ -1119,7 +1119,7 @@ func (δFtail *ΔFtail) BlkRevAt(ctx context.Context, zfile *ZBigFile, blk int64 ...@@ -1119,7 +1119,7 @@ func (δFtail *ΔFtail) BlkRevAt(ctx context.Context, zfile *ZBigFile, blk int64
//fmt.Printf(" epoch: @%s root: %s\n", epoch, root) //fmt.Printf(" epoch: @%s root: %s\n", epoch, root)
if root == xbtree.VDEL { if root == xbtree.VDEL {
return epoch, true, nil return epoch, (epoch > tail), nil
} }
zblk, tabRev, zblkExact, tabRevExact, err := δFtail.δBtail.GetAt(root, blk, at) zblk, tabRev, zblkExact, tabRevExact, err := δFtail.δBtail.GetAt(root, blk, at)
...@@ -1135,7 +1135,7 @@ func (δFtail *ΔFtail) BlkRevAt(ctx context.Context, zfile *ZBigFile, blk int64 ...@@ -1135,7 +1135,7 @@ func (δFtail *ΔFtail) BlkRevAt(ctx context.Context, zfile *ZBigFile, blk int64
} }
// if δBtail does not have entry that covers root[blk] - get it // if δBtail does not have entry that covers root[blk] - get it
// through any zconn with .at ∈ (tail, head]. // through any zconn with .at ∈ [tail, head].
if !zblkExact { if !zblkExact {
xblktab, err := zconn.Get(ctx, root) xblktab, err := zconn.Get(ctx, root)
if err != nil { if err != nil {
......
// Copyright (C) 2019-2021 Nexedi SA and Contributors. // Copyright (C) 2019-2022 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
...@@ -571,9 +571,16 @@ func testΔFtail(t_ *testing.T, testq chan ΔFTestEntry) { ...@@ -571,9 +571,16 @@ func testΔFtail(t_ *testing.T, testq chan ΔFTestEntry) {
return blkv[i] < blkv[j] return blkv[i] < blkv[j]
}) })
for j := 0; j < len(vδf); j++ { for j := -1; j < len(vδf); j++ {
at := vδf[j].Rev var at zodb.Tid
blkRev := blkRevAt[at] var blkRev map[int64]zodb.Tid
if j == -1 {
at = δFtail.Tail()
// blkRev remains ø
} else {
at = vδf[j].Rev
blkRev = blkRevAt[at]
}
for _, blk := range blkv { for _, blk := range blkv {
rev, exact, err := δFtail.BlkRevAt(ctx, zfile, blk, at); X(err) rev, exact, err := δFtail.BlkRevAt(ctx, zfile, blk, at); X(err)
revOK, ok := blkRev[blk] revOK, ok := blkRev[blk]
......
...@@ -1836,7 +1836,7 @@ def test_wcfs_crash_old_data(): ...@@ -1836,7 +1836,7 @@ def test_wcfs_crash_old_data():
wl = t.openwatch() wl = t.openwatch()
wl.watch(zf, at1, {}) wl.watch(zf, at1, {})
# wcfs is crashing on readPinWatcher -> ΔFtail.BlkRevAt with # wcfs was crashing on readPinWatcher -> ΔFtail.BlkRevAt with
# "at out of bounds: at: @at1, (tail,head] = (@at1,@at1] # "at out of bounds: at: @at1, (tail,head] = (@at1,@at1]
# because BlkRevAt(at=tail) query was disallowed. # because BlkRevAt(at=tail) query was disallowed.
f.assertBlk(0, 'a') # [0] becomes tracked f.assertBlk(0, 'a') # [0] becomes tracked
......
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