mirror of
https://github.com/logos-storage/logos-storage-nim.git
synced 2026-05-12 06:19:33 +00:00
158 lines
4.7 KiB
Nim
158 lines
4.7 KiB
Nim
import std/sets
|
|
import std/importutils
|
|
|
|
import pkg/unittest2
|
|
import pkg/chronos
|
|
|
|
import pkg/storage/blockexchange/engine/downloadcontext {.all.}
|
|
import pkg/storage/blockexchange/engine/scheduler {.all.}
|
|
|
|
privateAccess(BroadcastAvailabilityTracker)
|
|
|
|
suite "BroadcastAvailabilityTracker (sequential OOO)":
|
|
const
|
|
WindowSize = 16384'u64
|
|
Threshold = 0.75
|
|
BatchSize = 100'u64
|
|
TotalBlocks = 100_000'u64
|
|
|
|
var
|
|
tracker: BroadcastAvailabilityTracker
|
|
sched: Scheduler
|
|
|
|
template expireInterval() =
|
|
tracker.lastBroadcastTime = Moment.now() - 1.hours
|
|
tracker.broadcastInterval = 1.milliseconds
|
|
|
|
setup:
|
|
sched = Scheduler.new()
|
|
sched.init(TotalBlocks, BatchSize, WindowSize, Threshold)
|
|
tracker = BroadcastAvailabilityTracker(
|
|
policy: spSequential,
|
|
lastBroadcastedWatermark: 0,
|
|
broadcastedOutOfOrder: initHashSet[uint64](),
|
|
pendingOOOSnapshot: initHashSet[uint64](),
|
|
lastBroadcastTime: Moment.now(),
|
|
broadcastInterval: 1.hours,
|
|
)
|
|
|
|
test "OOO batch triggers broadcast when watermark has not moved":
|
|
discard sched.take()
|
|
discard sched.take()
|
|
sched.markComplete(100)
|
|
|
|
check sched.completedWatermark() == 0
|
|
check tracker.shouldBroadcast(sched)
|
|
|
|
let ranges = tracker.getRanges(sched)
|
|
check ranges == @[(start: 100'u64, count: BatchSize)]
|
|
check tracker.pendingOOOSnapshot == toHashSet([100'u64])
|
|
|
|
tracker.markBroadcasted(sched)
|
|
check tracker.broadcastedOutOfOrder == toHashSet([100'u64])
|
|
check tracker.lastBroadcastedWatermark == 0
|
|
|
|
test "Already-broadcast OOO is not re-emitted next cycle":
|
|
for _ in 0 ..< 3:
|
|
discard sched.take()
|
|
sched.markComplete(100)
|
|
discard tracker.getRanges(sched)
|
|
tracker.markBroadcasted(sched)
|
|
|
|
sched.markComplete(200)
|
|
check tracker.shouldBroadcast(sched)
|
|
let ranges = tracker.getRanges(sched)
|
|
check ranges == @[(start: 200'u64, count: BatchSize)]
|
|
|
|
test "Multiple new OOO batches are all emitted in a single broadcast":
|
|
for _ in 0 ..< 4:
|
|
discard sched.take()
|
|
sched.markComplete(100)
|
|
sched.markComplete(200)
|
|
sched.markComplete(300)
|
|
|
|
let ranges = tracker.getRanges(sched)
|
|
check ranges.len == 3
|
|
|
|
var starts: HashSet[uint64]
|
|
for r in ranges:
|
|
check r.count == BatchSize
|
|
starts.incl(r.start)
|
|
check starts == toHashSet([100'u64, 200, 300])
|
|
check tracker.pendingOOOSnapshot == toHashSet([100'u64, 200, 300])
|
|
|
|
test "Watermark absorbing already-broadcast OOO produces prefix overlap":
|
|
discard sched.take()
|
|
discard sched.take()
|
|
sched.markComplete(100)
|
|
discard tracker.getRanges(sched)
|
|
tracker.markBroadcasted(sched)
|
|
check tracker.broadcastedOutOfOrder == toHashSet([100'u64])
|
|
|
|
sched.markComplete(0)
|
|
check sched.completedWatermark() == 200
|
|
|
|
expireInterval()
|
|
check tracker.shouldBroadcast(sched)
|
|
|
|
let ranges = tracker.getRanges(sched)
|
|
check ranges == @[(start: 0'u64, count: 200'u64)]
|
|
|
|
tracker.markBroadcasted(sched)
|
|
check tracker.broadcastedOutOfOrder.len == 0
|
|
|
|
test "No new blocks and no new OOO → shouldBroadcast stays false":
|
|
expireInterval()
|
|
check not tracker.shouldBroadcast(sched)
|
|
|
|
test "Prefix-only broadcast fires only after the interval elapses":
|
|
discard sched.take()
|
|
sched.markComplete(0)
|
|
|
|
check not tracker.shouldBroadcast(sched)
|
|
|
|
expireInterval()
|
|
check tracker.shouldBroadcast(sched)
|
|
|
|
let ranges = tracker.getRanges(sched)
|
|
check ranges == @[(start: 0'u64, count: BatchSize)]
|
|
check tracker.pendingOOOSnapshot.len == 0
|
|
|
|
tracker.markBroadcasted(sched)
|
|
check tracker.lastBroadcastedWatermark == BatchSize
|
|
check tracker.broadcastedOutOfOrder.len == 0
|
|
|
|
test "OOO arriving between getRanges and markBroadcasted is not marked":
|
|
for _ in 0 ..< 3:
|
|
discard sched.take()
|
|
sched.markComplete(100)
|
|
|
|
let ranges = tracker.getRanges(sched)
|
|
check ranges == @[(start: 100'u64, count: BatchSize)]
|
|
check tracker.pendingOOOSnapshot == toHashSet([100'u64])
|
|
|
|
sched.markComplete(200)
|
|
|
|
tracker.markBroadcasted(sched)
|
|
check tracker.broadcastedOutOfOrder == toHashSet([100'u64])
|
|
check 200'u64 notin tracker.broadcastedOutOfOrder
|
|
|
|
check tracker.shouldBroadcast(sched)
|
|
let ranges2 = tracker.getRanges(sched)
|
|
check ranges2 == @[(start: 200'u64, count: BatchSize)]
|
|
check tracker.pendingOOOSnapshot == toHashSet([200'u64])
|
|
|
|
test "getRanges clears stale snapshot when mark was skipped":
|
|
discard sched.take()
|
|
discard sched.take()
|
|
sched.markComplete(100)
|
|
discard tracker.getRanges(sched)
|
|
check tracker.pendingOOOSnapshot.len == 1
|
|
|
|
sched.markComplete(0)
|
|
check sched.completedWatermark() == 200
|
|
|
|
let ranges = tracker.getRanges(sched)
|
|
check ranges == @[(start: 0'u64, count: 200'u64)]
|
|
check tracker.pendingOOOSnapshot.len == 0
|