Increased b-msghand thread utilization due to runestone transactions on 2026-02-17

I noticed my peer-observer nodes spending a lot of time in b-msghand thread today on 2026-02-17 starting around 1:30 am UTC. Note that the screenshots are UTC+1.

(red means we spend 1000ms out of 1000ms (i.e. 100%) of our time in the thread)

Around the same time, the mempools started to fill up from around 6% to 60%

and the sum(inv-to-send) sizes across all peers increased significantly. To about 1.7 M on some nodes.

with the maxium inv-to-send sizes being about 60k

edit: It was also visible in the Bitcoin protocol Ping-Pong time monitoring I do on localhost:


Randomly sampling from some of the transactions my nodes saw:

All include a UNCOMMON•GOODS rune, some also inscriptions, and NFT related content.

2 Likes

Interestingly, the last time I noticed this was also on a 17th: Increased b-msghand thread utilization due to many 352 byte runestone inscriptions on 2025-11-17

Another broadcast happend at 17:20 UTC

Here’s a snapshot of the continues profiling flamegraphs I’ve been experimenting with from the 1:30am broadcast: Pprof.me

It doesn’t show anything too suspicious I think. The getrawaddrman RPC taking long is known to me, but unrelated to this.

Looking on a different node at the broadcast at around 17:20 UTC shows that we spend about 20% of our time in CompareMiningScoreWithTopology() (i.e. the new name of CompareDepthAndScore).

Would one possibility be to have the inv-to-send sets always sorted from the beginning to avoid doing it every time we decide to INV something?

Sorting before sending ensures that you’re not leaking info about the order in which you received the txs by the order in which you announce them.

I think have a global queue for rate limiting outbound announcements is the solution here, fwiw. Here’s a gist:

I’ve been running that code with a very low rate limit for inbounds (4tx/s instead of the 14tx/s target from core) to ensure it behaves well when backlogs occur, and it seems to.

1 Like

To add: here are the transaction inv-sizes to a local spy-node-like peer (does not INV any transactions to the node).

And here is the rate at which my local spy-node-like peer received WTx invs.

Normally, one would expect the rate at which we send INVs to remain fairly constant since we’re on a fixed timer. However, it seems like for some nodes it actually slowed INV-sending down by about 50% while for others it (at least briefly) speed up. My theory for slowing down is: the node is overloaded to a point where it can’t send out INVs at a normal rate.

Sure. I think I wasn’t clear in my description: Don’t keep a set (as we do currently), but keep a (sorted) queue. When inserting a to-be-announced transaction, put it in the right place in the queue. This avoids having to re-sort the whole set every 5s (or 2s for outbounds) for each peer. The queue is already sorted, so we can just pick the top 70 + x transactions to INV. Then, do some clean up removing evicted/confirmed transactions by iterating the queue once.

Maybe that’s similar to you gloabl queue. I’ll have a look.

As far as data structures go, a sorted queue is best stored as a set anyway; problem is that with cluster mempool the sort order doesn’t stay consistent as txs are added – a cpfp tx can bump the ordering of the pre-existing parent, eg. That breaks set invariants, so risks UB if using std::set, and I couldn’t see a simpler way of dealing with it properly without just doing a re-sort.

1 Like

Cleaned up the code, added tests etc, and PRed as: