diff --git a/plugins/analysis/dashboard/recorded_events.go b/plugins/analysis/dashboard/recorded_events.go index d0174bc7b456469bbb84529fa38c341d8462d239..2d1750672bd8f126d98fd1bdbfdf61a33f9125de 100644 --- a/plugins/analysis/dashboard/recorded_events.go +++ b/plugins/analysis/dashboard/recorded_events.go @@ -5,6 +5,7 @@ import ( "sync" "time" + "github.com/iotaledger/goshimmer/packages/graph" "github.com/iotaledger/goshimmer/packages/shutdown" "github.com/iotaledger/goshimmer/plugins/analysis/packet" analysisserver "github.com/iotaledger/goshimmer/plugins/analysis/server" @@ -21,9 +22,57 @@ var ( nodes = make(map[string]time.Time) // maps nodeId to outgoing connections + latest arrival of heartbeat. links = make(map[string]map[string]time.Time) - lock sync.Mutex + lock sync.RWMutex ) +type NeighborMetric struct { + Inbound uint + Outbound uint +} + +// NumOfNeighbors returns a map of nodeId-s to their neighbor count. +func NumOfNeighbors() map[string]*NeighborMetric { + lock.RLock() + defer lock.RUnlock() + result := make(map[string]*NeighborMetric) + for nodeID, _ := range nodes { + // number of outgoing neighbors + if _, exist := result[nodeID]; !exist { + result[nodeID] = &NeighborMetric{Outbound: uint(len(links[nodeID]))} + } else { + result[nodeID].Outbound = uint(len(links[nodeID])) + } + + // fill in incoming neighbors + for outNeighborID, _ := range links[nodeID] { + if _, exist := result[outNeighborID]; !exist { + result[outNeighborID] = &NeighborMetric{Inbound: 1} + } else { + result[outNeighborID].Inbound++ + } + } + } + return result +} + +// NetworkGraph returns a graph. +func NetworkGraph() *graph.Graph { + lock.RLock() + defer lock.RUnlock() + var nodeIDs []string + for id, _ := range nodes { + nodeIDs = append(nodeIDs, id) + } + g := graph.New(nodeIDs) + + for src, trgMap := range links { + for dst, _ := range trgMap { + g.AddEdge(src, dst) + } + } + return g +} + // configures the event recording by attaching to the analysis server's events. func configureEventsRecording() { analysisserver.Events.Heartbeat.Attach(events.NewClosure(func(hb *packet.Heartbeat) { @@ -156,8 +205,8 @@ func cleanUp(interval time.Duration) { } func getEventsToReplay() (map[string]time.Time, map[string]map[string]time.Time) { - lock.Lock() - defer lock.Unlock() + lock.RLock() + defer lock.RUnlock() copiedNodes := make(map[string]time.Time, len(nodes)) for nodeID, lastHeartbeat := range nodes { diff --git a/plugins/metrics/clients_info.go b/plugins/metrics/clients_info.go index 68327c15f47cc080e8e2d8545032f964c1dc48dc..9cd84ad5259c37a61cb0e06ae8c7fce04c31d87e 100644 --- a/plugins/metrics/clients_info.go +++ b/plugins/metrics/clients_info.go @@ -1,11 +1,14 @@ package metrics import ( + "fmt" "sync" + analysisdashboard "github.com/iotaledger/goshimmer/plugins/analysis/dashboard" "github.com/iotaledger/goshimmer/plugins/analysis/packet" "github.com/iotaledger/hive.go/events" "github.com/mr-tron/base58/base58" + "go.uber.org/atomic" ) type ClientInfo struct { @@ -19,6 +22,7 @@ type ClientInfo struct { var ( clientsMetrics = make(map[string]ClientInfo) clientsMetricsMutex sync.RWMutex + networkDiameter atomic.Int32 ) var onMetricHeartbeatReceived = events.NewClosure(func(hb *packet.MetricHeartbeat) { @@ -45,3 +49,15 @@ func ClientsMetrics() map[string]ClientInfo { } return copy } + +func calculateNetworkDiameter() { + g := analysisdashboard.NetworkGraph() + diameter := g.Diameter() + networkDiameter.Store(int32(diameter)) + fmt.Println("Calculated network diameter: ", diameter) +} + +// NetworkDiameter returns the current network diameter. +func NetworkDiameter() int32 { + return networkDiameter.Load() +} diff --git a/plugins/metrics/plugin.go b/plugins/metrics/plugin.go index 23b883430cfc0f6a82aa3ec260730c2ea8ab6672..3f70832a40e36a040b5b1a6da19939f07291bb90 100644 --- a/plugins/metrics/plugin.go +++ b/plugins/metrics/plugin.go @@ -66,6 +66,10 @@ func run(_ *node.Plugin) { gossipCurrentTx.Store(uint64(g.BytesWritten)) }, 1*time.Second, shutdownSignal) } + if config.Node.GetBool(CfgMetricsGlobal) { + timeutil.Ticker(calculateNetworkDiameter, 1 * time.Minute, shutdownSignal) + } + }, shutdown.PriorityMetrics); err != nil { log.Panicf("Failed to start as daemon: %s", err) } diff --git a/plugins/prometheus/clients_info.go b/plugins/prometheus/clients_info.go index 5d2133282cfd33387f33d5463f99fe75ed2ba49d..49841f768508444adef11819cb1d00e581ea2f44 100644 --- a/plugins/prometheus/clients_info.go +++ b/plugins/prometheus/clients_info.go @@ -3,6 +3,7 @@ package prometheus import ( "strconv" + analysisdashboard "github.com/iotaledger/goshimmer/plugins/analysis/dashboard" "github.com/iotaledger/goshimmer/plugins/metrics" "github.com/prometheus/client_golang/prometheus" ) @@ -10,6 +11,9 @@ import ( var ( clientsInfoCPU *prometheus.GaugeVec clientsInfoMemory *prometheus.GaugeVec + + clientsNeighborCount *prometheus.GaugeVec + networkDiameter prometheus.Gauge ) func registerClientsMetrics() { @@ -39,8 +43,26 @@ func registerClientsMetrics() { }, ) + clientsNeighborCount = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "clients_neighbor_count", + Help: "Info about client's memory usage labeled with nodeID, OS, ARCH and number of cpu cores", + }, + []string{ + "nodeID", + "direction", + }, + ) + + networkDiameter = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "clients_network_diameter", + Help: "Autopeering network diameter", + }) + registry.MustRegister(clientsInfoCPU) registry.MustRegister(clientsInfoMemory) + registry.MustRegister(clientsNeighborCount) + registry.MustRegister(networkDiameter) addCollect(collectClientsInfo) } @@ -62,4 +84,10 @@ func collectClientsInfo() { strconv.Itoa(clientInfo.NumCPU), ).Set(float64(clientInfo.MemoryUsage)) } + + for nodeID, neighborCount := range analysisdashboard.NumOfNeighbors() { + clientsNeighborCount.WithLabelValues(nodeID, "in").Set(float64(neighborCount.Inbound)) + clientsNeighborCount.WithLabelValues(nodeID, "out").Set(float64(neighborCount.Inbound)) + } + networkDiameter.Set(float64(metrics.NetworkDiameter())) }