mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
cmd, dashboard, p2p: patch peer event data race
This commit is contained in:
parent
baded64d88
commit
4ffab91adf
5 changed files with 112 additions and 42 deletions
|
|
@ -25,6 +25,9 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
|
||||||
cli "gopkg.in/urfave/cli.v1"
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
|
@ -160,7 +163,32 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||||
utils.RegisterEthService(stack, &cfg.Eth)
|
utils.RegisterEthService(stack, &cfg.Eth)
|
||||||
|
|
||||||
if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {
|
if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {
|
||||||
utils.RegisterDashboardService(stack, &cfg.Dashboard, gitCommit)
|
// There is a data race between the network layer and the dashboard, which
|
||||||
|
// can cause some lost peer events, therefore some peers might not appear
|
||||||
|
// on the dashboard.
|
||||||
|
// In order to solve this problem, a peer event subscription is registered
|
||||||
|
// before the network layer starts, and when the dashboard is ready, the
|
||||||
|
// stored events are passed to it.
|
||||||
|
peerEventBridge := make(chan p2p.MeteredPeerEvent, 1000) // The events are stored by and passed through this channel.
|
||||||
|
closePeerEventBridge := make(chan struct{}) // This channel gets a signal from the dashboard when it is ready.
|
||||||
|
go func() {
|
||||||
|
peerCh := make(chan p2p.MeteredPeerEvent, 200) // Channel for the initial peer events.
|
||||||
|
subPeer := p2p.SubscribeMeteredPeerEvent(peerCh) // Subscribe to the peer events.
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event := <-peerCh:
|
||||||
|
select {
|
||||||
|
case peerEventBridge <- event:
|
||||||
|
default:
|
||||||
|
log.Warn("Too many peer events before the dashboard starts")
|
||||||
|
}
|
||||||
|
case <-closePeerEventBridge:
|
||||||
|
subPeer.Unsubscribe()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
utils.RegisterDashboardService(stack, &cfg.Dashboard, gitCommit, peerEventBridge, closePeerEventBridge)
|
||||||
}
|
}
|
||||||
// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
|
// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
|
||||||
shhEnabled := enableWhisper(ctx)
|
shhEnabled := enableWhisper(ctx)
|
||||||
|
|
|
||||||
|
|
@ -1461,9 +1461,9 @@ func RegisterEthService(stack *node.Node, cfg *eth.Config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterDashboardService adds a dashboard to the stack.
|
// RegisterDashboardService adds a dashboard to the stack.
|
||||||
func RegisterDashboardService(stack *node.Node, cfg *dashboard.Config, commit string) {
|
func RegisterDashboardService(stack *node.Node, cfg *dashboard.Config, commit string, peerEventBridge chan p2p.MeteredPeerEvent, closePeerEventBridge chan struct{}) {
|
||||||
stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||||
return dashboard.New(cfg, commit, ctx.ResolvePath("logs")), nil
|
return dashboard.New(cfg, commit, ctx.ResolvePath("logs"), peerEventBridge, closePeerEventBridge), nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,9 @@ type Dashboard struct {
|
||||||
|
|
||||||
quit chan chan error // Channel used for graceful exit
|
quit chan chan error // Channel used for graceful exit
|
||||||
wg sync.WaitGroup // Wait group used to close the data collector threads
|
wg sync.WaitGroup // Wait group used to close the data collector threads
|
||||||
|
|
||||||
|
peerEventBridge chan p2p.MeteredPeerEvent // Channel for the initial peer events.
|
||||||
|
closePeerEventBridge chan struct{} // Channel to signal, that the initial peer event collection can be stopped.
|
||||||
}
|
}
|
||||||
|
|
||||||
// client represents active websocket connection with a remote browser.
|
// client represents active websocket connection with a remote browser.
|
||||||
|
|
@ -77,7 +80,7 @@ type client struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new dashboard instance with the given configuration.
|
// New creates a new dashboard instance with the given configuration.
|
||||||
func New(config *Config, commit string, logdir string) *Dashboard {
|
func New(config *Config, commit string, logdir string, peerEventBridge chan p2p.MeteredPeerEvent, closePeerEventBridge chan struct{}) *Dashboard {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
versionMeta := ""
|
versionMeta := ""
|
||||||
if len(params.VersionMeta) > 0 {
|
if len(params.VersionMeta) > 0 {
|
||||||
|
|
@ -104,6 +107,8 @@ func New(config *Config, commit string, logdir string) *Dashboard {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
logdir: logdir,
|
logdir: logdir,
|
||||||
|
peerEventBridge: peerEventBridge,
|
||||||
|
closePeerEventBridge: closePeerEventBridge,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -380,10 +380,6 @@ func (db *Dashboard) collectPeerData() {
|
||||||
}
|
}
|
||||||
defer db.geodb.close()
|
defer db.geodb.close()
|
||||||
|
|
||||||
peerCh := make(chan p2p.MeteredPeerEvent, eventBufferLimit) // Peer event channel.
|
|
||||||
subPeer := p2p.SubscribeMeteredPeerEvent(peerCh) // Subscribe to peer events.
|
|
||||||
defer subPeer.Unsubscribe() // Unsubscribe at the end.
|
|
||||||
|
|
||||||
ticker := time.NewTicker(db.config.Refresh)
|
ticker := time.NewTicker(db.config.Refresh)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
|
@ -428,9 +424,8 @@ func (db *Dashboard) collectPeerData() {
|
||||||
ingress, egress := new(trafficMap), new(trafficMap)
|
ingress, egress := new(trafficMap), new(trafficMap)
|
||||||
*ingress, *egress = make(trafficMap), make(trafficMap)
|
*ingress, *egress = make(trafficMap), make(trafficMap)
|
||||||
|
|
||||||
for {
|
// handlePeerEvent handles a metered peer event.
|
||||||
select {
|
handlePeerEvent := func(event p2p.MeteredPeerEvent) {
|
||||||
case event := <-peerCh:
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
switch event.Type {
|
switch event.Type {
|
||||||
case p2p.PeerConnected:
|
case p2p.PeerConnected:
|
||||||
|
|
@ -465,6 +460,43 @@ func (db *Dashboard) collectPeerData() {
|
||||||
default:
|
default:
|
||||||
log.Error("Unknown metered peer event type", "type", event.Type)
|
log.Error("Unknown metered peer event type", "type", event.Type)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
peerCh := make(chan p2p.MeteredPeerEvent, eventBufferLimit) // Peer event channel.
|
||||||
|
subPeer := p2p.SubscribeMeteredPeerEvent(peerCh) // Subscribe to peer events.
|
||||||
|
defer subPeer.Unsubscribe() // Unsubscribe at the end.
|
||||||
|
|
||||||
|
firstEvent := true // denotes whether the delivered peer event is the first one for the dashboard.
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event := <-peerCh:
|
||||||
|
if firstEvent {
|
||||||
|
// There is a data race between the network layer and the dashboard, which
|
||||||
|
// can cause some lost peer events, therefore some peers might not appear
|
||||||
|
// on the dashboard.
|
||||||
|
// In order to solve this problem, a peer event subscription is registered
|
||||||
|
// before the network layer starts, and when the dashboard is ready, the
|
||||||
|
// stored events are passed to it.
|
||||||
|
//
|
||||||
|
// In order to synchronize the two subscriptions, the stored events are
|
||||||
|
// processed until the first event of the dashboard is found. After that
|
||||||
|
// all the events will be delivered to the dashboard too.
|
||||||
|
sync:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case e := <-db.peerEventBridge:
|
||||||
|
if event.Equal(e) {
|
||||||
|
db.closePeerEventBridge <- struct{}{}
|
||||||
|
break sync
|
||||||
|
}
|
||||||
|
handlePeerEvent(e)
|
||||||
|
default: // There were no events before the dashboard started.
|
||||||
|
break sync
|
||||||
|
}
|
||||||
|
}
|
||||||
|
firstEvent = false
|
||||||
|
}
|
||||||
|
handlePeerEvent(event)
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
// Collect the traffic samples from the registry.
|
// Collect the traffic samples from the registry.
|
||||||
p2p.PeerIngressRegistry.Each(collectIngress(ingress))
|
p2p.PeerIngressRegistry.Each(collectIngress(ingress))
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,11 @@ type MeteredPeerEvent struct {
|
||||||
Egress uint64 // Egress count at the moment of the event
|
Egress uint64 // Egress count at the moment of the event
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Equal reports whether event and e are equal.
|
||||||
|
func (event *MeteredPeerEvent) Equal(e MeteredPeerEvent) bool {
|
||||||
|
return event.Type == e.Type && event.IP.Equal(e.IP) && event.ID == e.ID && event.Elapsed == e.Elapsed && event.Ingress == e.Ingress && event.Egress == e.Egress
|
||||||
|
}
|
||||||
|
|
||||||
// SubscribeMeteredPeerEvent registers a subscription for peer life-cycle events
|
// SubscribeMeteredPeerEvent registers a subscription for peer life-cycle events
|
||||||
// if metrics collection is enabled.
|
// if metrics collection is enabled.
|
||||||
func SubscribeMeteredPeerEvent(ch chan<- MeteredPeerEvent) event.Subscription {
|
func SubscribeMeteredPeerEvent(ch chan<- MeteredPeerEvent) event.Subscription {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue