xrpld
Loading...
Searching...
No Matches
collectors.h
1#pragma once
2
3#include <xrpl/basics/UnorderedContainers.h>
4
5#include <csf/Histogram.h>
6#include <csf/SimTime.h>
7#include <csf/Tx.h>
8#include <csf/Validation.h>
9#include <csf/events.h>
10
11#include <algorithm>
12#include <cassert>
13#include <chrono>
14#include <cstddef>
15#include <iomanip>
16#include <ios>
17#include <map>
18#include <optional>
19#include <ostream>
20#include <tuple>
21#include <utility>
22#include <vector>
23
24namespace xrpl::test::csf {
25
26// A collector is any class that implements
27//
28// on(NodeID, SimTime, Event)
29//
30// for all events emitted by a Peer.
31//
32// This file contains helper functions for composing different collectors
33// and also defines several standard collectors available for simulations.
34
43template <class... Cs>
45{
46 std::tuple<Cs&...> cs_;
47
48 template <class C, class E>
49 static void
50 apply(C& c, PeerID who, SimTime when, E e)
51 {
52 c.on(who, when, e);
53 }
54
55 template <std::size_t... Is, class E>
56 static void
58 {
59 (..., apply(std::get<Is>(cs), who, when, e));
60 }
61
62public:
68 Collectors(Cs&... cs) : cs_(std::tie(cs...))
69 {
70 }
71
72 template <class E>
73 void
74 on(PeerID who, SimTime when, E e)
75 {
77 }
78};
79
83template <class... Cs>
84Collectors<Cs...>
86{
87 return Collectors<Cs...>(cs...);
88}
89
99template <class CollectorType>
101{
103
104 CollectorType&
106 {
107 return byNode[who];
108 }
109
110 CollectorType const&
112 {
113 return byNode[who];
114 }
115 template <class E>
116 void
117 on(PeerID who, SimTime when, E const& e)
118 {
119 byNode[who].on(who, when, e);
120 }
121};
122
127{
128 template <class E>
129 void
130 on(PeerID, SimTime, E const& e)
131 {
132 }
133};
134
139{
140 bool init = false;
143
144 template <class E>
145 void
146 on(PeerID, SimTime when, E const& e)
147 {
148 if (!init)
149 {
150 start = when;
151 init = true;
152 }
153 else
154 {
155 stop = when;
156 }
157 }
158};
159
171{
172 // Counts
176
188
190
194
195 // Ignore most events by default
196 template <class E>
197 void
198 on(PeerID, SimTime when, E const& e)
199 {
200 }
201
202 void
203 on(PeerID who, SimTime when, SubmitTx const& e)
204 {
205 // save first time it was seen
206 if (txs.emplace(e.tx.id(), Tracker{e.tx, when}).second)
207 {
208 submitted++;
209 }
210 }
211
212 void
213 on(PeerID who, SimTime when, AcceptLedger const& e)
214 {
215 for (auto const& tx : e.ledger.txs())
216 {
217 auto it = txs.find(tx.id());
218 if (it != txs.end() && !it->second.accepted)
219 {
220 Tracker& tracker = it->second;
221 tracker.accepted = when;
222 accepted++;
223
224 submitToAccept.insert(*tracker.accepted - tracker.submitted);
225 }
226 }
227 }
228
229 void
231 {
232 for (auto const& tx : e.ledger.txs())
233 {
234 auto it = txs.find(tx.id());
235 if (it != txs.end() && !it->second.validated)
236 {
237 Tracker& tracker = it->second;
238 // Should only validated a previously accepted Tx
239 assert(tracker.accepted);
240
241 tracker.validated = when;
242 validated++;
243 submitToValidate.insert(*tracker.validated - tracker.submitted);
244 }
245 }
246 }
247
248 // Returns the number of txs which were never accepted
249 [[nodiscard]] std::size_t
250 orphaned() const
251 {
252 return std::count_if(
253 txs.begin(), txs.end(), [](auto const& it) { return !it.second.accepted; });
254 }
255
256 // Returns the number of txs which were never validated
257 [[nodiscard]] std::size_t
259 {
260 return std::count_if(
261 txs.begin(), txs.end(), [](auto const& it) { return !it.second.validated; });
262 }
263
264 template <class T>
265 void
266 report(SimDuration simDuration, T& log, bool printBreakline = false)
267 {
268 using namespace std::chrono;
269 auto perSec = [&simDuration](std::size_t count) {
270 return double(count) / duration_cast<seconds>(simDuration).count();
271 };
272
273 auto fmtS = [](SimDuration dur) { return duration_cast<duration<float>>(dur).count(); };
274
275 if (printBreakline)
276 {
277 log << std::setw(11) << std::setfill('-') << "-" << "-" << std::setw(7)
278 << std::setfill('-') << "-" << "-" << std::setw(7) << std::setfill('-') << "-"
279 << "-" << std::setw(36) << std::setfill('-') << "-" << std::endl;
280 log << std::setfill(' ');
281 }
282
283 log << std::left << std::setw(11) << "TxStats" << "|" << std::setw(7) << "Count" << "|"
284 << std::setw(7) << "Per Sec" << "|" << std::setw(15) << "Latency (sec)" << std::right
285 << std::setw(7) << "10-ile" << std::setw(7) << "50-ile" << std::setw(7) << "90-ile"
286 << std::left << std::endl;
287
288 log << std::setw(11) << std::setfill('-') << "-" << "|" << std::setw(7) << std::setfill('-')
289 << "-" << "|" << std::setw(7) << std::setfill('-') << "-" << "|" << std::setw(36)
290 << std::setfill('-') << "-" << std::endl;
291 log << std::setfill(' ');
292
293 log << std::left << std::setw(11) << "Submit " << "|" << std::right << std::setw(7)
294 << submitted << "|" << std::setw(7) << std::setprecision(2) << perSec(submitted) << "|"
295 << std::setw(36) << "" << std::endl;
296
297 log << std::left << std::setw(11) << "Accept " << "|" << std::right << std::setw(7)
298 << accepted << "|" << std::setw(7) << std::setprecision(2) << perSec(accepted) << "|"
299 << std::setw(15) << std::left << "From Submit" << std::right << std::setw(7)
300 << std::setprecision(2) << fmtS(submitToAccept.percentile(0.1f)) << std::setw(7)
301 << std::setprecision(2) << fmtS(submitToAccept.percentile(0.5f)) << std::setw(7)
302 << std::setprecision(2) << fmtS(submitToAccept.percentile(0.9f)) << std::endl;
303
304 log << std::left << std::setw(11) << "Validate " << "|" << std::right << std::setw(7)
305 << validated << "|" << std::setw(7) << std::setprecision(2) << perSec(validated) << "|"
306 << std::setw(15) << std::left << "From Submit" << std::right << std::setw(7)
307 << std::setprecision(2) << fmtS(submitToValidate.percentile(0.1f)) << std::setw(7)
308 << std::setprecision(2) << fmtS(submitToValidate.percentile(0.5f)) << std::setw(7)
309 << std::setprecision(2) << fmtS(submitToValidate.percentile(0.9f)) << std::endl;
310
311 log << std::left << std::setw(11) << "Orphan" << "|" << std::right << std::setw(7)
312 << orphaned() << "|" << std::setw(7) << "" << "|" << std::setw(36) << std::endl;
313
314 log << std::left << std::setw(11) << "Unvalidated" << "|" << std::right << std::setw(7)
315 << unvalidated() << "|" << std::setw(7) << "" << "|" << std::setw(43) << std::endl;
316
317 log << std::setw(11) << std::setfill('-') << "-" << "-" << std::setw(7) << std::setfill('-')
318 << "-" << "-" << std::setw(7) << std::setfill('-') << "-" << "-" << std::setw(36)
319 << std::setfill('-') << "-" << std::endl;
320 log << std::setfill(' ');
321 }
322
323 template <class T, class Tag>
324 void
325 csv(SimDuration simDuration, T& log, Tag const& tag, bool printHeaders = false)
326 {
327 using namespace std::chrono;
328 auto perSec = [&simDuration](std::size_t count) {
329 return double(count) / duration_cast<seconds>(simDuration).count();
330 };
331
332 auto fmtS = [](SimDuration dur) { return duration_cast<duration<float>>(dur).count(); };
333
334 if (printHeaders)
335 {
336 log << "tag" << "," << "txNumSubmitted" << "," << "txNumAccepted"
337 << "," << "txNumValidated" << "," << "txNumOrphaned" << ","
338 << "txUnvalidated" << "," << "txRateSumbitted" << ","
339 << "txRateAccepted" << "," << "txRateValidated" << ","
340 << "txLatencySubmitToAccept10Pctl" << ","
341 << "txLatencySubmitToAccept50Pctl" << ","
342 << "txLatencySubmitToAccept90Pctl" << ","
343 << "txLatencySubmitToValidatet10Pctl" << ","
344 << "txLatencySubmitToValidatet50Pctl" << ","
345 << "txLatencySubmitToValidatet90Pctl" << std::endl;
346 }
347
348 log << tag
349 << ","
350 // txNumSubmitted
351 << submitted
352 << ","
353 // txNumAccepted
354 << accepted
355 << ","
356 // txNumValidated
357 << validated
358 << ","
359 // txNumOrphaned
360 << orphaned()
361 << ","
362 // txNumUnvalidated
363 << unvalidated()
364 << ","
365 // txRateSubmitted
366 << std::setprecision(2) << perSec(submitted)
367 << ","
368 // txRateAccepted
369 << std::setprecision(2) << perSec(accepted)
370 << ","
371 // txRateValidated
372 << std::setprecision(2) << perSec(validated)
373 << ","
374 // txLatencySubmitToAccept10Pctl
375 << std::setprecision(2) << fmtS(submitToAccept.percentile(0.1f))
376 << ","
377 // txLatencySubmitToAccept50Pctl
378 << std::setprecision(2) << fmtS(submitToAccept.percentile(0.5f))
379 << ","
380 // txLatencySubmitToAccept90Pctl
381 << std::setprecision(2) << fmtS(submitToAccept.percentile(0.9f))
382 << ","
383 // txLatencySubmitToValidate10Pctl
384 << std::setprecision(2) << fmtS(submitToValidate.percentile(0.1f))
385 << ","
386 // txLatencySubmitToValidate50Pctl
387 << std::setprecision(2) << fmtS(submitToValidate.percentile(0.5f))
388 << ","
389 // txLatencySubmitToValidate90Pctl
390 << std::setprecision(2) << fmtS(submitToValidate.percentile(0.9f)) << "," << std::endl;
391 }
392};
393
401{
404
414
416
421
422 // Ignore most events by default
423 template <class E>
424 void
425 on(PeerID, SimTime, E const& e)
426 {
427 }
428
429 void
430 on(PeerID who, SimTime when, AcceptLedger const& e)
431 {
432 // First time this ledger accepted
433 if (ledgers.emplace(e.ledger.id(), Tracker{when}).second)
434 {
435 ++accepted;
436 // ignore jumps?
437 if (e.prior.id() == e.ledger.parentID())
438 {
439 auto const it = ledgers.find(e.ledger.parentID());
440 if (it != ledgers.end())
441 {
442 acceptToAccept.insert(when - it->second.accepted);
443 }
444 }
445 }
446 }
447
448 void
450 {
451 // ignore jumps
452 if (e.prior.id() == e.ledger.parentID())
453 {
454 auto const it = ledgers.find(e.ledger.id());
455 assert(it != ledgers.end());
456 auto& tracker = it->second;
457 // first time fully validated
458 if (!tracker.fullyValidated)
459 {
461 tracker.fullyValidated = when;
462 acceptToFullyValid.insert(when - tracker.accepted);
463
464 auto const parentIt = ledgers.find(e.ledger.parentID());
465 if (parentIt != ledgers.end())
466 {
467 auto& parentTracker = parentIt->second;
468 if (parentTracker.fullyValidated)
469 {
470 fullyValidToFullyValid.insert(when - *parentTracker.fullyValidated);
471 }
472 }
473 }
474 }
475 }
476
477 [[nodiscard]] std::size_t
479 {
480 return std::count_if(ledgers.begin(), ledgers.end(), [](auto const& it) {
481 return !it.second.fullyValidated;
482 });
483 }
484
485 template <class T>
486 void
487 report(SimDuration simDuration, T& log, bool printBreakline = false)
488 {
489 using namespace std::chrono;
490 auto perSec = [&simDuration](std::size_t count) {
491 return double(count) / duration_cast<seconds>(simDuration).count();
492 };
493
494 auto fmtS = [](SimDuration dur) { return duration_cast<duration<float>>(dur).count(); };
495
496 if (printBreakline)
497 {
498 log << std::setw(11) << std::setfill('-') << "-" << "-" << std::setw(7)
499 << std::setfill('-') << "-" << "-" << std::setw(7) << std::setfill('-') << "-"
500 << "-" << std::setw(36) << std::setfill('-') << "-" << std::endl;
501 log << std::setfill(' ');
502 }
503
504 log << std::left << std::setw(11) << "LedgerStats" << "|" << std::setw(7) << "Count" << "|"
505 << std::setw(7) << "Per Sec"
506 << "|" << std::setw(15) << "Latency (sec)" << std::right << std::setw(7) << "10-ile"
507 << std::setw(7) << "50-ile" << std::setw(7) << "90-ile" << std::left << std::endl;
508
509 log << std::setw(11) << std::setfill('-') << "-" << "|" << std::setw(7) << std::setfill('-')
510 << "-" << "|" << std::setw(7) << std::setfill('-') << "-" << "|" << std::setw(36)
511 << std::setfill('-') << "-" << std::endl;
512 log << std::setfill(' ');
513
514 log << std::left << std::setw(11) << "Accept " << "|" << std::right << std::setw(7)
515 << accepted << "|" << std::setw(7) << std::setprecision(2) << perSec(accepted) << "|"
516 << std::setw(15) << std::left << "From Accept" << std::right << std::setw(7)
517 << std::setprecision(2) << fmtS(acceptToAccept.percentile(0.1f)) << std::setw(7)
518 << std::setprecision(2) << fmtS(acceptToAccept.percentile(0.5f)) << std::setw(7)
519 << std::setprecision(2) << fmtS(acceptToAccept.percentile(0.9f)) << std::endl;
520
521 log << std::left << std::setw(11) << "Validate " << "|" << std::right << std::setw(7)
522 << fullyValidated << "|" << std::setw(7) << std::setprecision(2)
523 << perSec(fullyValidated) << "|" << std::setw(15) << std::left << "From Validate "
525 << fmtS(fullyValidToFullyValid.percentile(0.1f)) << std::setw(7) << std::setprecision(2)
526 << fmtS(fullyValidToFullyValid.percentile(0.5f)) << std::setw(7) << std::setprecision(2)
527 << fmtS(fullyValidToFullyValid.percentile(0.9f)) << std::endl;
528
529 log << std::setw(11) << std::setfill('-') << "-" << "-" << std::setw(7) << std::setfill('-')
530 << "-" << "-" << std::setw(7) << std::setfill('-') << "-" << "-" << std::setw(36)
531 << std::setfill('-') << "-" << std::endl;
532 log << std::setfill(' ');
533 }
534
535 template <class T, class Tag>
536 void
537 csv(SimDuration simDuration, T& log, Tag const& tag, bool printHeaders = false)
538 {
539 using namespace std::chrono;
540 auto perSec = [&simDuration](std::size_t count) {
541 return double(count) / duration_cast<seconds>(simDuration).count();
542 };
543
544 auto fmtS = [](SimDuration dur) { return duration_cast<duration<float>>(dur).count(); };
545
546 if (printHeaders)
547 {
548 log << "tag" << "," << "ledgerNumAccepted" << ","
549 << "ledgerNumFullyValidated" << "," << "ledgerRateAccepted"
550 << "," << "ledgerRateFullyValidated" << ","
551 << "ledgerLatencyAcceptToAccept10Pctl" << ","
552 << "ledgerLatencyAcceptToAccept50Pctl" << ","
553 << "ledgerLatencyAcceptToAccept90Pctl" << ","
554 << "ledgerLatencyFullyValidToFullyValid10Pctl" << ","
555 << "ledgerLatencyFullyValidToFullyValid50Pctl" << ","
556 << "ledgerLatencyFullyValidToFullyValid90Pctl" << std::endl;
557 }
558
559 log << tag
560 << ","
561 // ledgerNumAccepted
562 << accepted
563 << ","
564 // ledgerNumFullyValidated
566 << ","
567 // ledgerRateAccepted
568 << std::setprecision(2) << perSec(accepted)
569 << ","
570 // ledgerRateFullyValidated
571 << std::setprecision(2) << perSec(fullyValidated)
572 << ","
573 // ledgerLatencyAcceptToAccept10Pctl
574 << std::setprecision(2) << fmtS(acceptToAccept.percentile(0.1f))
575 << ","
576 // ledgerLatencyAcceptToAccept50Pctl
577 << std::setprecision(2) << fmtS(acceptToAccept.percentile(0.5f))
578 << ","
579 // ledgerLatencyAcceptToAccept90Pctl
580 << std::setprecision(2) << fmtS(acceptToAccept.percentile(0.9f))
581 << ","
582 // ledgerLatencyFullyValidToFullyValid10Pctl
583 << std::setprecision(2) << fmtS(fullyValidToFullyValid.percentile(0.1f))
584 << ","
585 // ledgerLatencyFullyValidToFullyValid50Pctl
586 << std::setprecision(2) << fmtS(fullyValidToFullyValid.percentile(0.5f))
587 << ","
588 // ledgerLatencyFullyValidToFullyValid90Pctl
589 << std::setprecision(2) << fmtS(fullyValidToFullyValid.percentile(0.9f)) << std::endl;
590 }
591};
592
600{
602
603 // Ignore most events by default
604 template <class E>
605 void
606 on(PeerID, SimTime, E const& e)
607 {
608 }
609
610 void
611 on(PeerID who, SimTime when, AcceptLedger const& e)
612 {
613 out << when.time_since_epoch().count() << ": Node " << who << " accepted " << "L"
614 << e.ledger.id() << " " << e.ledger.txs() << "\n";
615 }
616
617 void
619 {
620 out << when.time_since_epoch().count() << ": Node " << who << " fully-validated " << "L"
621 << e.ledger.id() << " " << e.ledger.txs() << "\n";
622 }
623};
624
632{
640
643
644 // Ignore most events by default
645 template <class E>
646 void
647 on(PeerID, SimTime, E const& e)
648 {
649 }
650
651 void
652 on(PeerID who, SimTime when, AcceptLedger const& e)
653 {
654 // Not a direct child -> parent switch
655 if (e.ledger.parentID() != e.prior.id())
656 closeJumps.emplace_back(Jump{.id = who, .when = when, .from = e.prior, .to = e.ledger});
657 }
658
659 void
661 {
662 // Not a direct child -> parent switch
663 if (e.ledger.parentID() != e.prior.id())
664 {
665 fullyValidatedJumps.emplace_back(
666 Jump{.id = who, .when = when, .from = e.prior, .to = e.ledger});
667 }
668 }
669};
670
671} // namespace xrpl::test::csf
Group of collectors.
Definition collectors.h:45
static void apply(std::tuple< Cs &... > &cs, PeerID who, SimTime when, E e, std::index_sequence< Is... >)
Definition collectors.h:57
void on(PeerID who, SimTime when, E e)
Definition collectors.h:74
std::tuple< Cs &... > cs_
Definition collectors.h:46
Collectors(Cs &... cs)
Constructor.
Definition collectors.h:68
static void apply(C &c, PeerID who, SimTime when, E e)
Definition collectors.h:50
Basic histogram.
Definition Histogram.h:23
A ledger is a set of observed transactions and a sequence number identifying the ledger.
Definition ledgers.h:48
TxSetType const & txs() const
Definition ledgers.h:208
A single transaction.
Definition Tx.h:24
ID const & id() const
Definition Tx.h:40
T count_if(T... args)
T duration_cast(T... args)
T endl(T... args)
T left(T... args)
T log(T... args)
STL namespace.
SimClock::duration SimDuration
Definition SimTime.h:14
TaggedInteger< std::uint32_t, PeerIDTag > PeerID
Definition Validation.h:17
SimClock::time_point SimTime
Definition SimTime.h:15
Collectors< Cs... > makeCollectors(Cs &... cs)
Create an instance of Collectors<Cs...>.
Definition collectors.h:85
std::unordered_map< Key, Value, Hash, Pred, Allocator > hash_map
T setfill(T... args)
T setprecision(T... args)
T setw(T... args)
Peer accepted consensus results.
Definition events.h:121
Maintain an instance of a Collector per peer.
Definition collectors.h:101
CollectorType const & operator[](PeerID who) const
Definition collectors.h:111
void on(PeerID who, SimTime when, E const &e)
Definition collectors.h:117
std::map< PeerID, CollectorType > byNode
Definition collectors.h:102
CollectorType & operator[](PeerID who)
Definition collectors.h:105
Peer fully validated a new ledger.
Definition events.h:144
Ledger prior
The prior fully validated ledger This is a jump if prior.id() != ledger.parentID().
Definition events.h:154
Ledger ledger
The new fully validated ledger.
Definition events.h:148
Saves information about Jumps for closed and fully validated ledgers.
Definition collectors.h:632
std::vector< Jump > fullyValidatedJumps
Definition collectors.h:642
void on(PeerID, SimTime, E const &e)
Definition collectors.h:647
void on(PeerID who, SimTime when, FullyValidateLedger const &e)
Definition collectors.h:660
void on(PeerID who, SimTime when, AcceptLedger const &e)
Definition collectors.h:652
std::vector< Jump > closeJumps
Definition collectors.h:641
std::optional< SimTime > fullyValidated
Definition collectors.h:408
Tracks the accepted -> validated evolution of ledgers.
Definition collectors.h:401
void on(PeerID who, SimTime when, AcceptLedger const &e)
Definition collectors.h:430
hash_map< Ledger::ID, Tracker > ledgers
Definition collectors.h:415
void csv(SimDuration simDuration, T &log, Tag const &tag, bool printHeaders=false)
Definition collectors.h:537
void on(PeerID, SimTime, E const &e)
Definition collectors.h:425
void on(PeerID who, SimTime when, FullyValidateLedger const &e)
Definition collectors.h:449
void report(SimDuration simDuration, T &log, bool printBreakline=false)
Definition collectors.h:487
Histogram< SimTime::duration > Hist
Definition collectors.h:417
std::size_t unvalidated() const
Definition collectors.h:478
Collector which ignores all events.
Definition collectors.h:127
void on(PeerID, SimTime, E const &e)
Definition collectors.h:130
Tracks the overall duration of a simulation.
Definition collectors.h:139
void on(PeerID, SimTime when, E const &e)
Definition collectors.h:146
Write out stream of ledger activity.
Definition collectors.h:600
void on(PeerID, SimTime, E const &e)
Definition collectors.h:606
void on(PeerID who, SimTime when, FullyValidateLedger const &e)
Definition collectors.h:618
void on(PeerID who, SimTime when, AcceptLedger const &e)
Definition collectors.h:611
A transaction submitted to a peer.
Definition events.h:82
Tx tx
The submitted transaction.
Definition events.h:86
std::optional< SimTime > accepted
Definition collectors.h:181
std::optional< SimTime > validated
Definition collectors.h:182
Tracker(Tx tx, SimTime submitted)
Definition collectors.h:184
Tracks the submission -> accepted -> validated evolution of transactions.
Definition collectors.h:171
void report(SimDuration simDuration, T &log, bool printBreakline=false)
Definition collectors.h:266
void on(PeerID who, SimTime when, AcceptLedger const &e)
Definition collectors.h:213
std::size_t orphaned() const
Definition collectors.h:250
void on(PeerID who, SimTime when, FullyValidateLedger const &e)
Definition collectors.h:230
std::size_t unvalidated() const
Definition collectors.h:258
void on(PeerID, SimTime when, E const &e)
Definition collectors.h:198
Histogram< SimTime::duration > Hist
Definition collectors.h:191
hash_map< Tx::ID, Tracker > txs
Definition collectors.h:189
void on(PeerID who, SimTime when, SubmitTx const &e)
Definition collectors.h:203
void csv(SimDuration simDuration, T &log, Tag const &tag, bool printHeaders=false)
Definition collectors.h:325