1
- use std:: { collections:: BTreeMap , sync:: Arc } ;
1
+ use std:: {
2
+ collections:: { BTreeMap , BTreeSet } ,
3
+ sync:: Arc ,
4
+ } ;
2
5
3
6
use async_broadcast:: { Receiver , Sender } ;
4
7
use async_trait:: async_trait;
@@ -7,7 +10,11 @@ use hotshot_task::task::TaskState;
7
10
use hotshot_types:: {
8
11
consensus:: OuterConsensus ,
9
12
epoch_membership:: EpochMembershipCoordinator ,
10
- traits:: node_implementation:: { ConsensusTime , NodeType } ,
13
+ traits:: {
14
+ block_contents:: BlockHeader ,
15
+ node_implementation:: { ConsensusTime , NodeType } ,
16
+ BlockPayload ,
17
+ } ,
11
18
vote:: HasViewNumber ,
12
19
} ;
13
20
use hotshot_utils:: {
@@ -38,6 +45,7 @@ pub struct LeaderViewStats<TYPES: NodeType> {
38
45
pub struct ReplicaViewStats < TYPES : NodeType > {
39
46
pub view : TYPES :: View ,
40
47
pub view_change : Option < i128 > ,
48
+ pub proposal_timestamp : Option < i128 > ,
41
49
pub proposal_recv : Option < i128 > ,
42
50
pub vote_send : Option < i128 > ,
43
51
pub timeout_vote_send : Option < i128 > ,
@@ -74,6 +82,7 @@ impl<TYPES: NodeType> ReplicaViewStats<TYPES> {
74
82
Self {
75
83
view,
76
84
view_change : None ,
85
+ proposal_timestamp : None ,
77
86
proposal_recv : None ,
78
87
vote_send : None ,
79
88
timeout_vote_send : None ,
@@ -97,6 +106,10 @@ pub struct StatsTaskState<TYPES: NodeType> {
97
106
membership_coordinator : EpochMembershipCoordinator < TYPES > ,
98
107
leader_stats : BTreeMap < TYPES :: View , LeaderViewStats < TYPES > > ,
99
108
replica_stats : BTreeMap < TYPES :: View , ReplicaViewStats < TYPES > > ,
109
+ latencies_by_view : BTreeMap < TYPES :: View , i128 > ,
110
+ sizes_by_view : BTreeMap < TYPES :: View , i128 > ,
111
+ epoch_start_times : BTreeMap < TYPES :: Epoch , i128 > ,
112
+ timeouts : BTreeSet < TYPES :: View > ,
100
113
}
101
114
102
115
impl < TYPES : NodeType > StatsTaskState < TYPES > {
@@ -115,6 +128,10 @@ impl<TYPES: NodeType> StatsTaskState<TYPES> {
115
128
membership_coordinator,
116
129
leader_stats : BTreeMap :: new ( ) ,
117
130
replica_stats : BTreeMap :: new ( ) ,
131
+ latencies_by_view : BTreeMap :: new ( ) ,
132
+ sizes_by_view : BTreeMap :: new ( ) ,
133
+ epoch_start_times : BTreeMap :: new ( ) ,
134
+ timeouts : BTreeSet :: new ( ) ,
118
135
}
119
136
}
120
137
fn leader_entry ( & mut self , view : TYPES :: View ) -> & mut LeaderViewStats < TYPES > {
@@ -130,6 +147,9 @@ impl<TYPES: NodeType> StatsTaskState<TYPES> {
130
147
fn garbage_collect ( & mut self , view : TYPES :: View ) {
131
148
self . leader_stats = self . leader_stats . split_off ( & view) ;
132
149
self . replica_stats = self . replica_stats . split_off ( & view) ;
150
+ self . latencies_by_view = self . latencies_by_view . split_off ( & view) ;
151
+ self . sizes_by_view = self . sizes_by_view . split_off ( & view) ;
152
+ self . timeouts = BTreeSet :: new ( ) ;
133
153
}
134
154
135
155
fn dump_stats ( & self ) -> Result < ( ) > {
@@ -163,6 +183,29 @@ impl<TYPES: NodeType> StatsTaskState<TYPES> {
163
183
) ;
164
184
Ok ( ( ) )
165
185
}
186
+
187
+ fn log_basic_stats ( & self , now : i128 , epoch : & TYPES :: Epoch ) {
188
+ let num_views = self . latencies_by_view . len ( ) ;
189
+ let total_latency = self . latencies_by_view . values ( ) . sum :: < i128 > ( ) ;
190
+ let average_latency = total_latency / num_views as i128 ;
191
+ tracing:: warn!( "Average latency: {}ms" , average_latency) ;
192
+ tracing:: warn!(
193
+ "Number of timeouts in epoch: {}, is {}" ,
194
+ epoch,
195
+ self . timeouts. len( )
196
+ ) ;
197
+ let total_size = self . sizes_by_view . values ( ) . sum :: < i128 > ( ) ;
198
+ if total_size == 0 {
199
+ // Either no TXNs or we are not in the DA committee and don't know block sizes
200
+ return ;
201
+ }
202
+ if let Some ( epoch_start_time) = self . epoch_start_times . get ( epoch) {
203
+ let elapsed_time = now - epoch_start_time;
204
+ // multiply by 1000 to convert to seconds
205
+ let throughput = ( total_size / elapsed_time) * 1000 ;
206
+ tracing:: warn!( "Throughput: {} bytes/s" , throughput) ;
207
+ }
208
+ }
166
209
}
167
210
168
211
#[ async_trait]
@@ -240,6 +283,9 @@ impl<TYPES: NodeType> TaskState for StatsTaskState<TYPES> {
240
283
HotShotEvent :: QuorumProposalValidated ( proposal, _) => {
241
284
self . replica_entry ( proposal. data . view_number ( ) )
242
285
. proposal_validated = Some ( now) ;
286
+ self . replica_entry ( proposal. data . view_number ( ) )
287
+ . proposal_timestamp =
288
+ Some ( proposal. data . block_header ( ) . timestamp_millis ( ) as i128 ) ;
243
289
} ,
244
290
HotShotEvent :: DaProposalSend ( proposal, _) => {
245
291
self . leader_entry ( proposal. data . view_number ( ) )
@@ -288,6 +334,7 @@ impl<TYPES: NodeType> TaskState for StatsTaskState<TYPES> {
288
334
if self . view < * view {
289
335
self . view = * view;
290
336
}
337
+ let prev_epoch = self . epoch ;
291
338
let mut new_epoch = false ;
292
339
if self . epoch < * epoch {
293
340
self . epoch = * epoch;
@@ -298,6 +345,9 @@ impl<TYPES: NodeType> TaskState for StatsTaskState<TYPES> {
298
345
}
299
346
300
347
if new_epoch {
348
+ if let Some ( prev_epoch) = prev_epoch {
349
+ self . log_basic_stats ( now, & prev_epoch) ;
350
+ }
301
351
let _ = self . dump_stats ( ) ;
302
352
self . garbage_collect ( * view - 1 ) ;
303
353
}
@@ -312,8 +362,9 @@ impl<TYPES: NodeType> TaskState for StatsTaskState<TYPES> {
312
362
self . leader_entry ( * view) . builder_start = Some ( now) ;
313
363
}
314
364
} ,
315
- HotShotEvent :: Timeout ( ..) => {
316
- self . replica_entry ( self . view ) . timeout_triggered = Some ( now) ;
365
+ HotShotEvent :: Timeout ( view, _) => {
366
+ self . replica_entry ( * view) . timeout_triggered = Some ( now) ;
367
+ self . timeouts . insert ( * view) ;
317
368
} ,
318
369
HotShotEvent :: TransactionsRecv ( _txns) => {
319
370
// TODO: Track transactions by time
@@ -334,6 +385,23 @@ impl<TYPES: NodeType> TaskState for StatsTaskState<TYPES> {
334
385
self . replica_entry ( proposal. data . view_number ( ) )
335
386
. proposal_prelim_validated = Some ( now) ;
336
387
} ,
388
+ HotShotEvent :: LeavesDecided ( leaves) => {
389
+ for leaf in leaves {
390
+ if leaf. view_number ( ) == TYPES :: View :: genesis ( ) {
391
+ continue ;
392
+ }
393
+ let view = leaf. view_number ( ) ;
394
+ let timestamp = leaf. block_header ( ) . timestamp_millis ( ) as i128 ;
395
+ let now_millis = now / 1_000_000 ;
396
+ let latency = now_millis - timestamp;
397
+ tracing:: debug!( "View {} Latency: {}ms" , view, latency) ;
398
+ self . latencies_by_view . insert ( view, latency) ;
399
+ self . sizes_by_view . insert (
400
+ view,
401
+ leaf. block_payload ( ) . map ( |p| p. txn_bytes ( ) ) . unwrap_or ( 0 ) as i128 ,
402
+ ) ;
403
+ }
404
+ } ,
337
405
_ => { } ,
338
406
}
339
407
Ok ( ( ) )
0 commit comments