Line data Source code
1 : #ifndef HEADER_fd_src_disco_gui_fd_gui_hist_h
2 : #define HEADER_fd_src_disco_gui_fd_gui_hist_h
3 :
4 : /* fd_gui_hist is the GUI's historical event store semantics layer. It
5 : sits on top of fd_gui_store and owns everything GUI-specific about
6 : the monitoring history: the named DBs (FD_GUI_HIST_* below), their
7 : key/value structs, fixed record sizes, the eviction policy, and the
8 : query API.
9 :
10 : There are two kinds of DB:
11 :
12 : - TIME-SERIES (TS) DBs are append-only streams tagged by a
13 : wallclock window. Written by fd_gui_hist_ts_append, read by
14 : time-window range scan.
15 : - KEY-VALUE (KV) DBs hold one record per key. Written/read by
16 : point key (fd_gui_hist_kv_get_or_create / _get / _get_any).
17 : */
18 :
19 : #include "../../util/fd_util_base.h"
20 : #include "fd_gui_store.h"
21 : #include "../../flamenco/leaders/fd_leaders_base.h" /* MAX_SLOTS_PER_EPOCH */
22 : #include "../../flamenco/runtime/fd_slot_params.h" /* FD_SLOT_PARAMS_200MS */
23 :
24 : struct fd_gui;
25 : typedef struct fd_gui fd_gui_t;
26 :
27 0 : #define FD_GUI_HIST_MAGIC (0xf17e6d09147802UL)
28 :
29 0 : #define FD_GUI_HIST_RES_1S_NS (1000000000L)
30 :
31 0 : #define FD_GUI_HIST_MAX_LEADER_SLOTS_PER_EPOCH (43200UL) /* 10% capacity should be enough for mainnet/testnet */
32 :
33 0 : #define FD_GUI_HIST_MAX_EPOCHS (64UL) /* Comfortably above typical time between restarts */
34 0 : #define FD_GUI_HIST_SCHEDULER_COUNTS (0) /* (ts, type) */
35 0 : #define FD_GUI_HIST_TILE_TIMERS (1) /* (ts, type) */
36 0 : #define FD_GUI_HIST_SHRED_EVENTS (2)
37 0 : #define FD_GUI_HIST_TXN_START (3) /* (ts, type, bank, txn) */
38 0 : #define FD_GUI_HIST_TXN_END (4) /* (ts, type, bank, txn) */
39 0 : #define FD_GUI_HIST_SLOT (5) /* (slot, bank_seq) */
40 0 : #define FD_GUI_HIST_LEADER_SLOT (6) /* (slot, bank_seq) */
41 0 : #define FD_GUI_HIST_EPOCH (7) /* (epoch) */
42 0 : #define FD_GUI_HIST_TILE_STATS (8) /* (ts, type) */
43 0 : #define FD_GUI_HIST_TXN_WATERFALL (9) /* (ts, type) */
44 0 : #define FD_GUI_HIST_TIMELINE_DAY (10)
45 0 : #define FD_GUI_HIST_REPLAY_TXN (11)
46 0 : #define FD_GUI_HIST_CNT (12)
47 :
48 : struct fd_gui_hist_metrics {
49 : /* Writes dropped after exhausting region or KV index capacity. */
50 : ulong map_full[ FD_GUI_HIST_CNT ];
51 : /* Writes that evicted records before succeeding. */
52 : ulong reserves [ FD_GUI_HIST_CNT ];
53 : };
54 : typedef struct fd_gui_hist_metrics fd_gui_hist_metrics_t;
55 :
56 : struct fd_gui_hist_slot_key { ulong slot; ulong bank_seq; };
57 : struct fd_gui_hist_leader_slot_key { ulong slot; ulong bank_seq; };
58 : struct fd_gui_hist_epoch_key { ulong epoch; };
59 : typedef struct fd_gui_hist_slot_key fd_gui_hist_slot_key_t;
60 : typedef struct fd_gui_hist_leader_slot_key fd_gui_hist_leader_slot_key_t;
61 : typedef struct fd_gui_hist_epoch_key fd_gui_hist_epoch_key_t;
62 :
63 : struct fd_gui_hist_private;
64 : typedef struct fd_gui_hist_private fd_gui_hist_t;
65 :
66 : /* fd_gui_hist_kv_slot_iter_t iterates every record for `slot` (every
67 : fork's block) in a slot-keyed KV DB. Used to enumerate the
68 : equivocating forks of a slot. */
69 : struct fd_gui_hist_kv_slot_iter {
70 : void const * rec; /* current record; NULL when done */
71 : ulong bank_seq; /* current record's bank_seq */
72 : fd_gui_store_kv_iter_t _it;
73 : };
74 : typedef struct fd_gui_hist_kv_slot_iter fd_gui_hist_kv_slot_iter_t;
75 :
76 : /* fd_gui_hist_range_filter_fn is an optional per-record predicate
77 : evaluated during a range scan: called with a pointer to the DB's
78 : record and the caller's `ctx`, it returns non-zero to emit the record
79 : or zero to skip it. A NULL filter emits every record in the window
80 : range. */
81 :
82 : typedef int (*fd_gui_hist_range_filter_fn)( void const * rec, void * ctx );
83 :
84 : /* fd_gui_hist_iter_t iterates the records produced by a time-window
85 : range query.
86 :
87 : Records are emitted oldest-window-first, but the backend does not
88 : strictly order records within a window.
89 : */
90 : struct fd_gui_hist_iter {
91 : void const * rec;
92 : ulong rec_sz;
93 :
94 : int _dbi;
95 : int _emitted;
96 : fd_gui_hist_range_filter_fn _filter;
97 : void * _filter_ctx;
98 : fd_gui_store_ts_iter_t _it;
99 : };
100 :
101 : typedef struct fd_gui_hist_iter fd_gui_hist_iter_t;
102 :
103 : FD_PROTOTYPES_BEGIN
104 :
105 : FD_FN_CONST ulong
106 : fd_gui_hist_align( void );
107 :
108 : FD_FN_CONST ulong
109 : fd_gui_hist_footprint( void );
110 :
111 : /* fd_gui_hist_new formats the workspace region `mem`
112 : (>= fd_gui_hist_footprint bytes, fd_gui_hist_align aligned) as the
113 : history semantics layer over the store `db`. Returns `mem` on success
114 : or NULL on failure (logged). */
115 :
116 : void *
117 : fd_gui_hist_new( void * mem,
118 : fd_gui_store_t const * db );
119 :
120 : /* fd_gui_hist_join joins a region formatted by fd_gui_hist_new,
121 : returning a usable handle (mem may be NULL -> NULL). */
122 :
123 : fd_gui_hist_t *
124 : fd_gui_hist_join( void * mem );
125 :
126 : void *
127 : fd_gui_hist_leave( fd_gui_hist_t * hist );
128 :
129 : void *
130 : fd_gui_hist_delete( void * mem );
131 :
132 : /* fd_gui_hist_db_descs returns a pointer to a static array to be passed
133 : to fd_gui_store_new. */
134 :
135 : fd_gui_store_desc_t const *
136 : fd_gui_hist_db_descs( ulong store_bytes );
137 :
138 : FD_FN_CONST static inline ulong
139 0 : fd_gui_hist_db_cnt( void ) { return FD_GUI_HIST_CNT; }
140 :
141 : fd_gui_hist_metrics_t const *
142 : fd_gui_hist_metrics( fd_gui_t const * gui );
143 :
144 : /* ---- TS read/write -------------------------------------------------- */
145 :
146 : /* fd_gui_hist_ts_append copies the record at val into time-series DB
147 : dbi, preserving its contents. The timestamp at the DB's configured
148 : offset must be >= the timestamp of the last successful append.
149 :
150 : Returns 0 on success, -1 for an invalid DB, a decreasing timestamp,
151 : or a store append failure. */
152 :
153 : int
154 : fd_gui_hist_ts_append( fd_gui_t * gui,
155 : int dbi,
156 : void const * val );
157 :
158 : /* fd_gui_hist_ts_emplace is fd_gui_hist_ts_append without the copy: it
159 : appends a record whose timestamp field is stored_ts and returns a
160 : pointer to it in the store, or NULL on the same failures. */
161 :
162 : void *
163 : fd_gui_hist_ts_emplace( fd_gui_t * gui,
164 : int dbi,
165 : long stored_ts );
166 :
167 : int
168 : fd_gui_hist_range_begin( fd_gui_t * gui,
169 : fd_gui_hist_iter_t * iter,
170 : int dbi,
171 : long lo_ns,
172 : long hi_ns,
173 : fd_gui_hist_range_filter_fn filter,
174 : void * filter_ctx );
175 :
176 : int
177 : fd_gui_hist_range_next( fd_gui_hist_iter_t * iter );
178 :
179 : void
180 : fd_gui_hist_range_end( fd_gui_hist_iter_t * iter );
181 :
182 :
183 : /* ---- KV read/write -------------------------------------------------- */
184 :
185 : /* fd_gui_hist_kv_get_or_create reserves (creating if absent) the record
186 : for `*key` in a KV DB and returns a mutable pointer to the record's
187 : value, or NULL on failure.
188 :
189 : Will evict old entries to make room if necessary. Returns the value
190 : pointer on success, or NULL on error. */
191 :
192 : void *
193 : fd_gui_hist_kv_get_or_create( fd_gui_t * gui,
194 : int dbi,
195 : void const * key );
196 :
197 : /* fd_gui_hist_kv_get is like fd_gui_hist_kv_get_or_create, but returns
198 : NULL when the record is not found instead of creating one. */
199 :
200 : void *
201 : fd_gui_hist_kv_get( fd_gui_t * gui,
202 : int dbi,
203 : void const * key );
204 :
205 : /* fd_gui_hist_kv_get_slot_any looks up the slot record for the first
206 : key matching (slot, *) and returns a pointer to its value. */
207 :
208 : void *
209 : fd_gui_hist_kv_get_slot_any( fd_gui_t * gui,
210 : int dbi,
211 : ulong slot );
212 :
213 : fd_gui_hist_kv_slot_iter_t *
214 : fd_gui_hist_kv_iter_begin( fd_gui_t * gui,
215 : fd_gui_hist_kv_slot_iter_t * iter,
216 : int dbi,
217 : ulong slot );
218 :
219 : int
220 : fd_gui_hist_kv_iter_next( fd_gui_hist_kv_slot_iter_t * iter );
221 :
222 : /* ---- Eviction ------------------------------------------------------- */
223 :
224 : /* fd_gui_hist_evict_step reclaims at most FD_GUI_HIST_EVICT_BATCH
225 : records from an oldest excess region once at most one shared region
226 : remains free. Every DB's newest FD_GUI_STORE_BASE_REGIONS regions
227 : are protected. Returns 1 for actual progress, 0 when idle or all
228 : candidates pinned. Writes retry on pressure, then recycle only their
229 : own eligible history if no excess candidate remains. */
230 :
231 : int
232 : fd_gui_hist_evict_step( fd_gui_t * gui );
233 :
234 : FD_PROTOTYPES_END
235 :
236 : #endif /* HEADER_fd_src_disco_gui_fd_gui_hist_h */
|