Problem
RecentReceipts is the only source the notification dispatcher has for websocket log/receipt notifications (eth_subscribe("logs"), eth_subscribe("transactionReceipts"), eth_sendRawTransactionSync). Dispatcher.Dispatch (execution/execmodule/notification_dispatcher.go) walks [notifyFrom, notifyTo) and silently skips any block absent from the cache — there is no DB fallback, and subscribers get no gap signal that would let them refetch.
#22235 closes the resumed-block miss class (executors now only publish complete receipt sets, and reconstruction failure fails the batch instead of silently skipping). Other miss classes remain reachable:
- Eviction: the cache is bounded at 512 blocks (
NewRecentReceipts(512) in execution/notifications/accumulation.go), while the dispatcher's notify span is capped at 1024 (heightSpan := min(..., 1024)). A catch-up cycle that advances more than 512 blocks notifies a range whose older half is already evicted.
- Fork validator:
fv.extendingForkNotifications.RecentReceipts.Clear() (execution/execmodule/fork_validator.go) empties the cache.
- Any future producer-side gap: cache completeness is currently load-bearing for delivery, so every new execution path has to remember to feed it.
In all of these, log/receipt subscribers permanently miss the affected blocks' events.
Proposed fix
Add a cache-miss fallback in the dispatcher: for each missing block in the notify range, regenerate the full receipt set from the committed TemporalTx (RCacheV2 first, replay fallback — the machinery already exists in execution/receipts, e.g. DeriveBlockReceipts; make sure blooms are populated via receipts.DeriveFields, since the post-exec validator is the only other bloom filler). That turns executor-side cache completeness into a best-effort fast path instead of a delivery guarantee.
Follow-up to #22235; original symptom report in #22106.
Problem
RecentReceiptsis the only source the notification dispatcher has for websocket log/receipt notifications (eth_subscribe("logs"),eth_subscribe("transactionReceipts"),eth_sendRawTransactionSync).Dispatcher.Dispatch(execution/execmodule/notification_dispatcher.go) walks[notifyFrom, notifyTo)and silently skips any block absent from the cache — there is no DB fallback, and subscribers get no gap signal that would let them refetch.#22235 closes the resumed-block miss class (executors now only publish complete receipt sets, and reconstruction failure fails the batch instead of silently skipping). Other miss classes remain reachable:
NewRecentReceipts(512)inexecution/notifications/accumulation.go), while the dispatcher's notify span is capped at 1024 (heightSpan := min(..., 1024)). A catch-up cycle that advances more than 512 blocks notifies a range whose older half is already evicted.fv.extendingForkNotifications.RecentReceipts.Clear()(execution/execmodule/fork_validator.go) empties the cache.In all of these, log/receipt subscribers permanently miss the affected blocks' events.
Proposed fix
Add a cache-miss fallback in the dispatcher: for each missing block in the notify range, regenerate the full receipt set from the committed
TemporalTx(RCacheV2 first, replay fallback — the machinery already exists inexecution/receipts, e.g.DeriveBlockReceipts; make sure blooms are populated viareceipts.DeriveFields, since the post-exec validator is the only other bloom filler). That turns executor-side cache completeness into a best-effort fast path instead of a delivery guarantee.Follow-up to #22235; original symptom report in #22106.