GetFilterChanges held filtersMu for the entire call, including
pending-tx RPC formatting via NewRPCPendingTransaction. That blocks the
goroutines that append events to filters, and with unbuffered channels
the backpressure stalls event.Feed.Send — so polling clients see empty
results during high-throughput periods.
Copy the accumulated slices and nil the filter state under the lock,
then release it before doing any formatting. Same drain-on-read
semantics, just a shorter critical section.
- Move ChainConfig()/CurrentHeader() calls outside the lock — only the
PendingTransactionsSubscription fullTx branch needs them
- Tests: drain semantics for logs, blocks, pending txs (fullTx and
hash-only paths)
Related: #28838
Signed-off-by: Mark Liu <mark@prove.com.au>