Skip to content

Commit

Permalink
Optimized dashboards rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
bartosian committed Jan 19, 2024
1 parent 3e0f7a5 commit 3a74663
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 140 deletions.
1 change: 1 addition & 0 deletions internal/core/domain/host/addressinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ func (addr *AddressInfo) GetUrlPrometheus() (string, error) {
return hostUrl.String(), nil
}

// getProtocol returns the protocol based on the secure flag.
func getProtocol(secure bool) string {
protocol := protocolHTTP
if secure {
Expand Down
18 changes: 11 additions & 7 deletions internal/core/domain/host/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,25 +78,29 @@ func (host *Host) SetStatus(rpc Host) {
case enums.TableTypeValidator:
if !metricsHost.Updated || metricsHost.Uptime == "" {
host.Status = enums.StatusRed

return
}
case enums.TableTypeNode, enums.TableTypeRPC:
if !metricsHost.Updated || metricsHost.TotalTransactionsBlocks == 0 || metricsHost.LatestCheckpoint == 0 ||
metricsHost.TransactionsPerSecond == 0 && len(metricsHost.TransactionsHistory) == metrics.TransactionsPerSecondWindow ||
metricsHost.TxSyncPercentage == 0 || metricsHost.TxSyncPercentage > 110 || metricsHost.CheckSyncPercentage > 110 {

case enums.TableTypeNode, enums.TableTypeRPC:
if !metricsHost.Updated {
host.Status = enums.StatusRed
return
}

if metricsHost.TotalTransactionsBlocks == 0 ||
metricsHost.LatestCheckpoint == 0 ||
(metricsHost.TransactionsPerSecond == 0 && len(metricsHost.TransactionsHistory) == metrics.TransactionsPerSecondWindow) ||
metricsHost.TxSyncPercentage == 0 ||
metricsHost.TxSyncPercentage > 110 ||
metricsHost.CheckSyncPercentage > 110 {
host.Status = enums.StatusRed
return
}

if metricsHost.IsUnhealthy(enums.MetricTypeTransactionsPerSecond, metricsRPC.TransactionsPerSecond) ||
metricsHost.IsUnhealthy(enums.MetricTypeTotalTransactionBlocks, metricsRPC.TotalTransactionsBlocks) ||
metricsHost.IsUnhealthy(enums.MetricTypeLatestCheckpoint, metricsRPC.LatestCheckpoint) {

host.Status = enums.StatusYellow

return
}
}
Expand Down
1 change: 0 additions & 1 deletion internal/core/domain/service/dashboardbuilder/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
// new dashboard using the `container.New()` method. The dashboard instance is
// stored in the `db.dashboard` field for later use.
func (db *Builder) Init() (err error) {
// Use a deferred function to call db.TearDown() if there were errors or panics
defer func() {
if err != nil {
db.tearDown()
Expand Down
99 changes: 62 additions & 37 deletions internal/core/domain/service/dashboardbuilder/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,51 +16,76 @@ const (
queryInterval = 2500 * time.Millisecond
)

// Render displays the dashboard on the terminal and updates the cells with new data periodically.
// Render renders the dashboard by starting the query and rerender loops,
// and waiting for them to complete. It returns an error if any of the loops
// encounter an error.
func (db *Builder) Render() (err error) {
// Use a deferred function to call db.TearDown() if there were errors or panics
defer func() {
if err != nil {
if r := recover(); r != nil {
db.cliGateway.Error(fmt.Sprintf("panic: %v", r))
db.tearDown()
}

if err := recover(); err != nil {
// Handle the panic by logging the error and exiting the program
db.tearDown()

db.cliGateway.Error(fmt.Sprintf("panic: %v", err))

os.Exit(1)
} else if err != nil {
db.tearDown()
}
}()

var errGroup errgroup.Group

tickerQuery := time.NewTicker(queryInterval)
defer tickerQuery.Stop()
queryTicker, renderTicker := startTickers()
defer stopTickers(queryTicker, renderTicker)

errGroup.Go(queryMetricsLoop(db, queryTicker))
errGroup.Go(rerenderLoop(db, renderTicker))
errGroup.Go(runDashboard(db))

return errGroup.Wait()
}

func startTickers() (queryTicker *time.Ticker, renderTicker *time.Ticker) {
queryTicker = time.NewTicker(queryInterval)
renderTicker = time.NewTicker(renderInterval)
return
}

// Start a goroutine for the metric retrieval loop
errGroup.Go(func() error {
func stopTickers(tickers ...*time.Ticker) {
for _, ticker := range tickers {
ticker.Stop()
}
}

// queryMetricsLoop fetches the metrics from the host at regular intervals.
// It uses the provided ticker to trigger the fetch and returns an error if
// the fetch encounters an error or if the context is done.
// It returns a function that can be used to start the loop.
// The loop can be stopped by canceling the context.
// The function signature is compatible with the errgroup.Group.Go method.
// The loop stops when the context is done.
func queryMetricsLoop(db *Builder, ticker *time.Ticker) func() error {
return func() error {
for {
select {
case <-tickerQuery.C:
case <-ticker.C:
if err := db.host.GetMetrics(); err != nil {
return err
}
case <-db.ctx.Done():
return nil
}
}
})

tickerRerender := time.NewTicker(renderInterval)
defer tickerRerender.Stop()
}
}

// Start a goroutine for the dashboard rendering loop
errGroup.Go(func() error {
// rerenderLoop continuously fetches the latest column values from the host at regular intervals.
// It uses the provided ticker to trigger the fetch and updates the cells with the latest values.
// The loop stops when the context is done.
// The function signature is compatible with the errgroup.Group.Go method.
// It returns a function that can be used to start the loop.
func rerenderLoop(db *Builder, ticker *time.Ticker) func() error {
return func() error {
for {
select {
case <-tickerRerender.C:
case <-ticker.C:
columnValues, err := dashboards.GetColumnsValues(db.tableType, db.host)
if err != nil {
return err
Expand All @@ -80,19 +105,19 @@ func (db *Builder) Render() (err error) {
return nil
}
}
})

errGroup.Go(func() error {
// Display the dashboard on the terminal and handle errors
if err := termdash.Run(
db.ctx, db.terminal, db.dashboard,
termdash.KeyboardSubscriber(db.quitter),
); err != nil {
return fmt.Errorf("failed to run terminal dashboard: %w", err)
}

return nil
})
}
}

return errGroup.Wait()
// runDashboard runs the dashboard using termdash.Run method.
// It takes a Builder instance as input and returns a function that can be used to start the dashboard.
// The returned function can be used to start the dashboard and handle keyboard events using the provided quitter function.
// It returns an error if the dashboard fails to run.
// The dashboard is run using the termdash.Run method with the provided context, terminal, dashboard, and keyboard subscriber.
// The returned error indicates any failure during the dashboard run.
// The function signature is compatible with the errgroup.Group.Go method.
// It returns a function that can be used to start the dashboard.
func runDashboard(db *Builder) func() error {
return func() error {
return termdash.Run(db.ctx, db.terminal, db.dashboard, termdash.KeyboardSubscriber(db.quitter))
}
}
Loading

0 comments on commit 3a74663

Please sign in to comment.