perf(rpc): rebuild the block template when the mempool changes - #11374
perf(rpc): rebuild the block template when the mempool changes#11374upbqdn wants to merge 2 commits into
Conversation
And two more auto-invalidated findings. Analyzed three files, diff |
|
Heads up on the checks: this targets #11371's branch, and Zebra's |
c4e8c6e to
8baeb1e
Compare
|
Staying a draft on purpose: its base is #11371's branch, and marking it ready would let an approval merge it into that branch, which would fold these commits into #11371's diff while that PR is still under review. It retargets to Also opened #11376 for the other half of #5891, based on |
The block template updater task rebuilt every five seconds whether or not anything had changed, so a new transaction waited up to that long to reach miners while an idle chain paid for rebuilds nobody needed. Subscribe to the mempool change channel instead: additions rebuild, and a burst of them coalesces into one rebuild. A rebuild reads the whole mempool, re-runs ZIP-317 selection, and rebuilds the coinbase, so changes that cannot alter the template do not trigger one. The `Invalidated` kind also fires for transactions that failed verification and were never in the mempool, so it only rebuilds when the template's long poll ID covers the transaction. That ID is derived from every ID in the mempool rather than the transactions ZIP-317 selected, so the cache stores the whole set. Overflowing the change channel compares the mempool with that set rather than rebuilding, or a peer sending invalid transactions fast enough to make the channel lag would buy the rebuilds the filter denies it. A thirty second backstop keeps `cur_time` current, which Testnet's minimum-difficulty rule depends on, and bounds a lost notification.
A long polling `getblocktemplate` call waited on its own five second mempool poll, so the updater task's fresh template sat unused until that timer elapsed. Wait on the template cache as well, which the previous commit rebuilds when the mempool changes. The poll stays as a backstop for a node whose updater task isn't running, and waiting forever on a dropped sender keeps a template cache without a task from spinning the loop.
8baeb1e to
c2a814d
Compare
Based on #11371's branch, not
main: that PR moves template production into the block template updater task, and this changes what makes that task rebuild. Please review and merge #11371 first.Motivation
Part of #5891.
Solution
The updater task subscribes to the mempool change channel instead of rebuilding
on a five second timer, and a long polling
getblocktemplatecall returns whenthe updater publishes a template instead of waiting for its own mempool poll.
A rebuild reads the whole mempool, re-runs ZIP-317 selection, and rebuilds the
coinbase transaction, so it costs far more than the notification that triggers
it. Changes that cannot alter the template do not cause one:
debounce is also what Detect mempool changes for getblocktemplate long polling with a channel聽#5891 asks for after a chain fork: the mempool resets
and re-verifies, so waiting lets re-verified transactions rejoin the template
instead of publishing the emptied mempool.
Invalidatedalso fires for transactions that failed verification and werenever in the mempool, so it only rebuilds when the template's long poll ID
covers the transaction. That ID is derived from every ID in the mempool
rather than the transactions ZIP-317 selected, so the cache keeps the whole
set: a template built from a mempool that no longer exists must be replaced
even for a transaction that was never selected.
rebuilding. Debouncing alone would not help, because a peer sending invalid
transactions can keep sending: without the comparison, making the channel lag
would buy the rebuilds the filter denies.
Minedarrives with the chain tip change that mined it, which rebuildsanyway.
A thirty second backstop keeps
cur_timecurrent, which Testnet'sminimum-difficulty rule depends on, and bounds a lost notification. It replaces
a five second poll, so an idle node does six times less of this work.
This does not close #5891 on its own. The mempool announces newly verified
transactions from its
poll_ready, so a change is announced only when themempool is next polled, which the queue checker guarantees at a five second
rate limit. That is the remaining fixed interval the issue asks to remove, and
it needs a notification from the verifier rather than a change here.
Tests
it rebuilds twenty times.
it, so rejected transactions cannot force rebuilds. Making
Invalidatedalways rebuild fails this.
because the template's long poll ID still covers it. Checking the selected
transactions instead of the mempool ID set fails this.
template was built from does not rebuild it. Treating a lagged channel as a
change fails this.
own poll interval. Without the new wake-up it waits for that interval.
On a Regtest node, rejecting an invalid transaction broadcasts the change and a
long poll stays asleep afterwards; long polling still wakes on a chain tip
change; and an idle node holds a long poll open without returning or spinning.
AI disclosure: written with Claude Code.