Skip to content

Commit cd24f5d

Browse files
authored
Merge pull request #7227 from multiversx/termui-new-metrics
MX-17032 Termui update - added metrics
2 parents 7ca6157 + 24e1310 commit cd24f5d

24 files changed

+954
-48
lines changed

cmd/termui/presenter/blockInfoGetters.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,23 @@ func (psh *PresenterStatusHandler) GetBlockSize() uint64 {
5656
func (psh *PresenterStatusHandler) GetHighestFinalBlock() uint64 {
5757
return psh.getFromCacheAsUint64(common.MetricHighestFinalBlock)
5858
}
59+
60+
// GetDurationProposedBlockReceivedOrSentFromRoundStart returns the metrics containing time taken to receive a proposed block body since the round started
61+
func (psh *PresenterStatusHandler) GetDurationProposedBlockReceivedOrSentFromRoundStart() uint64 {
62+
return psh.getFromCacheAsUint64(common.MetricReceivedOrSentProposedBlock)
63+
}
64+
65+
// GetDurationProofReceivedFromProposedBlockReceivedOrSent returns the metrics containing time taken to receive signatures for a block since the block was received
66+
func (psh *PresenterStatusHandler) GetDurationProofReceivedFromProposedBlockReceivedOrSent() uint64 {
67+
return psh.getFromCacheAsUint64(common.MetricReceivedProof)
68+
}
69+
70+
// GetAvgDurationProposedBlockReceivedOrSentFromRoundStart returns the average received proposed block body metric
71+
func (psh *PresenterStatusHandler) GetAvgDurationProposedBlockReceivedOrSentFromRoundStart() uint64 {
72+
return psh.getFromCacheAsUint64(common.MetricAvgReceivedOrSentProposedBlock)
73+
}
74+
75+
// GetAvgDurationProofReceivedFromProposedBlockReceivedOrSent returns the average received signatures metric from the time of block body received
76+
func (psh *PresenterStatusHandler) GetAvgDurationProofReceivedFromProposedBlockReceivedOrSent() uint64 {
77+
return psh.getFromCacheAsUint64(common.MetricAvgReceivedProof)
78+
}

cmd/termui/presenter/blockInfoGetters_test.go

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -125,18 +125,47 @@ func TestPresenterStatusHandler_GetCurrentRoundTimestamp(t *testing.T) {
125125
assert.Equal(t, currentRoundTimestamp, result)
126126
}
127127

128-
func TestPresenterStatusHandler_GetBlockSize(t *testing.T) {
128+
func TestPresenterStatusHandler_GetBlockReceived(t *testing.T) {
129129
t.Parallel()
130130

131-
miniBlocksSize := uint64(100)
132-
headerSize := uint64(50)
131+
proposedBlockMs := uint64(100)
132+
133133
presenterStatusHandler := NewPresenterStatusHandler()
134-
presenterStatusHandler.SetUInt64Value(common.MetricMiniBlocksSize, miniBlocksSize)
135-
presenterStatusHandler.SetUInt64Value(common.MetricHeaderSize, headerSize)
136-
result := presenterStatusHandler.GetBlockSize()
134+
presenterStatusHandler.SetUInt64Value(common.MetricReceivedOrSentProposedBlock, proposedBlockMs)
135+
result := presenterStatusHandler.GetDurationProposedBlockReceivedOrSentFromRoundStart()
136+
assert.Equal(t, proposedBlockMs, result)
137+
}
137138

138-
blockExpectedSize := miniBlocksSize + headerSize
139-
assert.Equal(t, blockExpectedSize, result)
139+
func TestPresenterStatusHandler_GetAvgBlockReceived(t *testing.T) {
140+
t.Parallel()
141+
142+
proposedBlockMs := uint64(100)
143+
144+
presenterStatusHandler := NewPresenterStatusHandler()
145+
presenterStatusHandler.SetUInt64Value(common.MetricAvgReceivedOrSentProposedBlock, proposedBlockMs)
146+
result := presenterStatusHandler.GetAvgDurationProposedBlockReceivedOrSentFromRoundStart()
147+
assert.Equal(t, proposedBlockMs, result)
148+
}
149+
150+
func TestPresenterStatusHandler_GetProofReceived(t *testing.T) {
151+
t.Parallel()
152+
153+
proofMs := uint64(100)
154+
155+
presenterStatusHandler := NewPresenterStatusHandler()
156+
presenterStatusHandler.SetUInt64Value(common.MetricReceivedProof, proofMs)
157+
result := presenterStatusHandler.GetDurationProofReceivedFromProposedBlockReceivedOrSent()
158+
assert.Equal(t, proofMs, result)
159+
}
160+
func TestPresenterStatusHandler_GetAvgProofReceived(t *testing.T) {
161+
t.Parallel()
162+
163+
proofMs := uint64(100)
164+
165+
presenterStatusHandler := NewPresenterStatusHandler()
166+
presenterStatusHandler.SetUInt64Value(common.MetricAvgReceivedProof, proofMs)
167+
result := presenterStatusHandler.GetAvgDurationProofReceivedFromProposedBlockReceivedOrSent()
168+
assert.Equal(t, proofMs, result)
140169
}
141170

142171
func TestPresenterStatusHandler_GetHighestFinalBlock(t *testing.T) {
@@ -149,3 +178,17 @@ func TestPresenterStatusHandler_GetHighestFinalBlock(t *testing.T) {
149178

150179
assert.Equal(t, highestFinalBlockNonce, result)
151180
}
181+
182+
func TestPresenterStatusHandler_GetBlockSize(t *testing.T) {
183+
t.Parallel()
184+
185+
miniBlocksSize := uint64(100)
186+
headerSize := uint64(50)
187+
presenterStatusHandler := NewPresenterStatusHandler()
188+
presenterStatusHandler.SetUInt64Value(common.MetricMiniBlocksSize, miniBlocksSize)
189+
presenterStatusHandler.SetUInt64Value(common.MetricHeaderSize, headerSize)
190+
result := presenterStatusHandler.GetBlockSize()
191+
192+
blockExpectedSize := miniBlocksSize + headerSize
193+
assert.Equal(t, blockExpectedSize, result)
194+
}

cmd/termui/provider/logProvider.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"github.com/multiversx/mx-chain-core-go/marshal"
1212
"github.com/multiversx/mx-chain-go/common"
1313
"github.com/multiversx/mx-chain-go/node"
14-
"github.com/multiversx/mx-chain-logger-go"
14+
logger "github.com/multiversx/mx-chain-logger-go"
1515
)
1616

1717
var formatter = logger.PlainFormatter{}
@@ -48,14 +48,18 @@ func InitLogHandler(args LogHandlerArgs) error {
4848

4949
var err error
5050
scheme := ws
51+
5152
if args.UseWss {
5253
scheme = wss
5354
}
55+
nodeURL := getNodeUrlPath(args.NodeURL)
56+
5457
go func() {
5558
for {
56-
webSocket, err = openWebSocket(scheme, args.NodeURL)
59+
webSocket, err = openWebSocket(scheme, nodeURL)
60+
5761
if err != nil {
58-
_, _ = args.Presenter.Write([]byte(fmt.Sprintf("termui websocket error, retrying in %v...", retryDuration)))
62+
_, _ = args.Presenter.Write([]byte(fmt.Sprintf("termui websocket error %s, retrying in %v...", err, retryDuration)))
5963
time.Sleep(retryDuration)
6064
continue
6165
}
@@ -75,12 +79,17 @@ func InitLogHandler(args LogHandlerArgs) error {
7579
return nil
7680
}
7781

82+
func getNodeUrlPath(nodeURL string) string {
83+
return strings.TrimPrefix(strings.TrimPrefix(nodeURL, "http://"), "https://")
84+
}
85+
7886
func openWebSocket(scheme string, address string) (*websocket.Conn, error) {
7987
u := url.URL{
8088
Scheme: scheme,
8189
Host: address,
8290
Path: "/log",
8391
}
92+
8493
conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
8594
if err != nil {
8695
return nil, err

cmd/termui/view/interface.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ type Presenter interface {
1818
GetCountConsensusAcceptedBlocks() uint64
1919
GetCountLeader() uint64
2020
GetCountAcceptedBlocks() uint64
21+
GetDurationProposedBlockReceivedOrSentFromRoundStart() uint64
22+
GetDurationProofReceivedFromProposedBlockReceivedOrSent() uint64
23+
GetAvgDurationProposedBlockReceivedOrSentFromRoundStart() uint64
24+
GetAvgDurationProofReceivedFromProposedBlockReceivedOrSent() uint64
2125
GetIsSyncing() uint64
2226
GetTxPoolLoad() uint64
2327
GetNonce() uint64

cmd/termui/view/termuic/termuiRenders/drawableContainer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"github.com/gizak/termui/v3"
55
)
66

7-
const topHeight = 22
7+
const topHeight = 24
88

99
// DrawableContainer defines a container of drawable object with position and dimensions
1010
type DrawableContainer struct {

cmd/termui/view/termuic/termuiRenders/widgetsRender.go

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"github.com/multiversx/mx-chain-go/common"
1212
)
1313

14+
const conversionFactorToSeconds = 1e9
15+
1416
const (
1517
statusSyncing = "currently syncing"
1618
statusSynchronized = "synchronized"
@@ -90,12 +92,15 @@ func (wr *WidgetsRender) setGrid() {
9092
colMemoryLoad := ui.NewCol(1.0/2, wr.memoryLoad)
9193

9294
gridRight := ui.NewGrid()
95+
96+
progressBarElementRatio := 3.0 / topHeight
97+
blockSectionHeightRatio := 1 - 4*progressBarElementRatio
9398
gridRight.Set(
94-
ui.NewRow(10.0/22, wr.blockInfo),
95-
ui.NewRow(3.0/22, colCpuLoad, colMemoryLoad),
96-
ui.NewRow(3.0/22, wr.epochLoad),
97-
ui.NewRow(3.0/22, wr.networkBytesInEpoch),
98-
ui.NewRow(3.0/22, colNetworkSent, colNetworkRecv),
99+
ui.NewRow(blockSectionHeightRatio, wr.blockInfo),
100+
ui.NewRow(progressBarElementRatio, colCpuLoad, colMemoryLoad),
101+
ui.NewRow(progressBarElementRatio, wr.epochLoad),
102+
ui.NewRow(progressBarElementRatio, wr.networkBytesInEpoch),
103+
ui.NewRow(progressBarElementRatio, colNetworkSent, colNetworkRecv),
99104
)
100105

101106
gridBottom := ui.NewGrid()
@@ -294,51 +299,70 @@ func computeRedundancyStr(redundancyLevel int64, redundancyIsMainActive string)
294299
}
295300

296301
func (wr *WidgetsRender) prepareBlockInfo() {
297-
// 7 rows and one column
298-
numRows := 8
302+
// 8 rows and one column
303+
numRows := 9
299304
rows := make([][]string, numRows)
300305

301306
currentBlockHeight := wr.presenter.GetNonce()
302307
blockSize := wr.presenter.GetBlockSize()
303308
rows[0] = []string{fmt.Sprintf("Current block height: %d, size: %s", currentBlockHeight, core.ConvertBytes(blockSize))}
304309

305310
numTransactionInBlock := wr.presenter.GetNumTxInBlock()
306-
rows[1] = []string{fmt.Sprintf("Num transactions in block: %d", numTransactionInBlock)}
307-
308311
numMiniBlocks := wr.presenter.GetNumMiniBlocks()
309-
rows[2] = []string{fmt.Sprintf("Num miniblocks in block: %d", numMiniBlocks)}
312+
rows[1] = []string{fmt.Sprintf("Num transactions in block: %d | Num miniblocks in block: %d",
313+
numTransactionInBlock,
314+
numMiniBlocks,
315+
)}
310316

311317
currentBlockHash := wr.presenter.GetCurrentBlockHash()
312-
rows[3] = []string{fmt.Sprintf("Current block hash: %s", currentBlockHash)}
318+
rows[2] = []string{fmt.Sprintf("Current block hash: %s", currentBlockHash)}
313319

314320
crossCheckBlockHeight := wr.presenter.GetCrossCheckBlockHeight()
315-
rows[4] = []string{fmt.Sprintf("Cross check: %s", crossCheckBlockHeight)}
321+
rows[3] = []string{fmt.Sprintf("Cross check: %s", crossCheckBlockHeight)}
316322

317323
shardId := wr.presenter.GetShardId()
318324
if shardId != uint64(core.MetachainShardId) {
319325
highestFinalBlock := wr.presenter.GetHighestFinalBlock()
320-
rows[4][0] += fmt.Sprintf(", final nonce: %d", highestFinalBlock)
326+
rows[3][0] += fmt.Sprintf(", final nonce: %d", highestFinalBlock)
321327
}
322328

323329
consensusState := wr.presenter.GetConsensusState()
324-
rows[5] = []string{fmt.Sprintf("Consensus state: %s", consensusState)}
330+
rows[4] = []string{fmt.Sprintf("Consensus state: %s", consensusState)}
325331

326332
syncStatus := wr.presenter.GetIsSyncing()
327333
switch syncStatus {
328334
case 1:
329-
rows[6] = []string{"Consensus round state: N/A (syncing)"}
335+
rows[5] = []string{"Consensus round state: N/A (syncing)"}
330336
case 0:
331337
instanceType := wr.presenter.GetNodeType()
332338
if instanceType == string(core.NodeTypeObserver) {
333-
rows[6] = []string{fmt.Sprintf("Consensus round state: N/A (%s)", string(core.NodeTypeObserver))}
339+
rows[5] = []string{fmt.Sprintf("Consensus round state: N/A (%s)", string(core.NodeTypeObserver))}
334340
} else {
335341
consensusRoundState := wr.presenter.GetConsensusRoundState()
336-
rows[6] = []string{fmt.Sprintf("Consensus round state: %s", consensusRoundState)}
342+
rows[5] = []string{fmt.Sprintf("Consensus round state: %s", consensusRoundState)}
337343
}
338344
}
339345

340346
currentRoundTimestamp := wr.presenter.GetCurrentRoundTimestamp()
341-
rows[7] = []string{fmt.Sprintf("Current round timestamp: %d", currentRoundTimestamp)}
347+
rows[8] = []string{fmt.Sprintf("Current round timestamp: %d", currentRoundTimestamp)}
348+
349+
durationStartRoundToSentOrReceivedBlock := float64(wr.presenter.GetDurationProposedBlockReceivedOrSentFromRoundStart()) / conversionFactorToSeconds
350+
durationSentOrReceivedBlockToReceivedSignatures := float64(wr.presenter.GetDurationProofReceivedFromProposedBlockReceivedOrSent()) / conversionFactorToSeconds
351+
352+
rows[6] = []string{
353+
fmt.Sprintf("Received proposed block: %.6f sec | Received signatures: %.6f sec",
354+
durationStartRoundToSentOrReceivedBlock,
355+
durationSentOrReceivedBlockToReceivedSignatures),
356+
}
357+
358+
durationStartRoundToSentOrReceivedBlock = float64(wr.presenter.GetAvgDurationProposedBlockReceivedOrSentFromRoundStart()) / conversionFactorToSeconds
359+
durationSentOrReceivedBlockToReceivedSignatures = float64(wr.presenter.GetAvgDurationProofReceivedFromProposedBlockReceivedOrSent()) / conversionFactorToSeconds
360+
361+
rows[7] = []string{
362+
fmt.Sprintf("Avg Received proposed block: %.6f sec | Avg Received signatures: %.6f sec",
363+
durationStartRoundToSentOrReceivedBlock,
364+
durationSentOrReceivedBlockToReceivedSignatures),
365+
}
342366

343367
wr.blockInfo.Title = "Block info:"
344368
wr.blockInfo.RowSeparator = false

cmd/termui/view/termuic/termuiRenders/widgetsRender_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import (
44
"fmt"
55
"testing"
66

7+
"github.com/gizak/termui/v3/widgets"
8+
"github.com/multiversx/mx-chain-go/cmd/termui/presenter"
9+
"github.com/multiversx/mx-chain-go/common"
710
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
812
)
913

1014
func TestWidgetsRender_prepareLogLines(t *testing.T) {
@@ -43,3 +47,46 @@ func TestWidgetsRender_prepareLogLines(t *testing.T) {
4347
assert.Equal(t, 8, len(result))
4448
})
4549
}
50+
51+
func TestPrepareBlockInfo(t *testing.T) {
52+
presenterStatusHandler := presenter.NewPresenterStatusHandler()
53+
presenterStatusHandler.SetUInt64Value(common.MetricNonce, 42)
54+
presenterStatusHandler.SetUInt64Value(common.MetricMiniBlocksSize, 2000)
55+
presenterStatusHandler.SetUInt64Value(common.MetricHeaderSize, 48)
56+
presenterStatusHandler.SetUInt64Value(common.MetricNumTxInBlock, 5)
57+
presenterStatusHandler.SetUInt64Value(common.MetricNumMiniBlocks, 2)
58+
presenterStatusHandler.SetStringValue(common.MetricCurrentBlockHash, "hash")
59+
presenterStatusHandler.SetStringValue(common.MetricCrossCheckBlockHeight, "cross123")
60+
presenterStatusHandler.SetUInt64Value(common.MetricHighestFinalBlock, 99)
61+
presenterStatusHandler.SetUInt64Value(common.MetricShardId, 0) // not metachain
62+
presenterStatusHandler.SetStringValue(common.MetricConsensusState, "ProposedBlock")
63+
presenterStatusHandler.SetUInt64Value(common.MetricIsSyncing, 0)
64+
presenterStatusHandler.SetStringValue(common.MetricConsensusRoundState, "Success")
65+
presenterStatusHandler.SetUInt64Value(common.MetricCurrentRoundTimestamp, 123456)
66+
presenterStatusHandler.SetUInt64Value(common.MetricReceivedOrSentProposedBlock, 2_000_000) // ns
67+
presenterStatusHandler.SetUInt64Value(common.MetricReceivedProof, 3_000_000)
68+
presenterStatusHandler.SetUInt64Value(common.MetricAvgReceivedOrSentProposedBlock, 1_000_000)
69+
presenterStatusHandler.SetUInt64Value(common.MetricAvgReceivedProof, 1_500_000)
70+
71+
wr := &WidgetsRender{
72+
presenter: presenterStatusHandler,
73+
blockInfo: &widgets.Table{},
74+
}
75+
76+
wr.prepareBlockInfo()
77+
78+
require.Equal(t, "Block info:", wr.blockInfo.Title)
79+
require.False(t, wr.blockInfo.RowSeparator)
80+
require.Len(t, wr.blockInfo.Rows, 9)
81+
82+
// Example: check one row
83+
require.Contains(t, wr.blockInfo.Rows[0][0], fmt.Sprintf("Current block height: %d, size: %s", 42, "2.00 KB"))
84+
require.Contains(t, wr.blockInfo.Rows[2][0], "hash")
85+
require.Contains(t, wr.blockInfo.Rows[4][0], "Consensus state: ProposedBlock")
86+
require.Contains(t, wr.blockInfo.Rows[5][0], "Consensus round state: Success")
87+
require.Contains(t, wr.blockInfo.Rows[6][0], "Received proposed block: 0.002000 sec | Received signatures: 0.003000 sec")
88+
require.Contains(t, wr.blockInfo.Rows[7][0], "Avg Received proposed block: 0.001000 sec | Avg Received signatures: 0.001500 sec")
89+
require.Contains(t, wr.blockInfo.Rows[8][0], "Current round timestamp: 123456")
90+
require.Contains(t, wr.blockInfo.Rows[3][0], "Cross check: cross123, final nonce: 99")
91+
92+
}

common/constants.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,22 @@ const MetricNoncesPassedInCurrentEpoch = "erd_nonces_passed_in_current_epoch"
315315
// 100 meaning that the block has been received in the last moment of the round)
316316
const MetricReceivedProposedBlock = "erd_consensus_received_proposed_block"
317317

318+
// MetricReceivedOrSentProposedBlock is the metric that specifies the delay in nanoseconds from the start of the current round until
319+
// the time the proposed block body has been sent or has reached the current node.
320+
const MetricReceivedOrSentProposedBlock = "erd_consensus_received_or_sent_proposed_block"
321+
322+
// MetricReceivedProof is the metric that specifies the delay in nanoseconds between the time the proposed block has been sent
323+
// or has reached the current node until the proof was received.
324+
const MetricReceivedProof = "erd_consensus_received_proof"
325+
326+
// MetricAvgReceivedOrSentProposedBlock is the metric that specifies the average delay in nanoseconds from the start of the round until
327+
// the time the proposed block has been sent or has reached the current node.
328+
const MetricAvgReceivedOrSentProposedBlock = "erd_consensus_average_received_or_sent_proposed_block"
329+
330+
// MetricAvgReceivedProof is the metric that specifies the average delay in nanoseconds between the time the proposed block
331+
// has been sent or has reached the current node until proof was received.
332+
const MetricAvgReceivedProof = "erd_consensus_average_received_sent_proof"
333+
318334
// MetricCreatedProposedBlock is the metric that specifies the percent of the block subround used for header and body
319335
// creation (0 meaning that the block was created in no-time and 100 meaning that the block creation used all the
320336
// subround spare duration)

consensus/interface.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ type EquivalentProofsPool interface {
212212
GetProof(shardID uint32, headerHash []byte) (data.HeaderProofHandler, error)
213213
GetProofByNonce(headerNonce uint64, shardID uint32) (data.HeaderProofHandler, error)
214214
HasProof(shardID uint32, headerHash []byte) bool
215+
RegisterHandler(handler func(headerProof data.HeaderProofHandler))
215216
IsInterfaceNil() bool
216217
}
217218

consensus/spos/bls/v2/export_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,11 @@ func (sr *subroundBlock) GetLeaderForHeader(headerHandler data.HeaderHandler) ([
226226
return sr.getLeaderForHeader(headerHandler)
227227
}
228228

229+
// UpdateConsensusMetricsProposedBlockReceivedOrSent updates the metrics containing time taken to receive a proposed block body since the round started
230+
func (sr *subroundBlock) UpdateConsensusMetricsProposedBlockReceivedOrSent() {
231+
sr.updateConsensusMetricsProposedBlockReceivedOrSent()
232+
}
233+
229234
// subroundSignature
230235

231236
// SubroundSignature defines an alias to the subroundSignature structure
@@ -360,3 +365,8 @@ func (sr *subroundEndRound) GetEquivalentProofSender() string {
360365
func (sr *subroundEndRound) SendProof() (bool, error) {
361366
return sr.sendProof()
362367
}
368+
369+
// UpdateConsensusMetricsProof -
370+
func (sr *subroundEndRound) UpdateConsensusMetricsProof() {
371+
sr.updateConsensusMetricsProof()
372+
}

0 commit comments

Comments
 (0)