Skip to content

Commit

Permalink
feat: TPS and average round times
Browse files Browse the repository at this point in the history
  • Loading branch information
PhearZero committed Oct 22, 2024
1 parent 90e12f0 commit 16291f5
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 46 deletions.
24 changes: 24 additions & 0 deletions internal/block.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package internal

import (
"context"
"errors"
"github.com/algorandfoundation/hack-tui/api"
)

func GetBlock(ctx context.Context, client *api.ClientWithResponses, round uint64) (map[string]interface{}, error) {

var format api.GetBlockParamsFormat = "json"
block, err := client.GetBlockWithResponse(ctx, int(round), &api.GetBlockParams{
Format: &format,
})
if err != nil {
return nil, err
}

if block.StatusCode() != 200 {
return nil, errors.New("invalid status code")
}

return block.JSON200.Block, nil
}
85 changes: 41 additions & 44 deletions internal/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,12 @@ type StateModel struct {
Watching bool
}

func getAverage(data []float64) float64 {
sum := 0.0
for _, element := range data {
sum += element
}
return sum / (float64(len(data)))
}
func getAverageDuration(timings []time.Duration) time.Duration {
sum := 0.0
for _, element := range timings {
sum += element.Seconds()
}
avg := sum / (float64(len(timings)))
return time.Duration(avg * float64(time.Second))
}

// TODO: allow context to handle loop
func (s *StateModel) Watch(cb func(model *StateModel, err error), ctx context.Context, client *api.ClientWithResponses) {
s.Watching = true
if s.Metrics.Window == 0 {
s.Metrics.Window = 100
}

err := s.Status.Fetch(ctx, client)
if err != nil {
Expand All @@ -44,28 +31,20 @@ func (s *StateModel) Watch(cb func(model *StateModel, err error), ctx context.Co

lastRound := s.Status.LastRound

// Collection of Round Durations
timings := make([]time.Duration, 0)
// Collection of Transaction Counts
txns := make([]float64, 0)

for {
if !s.Watching {
break
}
// Collect Time of Round
startTime := time.Now()
status, err := client.WaitForBlockWithResponse(ctx, int(lastRound))
if err != nil {
cb(nil, err)
}
if status.StatusCode() != 200 {
cb(nil, errors.New(status.Status()))
}
// Store round timing
endTime := time.Now()
dur := endTime.Sub(startTime)
timings = append(timings, dur)

// Update Status
s.Status.LastRound = uint64(status.JSON200.LastRound)
Expand All @@ -82,22 +61,34 @@ func (s *StateModel) Watch(cb func(model *StateModel, err error), ctx context.Co
cb(nil, err)
}

// Check for transactions
if block.JSON200.Block["txns"] != nil {
// Get the average duration in seconds (TPS)
txnCount := float64(len(block.JSON200.Block["txns"].([]any)))
txns = append(txns, txnCount/getAverageDuration(timings).Seconds())
} else {
txns = append(txns, 0)
// Push to the transactions count list
txnsValue := block.JSON200.Block["txns"]
if txnsValue == nil {
txns = append(txns, 0.0)
}
if txnsValue != nil {
txns = append(txns, float64(len(txnsValue.([]interface{}))))
}

// Fetch RX/TX every 5th round
// Run Round Averages and RX/TX every 5 rounds
if s.Status.LastRound%5 == 0 {
s.UpdateMetrics(ctx, client, timings, txns)
s.UpdateMetricsFromRPC(ctx, client)
err := s.UpdateRoundTime(ctx, client, time.Duration(block.JSON200.Block["ts"].(float64))*time.Second)
if err != nil {
cb(nil, err)
}
}
txnSum := 0.0
for i := 0; i < len(txns); i++ {
txnSum += txns[i]
}
txnAvg := txnSum / float64(len(txns))
if s.Metrics.RoundTime != 0 {
s.Metrics.TPS = txnAvg / s.Metrics.RoundTime.Seconds()
}

// Trim data
if len(timings) >= 100 {
timings = timings[1:]
if len(txns) >= s.Metrics.Window {
txns = txns[1:]
}

Expand All @@ -110,20 +101,27 @@ func (s *StateModel) Stop() {
s.Watching = false
}

func (s *StateModel) UpdateMetrics(
func (s *StateModel) UpdateRoundTime(
ctx context.Context,
client *api.ClientWithResponses,
timings []time.Duration,
txns []float64,
) {
timestamp time.Duration,
) error {
if s == nil {
panic("StateModel is nil while UpdateMetrics is called")
}
// Set Metrics
s.Metrics.RoundTime = getAverageDuration(timings)
s.Metrics.Window = len(timings)
s.Metrics.TPS = getAverage(txns)
previousRound := s.Status.LastRound - uint64(s.Metrics.Window)
previousBlock, err := GetBlock(ctx, client, previousRound)
if err != nil {
s.Metrics.Enabled = false
return err
}
previousBlockTs := time.Duration(previousBlock["ts"].(float64)) * time.Second

s.Metrics.RoundTime = time.Duration(int(timestamp-previousBlockTs) / s.Metrics.Window)
return nil
}

func (s *StateModel) UpdateMetricsFromRPC(ctx context.Context, client *api.ClientWithResponses) {
// Fetch RX/TX
res, err := GetMetrics(ctx, client)
if err != nil {
Expand All @@ -135,7 +133,6 @@ func (s *StateModel) UpdateMetrics(
s.Metrics.RX = res["algod_network_received_bytes_total"]
}
}

func (s *StateModel) UpdateAccounts() {
s.Accounts = AccountsFromState(s)
}
Expand Down
3 changes: 1 addition & 2 deletions internal/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (

// StatusModel represents a status response from algod.Status
type StatusModel struct {
Metrics MetricsModel
State string
Version string
Network string
Expand All @@ -19,7 +18,7 @@ type StatusModel struct {

// String prints the last round value
func (m *StatusModel) String() string {
return fmt.Sprintf("\nLastRound: %d\nRoundTime: %f \nTPS: %f", m.LastRound, m.Metrics.RoundTime.Seconds(), m.Metrics.TPS)
return fmt.Sprintf("\nLastRound: %d\n", m.LastRound)
}

// Fetch handles algod.Status
Expand Down

0 comments on commit 16291f5

Please sign in to comment.