-
Notifications
You must be signed in to change notification settings - Fork 21.5k
Description
System information
Geth version: 1.9.25
OS & Version: Linux
Expected behaviour
We are calling rpc.Unsubscribe like so:
client.SubscribeFilterLogs(ctx, filterQuery, ch)
# ... some time later ...
client.Unsubscribe()
close(ch)
I would expect this to always be safe.
Actual behaviour
In very rare cases this can panic with send on closed channel. It seems that messages can still be sent on the passed in channel even after Unsubscribe returns.
We found this while load testing with fast resubscribes and hundreds of thousands of iterations.
Steps to reproduce the behaviour
Almost impossible to reproduce. Extremely high rates of subscribe/unsubscribe with channel close as detailed above might expose it.
Backtrace
panic: send on closed channel
goroutine 276873727 [running]:
reflect.rselect(0xc03196b860, 0x3, 0x3, 0x188458e, 0xe)
/usr/local/go/src/runtime/select.go:544 +0x38a
reflect.Select(0xc013d83ee0, 0x3, 0x3, 0xc02e50f6e0, 0x1796e20, 0xc0353b62c0, 0x0, 0x0)
/usr/local/go/src/reflect/value.go:2229 +0x170
github.com/ethereum/go-ethereum/rpc.(*ClientSubscription).forward(0xc005e0c090, 0xc0324ef100, 0x0, 0x0)
/root/go/pkg/mod/github.com/ethereum/[email protected]/rpc/subscription.go:299 +0x2db
github.com/ethereum/go-ethereum/rpc.(*ClientSubscription).start(0xc005e0c090)
/root/go/pkg/mod/github.com/ethereum/[email protected]/rpc/subscription.go:279 +0x2b
created by github.com/ethereum/go-ethereum/rpc.(*handler).handleResponse
/root/go/pkg/mod/github.com/ethereum/[email protected]/rpc/handler.go:284 +0x23d
Probable cause and suggested fix
It seems that quitWithError (
go-ethereum/rpc/subscription.go
Line 251 in 231040c
| func (sub *ClientSubscription) quitWithError(unsubscribeServer bool, err error) { |
forward (go-ethereum/rpc/subscription.go
Line 282 in 231040c
| func (sub *ClientSubscription) forward() (unsubscribeServer bool, err error) { |
Unsubscribe returns.
A coordination channel so that quitWithError waits until forward has executed could resolve it.