diff --git a/CHANGELOG.md b/CHANGELOG.md index b6f76e8..1ce6176 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- A live stream starting at a block number with no block (a missed or skipped slot) now starts at the next block instead of waiting for the merged bundle containing it. - `hub.ForkableHub` no longer breaks when its one-block bootstrap races with the merger deleting the files of a bundle it just merged. Concretely: bootstrap retries a few times when the listing comes back with a gap or a listed file is gone by the time it is opened (both expected during that race), a failed bootstrap no longer leaves the forkable holding a bogus LIB it then can't reconcile with live blocks (the hub now only swaps in the forkable it built once bootstrap actually succeeds, falling back to a fresh one otherwise), and `cannot link block after reconnection` is only fatal once the hub has actually been ready — before that, an unlinkable block is handled like any other, already subject to `WithMaxConsecutiveUnlinkableBlocks`. Previously a consumer starting or restarting during that race could see the hub die or never become ready. - The hub no longer walks the one-block store for a block it already has below LIB. With redundant live sources, a source re-sending a final block that the hub still keeps below LIB (the lowest one, whose parent was already pruned) triggered a store lookup and a `block not linkable after one-block lookup` warning, even though the forkable drops blocks below LIB anyway. The hub now compares against LIB instead of the lowest kept block. `Forkable.LIBNum` is added for this. - `hub.WithMaxConsecutiveUnlinkableBlocks` now counts blocks rather than messages: an intermediate flash block — `PartialIndex != 0` without `LastPartial` — no longer advances the counter. Every partial of a block fails the same link check, so on a chain delivering four per block the hub gave up after a quarter of the blocks the limit names. A plain block and a block's final partial still count, and any linkable block still resets the count. diff --git a/forkable/forkable.go b/forkable/forkable.go index 3f93405..b070f93 100644 --- a/forkable/forkable.go +++ b/forkable/forkable.go @@ -203,7 +203,7 @@ func (p *Forkable) blocksFromNum(num uint64) ([]*bstream.PreprocessedBlock, erro var seenBlock bool for i := range seg { ref := seg[i].AsRef() - if !seenBlock && ref.Num() == num { + if !seenBlock && ref.Num() >= num { seenBlock = true } diff --git a/forkable/forkable_test.go b/forkable/forkable_test.go index e67f8b1..ec2a2aa 100644 --- a/forkable/forkable_test.go +++ b/forkable/forkable_test.go @@ -1910,6 +1910,27 @@ func TestForkable_BlocksFromIrreversibleNum(t *testing.T) { }, }, }, + { + name: "missing block number starts at the next block", + forkdbBlocks: []*pbbstream.Block{ + bstream.TestBlockWithLIBNum("00000005", "00000004", 2), + bstream.TestBlockWithLIBNum("00000008", "00000005", 4), + bstream.TestBlockWithLIBNum("00000009", "00000008", 5), + }, + requestBlock: 6, + expectBlocks: []expectedBlock{ + { + bstream.TestBlockWithLIBNum("00000008", "00000005", 4), + bstream.StepNew, + 5, + }, + { + bstream.TestBlockWithLIBNum("00000009", "00000008", 5), + bstream.StepNew, + 5, + }, + }, + }, { name: "no source", forkdbBlocks: []*pbbstream.Block{