Line data Source code
1 : #include "fd_gui_shred.h"
2 : #include "fd_gui.h"
3 :
4 56352 : #define FD_GUI_SHRED_LITERAL (0U)
5 3388479 : #define FD_GUI_SHRED_DELTA (1U)
6 28335405 : #define FD_GUI_SHRED_TS_NEG (2U)
7 28321179 : #define FD_GUI_SHRED_IDX_NEG (4U)
8 35102232 : #define FD_GUI_SHRED_EVENT_SHIFT (3U)
9 :
10 : struct __attribute__((packed)) fd_gui_shred_delta {
11 : uint insert_delta;
12 : uint timestamp_delta : 24;
13 : uchar idx_delta;
14 : };
15 : typedef struct fd_gui_shred_delta fd_gui_shred_delta_t;
16 :
17 : FD_STATIC_ASSERT( sizeof(fd_gui_shred_delta_t)==8UL, shred_delta_sz );
18 :
19 : static ulong
20 299154 : shred_window( long ns ) {
21 299154 : return ns<=0L ? 0UL : (ulong)(ns/FD_GUI_HIST_RES_1S_NS);
22 299154 : }
23 :
24 : static inline fd_gui_shred_event_t
25 6300726 : fec_shred_event( fd_gui_fec_event_t const * src ) {
26 6300726 : return (fd_gui_shred_event_t){ .insert_time_ns=src->insert_time_ns, .event_time_ns=src->event_time_ns,
27 6300726 : .slot=src->key.slot, .idx=src->key.idx, .event=src->key.event };
28 6300726 : }
29 :
30 : static int
31 : batch_append( fd_gui_shred_builder_t * builder,
32 3451788 : fd_gui_shred_event_t const * event ) {
33 3451788 : fd_gui_shred_batch_t * batch = &builder->batch;
34 3451788 : fd_gui_shred_event_t const * prev = &builder->prev;
35 3451788 : ulong ts_delta = event->event_time_ns>=prev->event_time_ns
36 3451788 : ? (ulong)event->event_time_ns-(ulong)prev->event_time_ns
37 3451788 : : (ulong)prev->event_time_ns-(ulong)event->event_time_ns;
38 3451788 : ulong idx_delta = event->idx>=prev->idx ? (ulong)(event->idx-prev->idx) : (ulong)(prev->idx-event->idx);
39 3451788 : int delta = batch->event_cnt && event->slot==prev->slot && ts_delta<=FD_GUI_SHRED_EVENT_TS_MAX && idx_delta<=UCHAR_MAX &&
40 3451788 : event->event<=(UCHAR_MAX>>FD_GUI_SHRED_EVENT_SHIFT);
41 3451788 : ulong sz = 1UL + (delta ? sizeof(fd_gui_shred_delta_t) : sizeof(fd_gui_shred_event_t));
42 3451788 : if( FD_UNLIKELY( sz>sizeof(batch->data)-batch->data_sz ) ) return 0;
43 :
44 3444831 : uchar * dst = batch->data+batch->data_sz;
45 3444831 : if( FD_LIKELY( delta ) ) {
46 3388479 : *dst++ = (uchar)(FD_GUI_SHRED_DELTA |
47 3388479 : (event->event_time_ns<prev->event_time_ns ? FD_GUI_SHRED_TS_NEG : 0U) |
48 3388479 : (event->idx<prev->idx ? FD_GUI_SHRED_IDX_NEG : 0U) |
49 3388479 : ((uint)event->event<<FD_GUI_SHRED_EVENT_SHIFT));
50 3388479 : fd_gui_shred_delta_t d = {
51 3388479 : .insert_delta = (uint)(event->insert_time_ns-prev->insert_time_ns),
52 3388479 : .timestamp_delta = (uint)(ts_delta & FD_GUI_SHRED_EVENT_TS_MAX),
53 3388479 : .idx_delta = (uchar)idx_delta
54 3388479 : };
55 3388479 : fd_memcpy( dst, &d, sizeof(d) );
56 3388479 : } else {
57 56352 : *dst++ = FD_GUI_SHRED_LITERAL;
58 56352 : fd_memcpy( dst, event, sizeof(*event) );
59 56352 : }
60 3444831 : batch->data_sz += (uint)sz;
61 3444831 : batch->event_cnt++;
62 3444831 : builder->prev = *event;
63 3444831 : return 1;
64 3451788 : }
65 :
66 : ulong
67 : fd_gui_fec_archive( fd_gui_t * gui,
68 6168 : ulong budget ) {
69 6168 : if( !gui->db || !gui->hist ) return 0UL;
70 6168 : ulong total = 0UL;
71 18474 : while( budget ) {
72 12327 : fd_gui_store_kv_scan_t scan;
73 12327 : fd_gui_store_kv_scan_begin( gui->db, &scan, FD_GUI_HIST_FEC_EVENTS );
74 12327 : fd_gui_shred_builder_t builder = {0};
75 12327 : fd_gui_fec_event_batch_t events = { .min_ns=LONG_MAX, .max_ns=LONG_MIN };
76 12327 : ulong count = 0UL;
77 3158082 : while( count<budget && fd_gui_store_kv_scan_next( &scan ) ) {
78 3151929 : fd_gui_fec_event_t const * src = scan.rec;
79 3151929 : if( src->key.slot>=fd_gui_event_closed_slot_wmark( gui ) ) break;
80 : /* Bound the batch's admission span so event-time queries can find
81 : it (see FD_GUI_FEC_BATCH_SPAN_NS). This also keeps the codec's
82 : 32-bit admission deltas in range. */
83 3151905 : if( count && src->insert_time_ns-events.insert_time_ns>FD_GUI_FEC_BATCH_SPAN_NS ) break;
84 3151899 : fd_gui_shred_event_t event = fec_shred_event( src );
85 3151899 : if( !count ) events.insert_time_ns = builder.batch.insert_time_ns = src->insert_time_ns;
86 3151899 : if( !batch_append( &builder, &event ) ) break;
87 3145755 : events.min_ns = fd_long_min( events.min_ns, src->event_time_ns );
88 3145755 : events.max_ns = fd_long_max( events.max_ns, src->event_time_ns );
89 3145755 : count++;
90 3145755 : }
91 12327 : if( !count ) break; /* An open prefix pins all later admissions. */
92 12306 : events.batch = builder.batch;
93 : /* Keep staging intact on archive admission failure. */
94 12306 : if( fd_gui_hist_ts_append( gui, FD_GUI_HIST_FEC_EVENT_ARCHIVE, &events ) ) break;
95 12306 : FD_TEST( fd_gui_store_kv_reclaim_prefix( gui->db, FD_GUI_HIST_FEC_EVENTS, count, NULL, NULL )==count );
96 12306 : budget -= count;
97 12306 : total += count;
98 12306 : }
99 6168 : return total;
100 6168 : }
101 :
102 : static void
103 64027824 : batch_next( fd_gui_shred_event_iter_t * iter ) {
104 64027824 : uchar const * src = iter->_batch->data+iter->_data_off;
105 64027824 : uchar tag = *src++;
106 64027824 : if( FD_LIKELY( tag & FD_GUI_SHRED_DELTA ) ) {
107 28318578 : fd_gui_shred_delta_t d;
108 28318578 : fd_memcpy( &d, src, sizeof(d) );
109 28318578 : iter->event.insert_time_ns += (long)d.insert_delta;
110 28318578 : iter->event.event_time_ns += (tag & FD_GUI_SHRED_TS_NEG) ? -(long)d.timestamp_delta : (long)d.timestamp_delta;
111 28318578 : iter->event.idx = (ushort)((tag & FD_GUI_SHRED_IDX_NEG) ? iter->event.idx-d.idx_delta : iter->event.idx+d.idx_delta);
112 28318578 : iter->event.event = (uchar)(tag>>FD_GUI_SHRED_EVENT_SHIFT);
113 28318578 : iter->_data_off += 1UL+sizeof(d);
114 35709246 : } else {
115 35709246 : fd_memcpy( &iter->event, src, sizeof(iter->event) );
116 35709246 : iter->_data_off += 1UL+sizeof(iter->event);
117 35709246 : }
118 64027824 : iter->_batch_idx++;
119 64027824 : }
120 :
121 : static int
122 : fec_admitted_before( void const * rec,
123 291 : void * ctx ) {
124 291 : return ((fd_gui_fec_event_t const *)rec)->insert_time_ns<*(long const *)ctx;
125 291 : }
126 :
127 : static fd_gui_shred_event_iter_t *
128 : shred_iter_begin( fd_gui_t * gui,
129 : fd_gui_shred_event_iter_t * iter,
130 : int event_time,
131 : int fec,
132 : long after_ns,
133 6429 : long before_ns ) {
134 6429 : fd_memset( iter, 0, sizeof(*iter) );
135 6429 : iter->_gui = gui;
136 6429 : iter->_after_ns = after_ns;
137 6429 : iter->_before_ns = before_ns;
138 6429 : iter->_event_time = event_time;
139 6429 : iter->_fec_archive = fec;
140 :
141 : /* Event-time shred queries widen the scan by +-1 second. The
142 : assumption is that event-ordering approximately equals
143 : insert-ordering, and in rare cases where there is more than a second
144 : of skew in the system we can tolerate missing data. Stored FEC event
145 : times are within FD_GUI_FEC_EVENT_LAG_MAX_NS of admission and
146 : batches span at most FD_GUI_FEC_BATCH_SPAN_NS of admission. */
147 6429 : if( FD_LIKELY( gui->db && gui->hist && after_ns<=before_ns ) ) {
148 6429 : long lo_margin = fec ? FD_GUI_FEC_EVENT_LAG_MAX_NS+FD_GUI_FEC_BATCH_SPAN_NS : event_time ? FD_GUI_HIST_RES_1S_NS : 0L;
149 6429 : long hi_margin = fec ? FD_GUI_FEC_EVENT_LAG_MAX_NS : event_time ? FD_GUI_HIST_RES_1S_NS : 0L;
150 6429 : long lo_ns = fd_long_max( 0L, fd_long_sat_sub( after_ns, lo_margin ) );
151 6429 : long hi_ns = fd_long_min( LONG_MAX-1L, fd_long_sat_add( before_ns, hi_margin ) );
152 6429 : int dbi = fec ? FD_GUI_HIST_FEC_EVENT_ARCHIVE : FD_GUI_HIST_SHRED_EVENTS;
153 6429 : iter->_hist_active = !fd_gui_hist_range_begin( gui, &iter->_hist_iter, dbi, lo_ns, hi_ns, NULL, NULL );
154 6429 : if( fec ) {
155 : /* Staged admission times are nondecreasing, so only admissions in
156 : [after_ns-LAG_MAX,before_ns+LAG_MAX) can match. */
157 60 : long insert_lo = fd_long_sat_sub( after_ns, FD_GUI_FEC_EVENT_LAG_MAX_NS );
158 60 : iter->_fec_insert_hi = fd_long_sat_add( before_ns, FD_GUI_FEC_EVENT_LAG_MAX_NS );
159 60 : fd_gui_store_kv_scan_begin( gui->db, &iter->_fec_iter, FD_GUI_HIST_FEC_EVENTS );
160 60 : fd_gui_store_kv_scan_seek( &iter->_fec_iter, fec_admitted_before, &insert_lo );
161 6369 : } else {
162 6369 : iter->_builder_pending = 1;
163 6369 : }
164 6429 : }
165 6429 : return iter;
166 6429 : }
167 :
168 : fd_gui_shred_event_iter_t *
169 : fd_gui_shred_event_hist_iter_begin( fd_gui_t * gui,
170 : fd_gui_shred_event_iter_t * iter,
171 : long after_ns,
172 12 : long before_ns ) {
173 12 : return shred_iter_begin( gui, iter, 1, 0, after_ns, before_ns );
174 12 : }
175 :
176 : fd_gui_shred_event_iter_t *
177 : fd_gui_shred_event_iter_begin( fd_gui_t * gui,
178 : fd_gui_shred_event_iter_t * iter,
179 : long after_ns,
180 6357 : long before_ns ) {
181 6357 : return shred_iter_begin( gui, iter, 0, 0, fd_long_max( after_ns, 0L ), fd_long_min( before_ns, LONG_MAX-1L ) );
182 6357 : }
183 :
184 : int
185 23186601 : fd_gui_shred_event_iter_next( fd_gui_shred_event_iter_t * iter ) {
186 67495800 : while( iter->_batch || iter->_hist_active || iter->_builder_pending ) {
187 64341450 : if( iter->_batch && iter->_batch_idx<iter->_batch->event_cnt ) {
188 64027824 : batch_next( iter );
189 64027824 : long ts = iter->_event_time ? iter->event.event_time_ns : iter->event.insert_time_ns;
190 64027824 : if( ts>=iter->_after_ns && (iter->_event_time ? ts<iter->_before_ns : ts<=iter->_before_ns) ) return 1;
191 43995573 : continue;
192 64027824 : }
193 313626 : iter->_batch = NULL;
194 313626 : iter->_batch_idx = 0UL;
195 313626 : iter->_data_off = 0UL;
196 313626 : if( iter->_hist_active && fd_gui_hist_range_next( &iter->_hist_iter ) ) {
197 302631 : if( iter->_fec_archive ) {
198 36966 : fd_gui_fec_event_batch_t const * b = iter->_hist_iter.rec;
199 36966 : if( b->max_ns<iter->_after_ns || b->min_ns>=iter->_before_ns ) continue;
200 12345 : iter->_batch = &b->batch;
201 265665 : } else iter->_batch = iter->_hist_iter.rec;
202 278010 : continue;
203 302631 : }
204 10995 : if( iter->_hist_active ) fd_gui_hist_range_end( &iter->_hist_iter );
205 10995 : iter->_hist_active = 0;
206 10995 : if( iter->_builder_pending ) {
207 5472 : iter->_builder_pending = 0;
208 5472 : iter->_batch = &iter->_gui->shreds.builder.batch;
209 5472 : }
210 10995 : }
211 :
212 : /* Staged FEC events, only set up by fd_gui_fec_event_iter_begin. */
213 3157422 : while( fd_gui_store_kv_scan_next( &iter->_fec_iter ) ) {
214 3151929 : fd_gui_fec_event_t const * src = iter->_fec_iter.rec;
215 3151929 : if( src->insert_time_ns>=iter->_fec_insert_hi ) {
216 30 : iter->_fec_iter.db = NULL;
217 30 : break;
218 30 : }
219 3151899 : if( src->event_time_ns<iter->_after_ns || src->event_time_ns>=iter->_before_ns ) continue;
220 3148827 : iter->event = fec_shred_event( src );
221 3148827 : return 1;
222 3151899 : }
223 5523 : return 0;
224 3154350 : }
225 :
226 : void
227 6429 : fd_gui_shred_event_iter_end( fd_gui_shred_event_iter_t * iter ) {
228 6429 : if( iter->_hist_active ) fd_gui_hist_range_end( &iter->_hist_iter );
229 6429 : iter->_hist_active = 0;
230 6429 : iter->_builder_pending = 0;
231 6429 : iter->_batch = NULL;
232 6429 : iter->_fec_iter.db = NULL;
233 6429 : }
234 :
235 : static void
236 903 : batch_flush( fd_gui_t * gui ) {
237 903 : fd_gui_shred_batch_t * batch = &gui->shreds.builder.batch;
238 903 : if( !batch->event_cnt ) return;
239 903 : if( FD_UNLIKELY( fd_gui_hist_ts_append( gui, FD_GUI_HIST_SHRED_EVENTS, batch ) ) ) {
240 0 : gui->shreds.dropped_event_cnt += batch->event_cnt;
241 0 : }
242 903 : batch->event_cnt = 0U;
243 903 : batch->data_sz = 0U;
244 903 : }
245 :
246 : void
247 : fd_gui_shred_event_append( fd_gui_t * gui,
248 : ulong slot,
249 : ulong idx,
250 : uchar event,
251 : long timestamp,
252 316980 : long now ) {
253 316980 : fd_gui_shred_builder_t * builder = &gui->shreds.builder;
254 316980 : int marker = event==FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE;
255 316980 : if( FD_UNLIKELY( !gui->db || !gui->hist || slot>UINT_MAX || idx>USHORT_MAX ||
256 316980 : (marker ? idx!=USHORT_MAX : idx==USHORT_MAX) ||
257 316980 : (event>FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE && event!=FD_GUI_SLOT_SHRED_SHRED_PUBLISHED) ||
258 316980 : (!marker && (timestamp<0L || timestamp==LONG_MAX || slot<fd_gui_event_closed_slot_wmark( gui ))) ||
259 316980 : now<0L || now==LONG_MAX || now<builder->prev.insert_time_ns ) ) {
260 17904 : gui->shreds.dropped_event_cnt++;
261 17904 : return;
262 17904 : }
263 299076 : if( event==FD_GUI_SLOT_SHRED_SHRED_RECEIVED_TURBINE || event==FD_GUI_SLOT_SHRED_SHRED_RECEIVED_REPAIR ||
264 299076 : event==FD_GUI_SLOT_SHRED_SHRED_PUBLISHED ) {
265 : /* Index the first arrival per slot for the timeline; the raw events
266 : cannot be looked up by slot. Producer timestamps need not arrive
267 : in order. */
268 245793 : fd_gui_epoch_t * epoch = fd_gui_get_epoch_by_slot( gui, slot );
269 245793 : if( epoch && slot>=epoch->start_slot && slot-epoch->start_slot<fd_ulong_min( epoch->slot_cnt, MAX_SLOTS_PER_EPOCH ) ) {
270 219 : ulong i = slot-epoch->start_slot;
271 219 : uchar state = epoch->timeline_slot_state[ i ];
272 219 : epoch->timeline_slot_first_shred_ns[ i ] = (state & FD_GUI_TIMELINE_SLOT_STATE_STARTED)
273 219 : ? fd_long_min( epoch->timeline_slot_first_shred_ns[ i ], timestamp ) : timestamp;
274 219 : epoch->timeline_slot_state[ i ] = (uchar)(state | FD_GUI_TIMELINE_SLOT_STATE_STARTED);
275 219 : }
276 245793 : }
277 299076 : fd_gui_shred_batch_t * batch = &builder->batch;
278 299076 : long window_ns = (long)shred_window( now )*FD_GUI_HIST_RES_1S_NS;
279 299076 : if( batch->event_cnt && batch->insert_time_ns!=window_ns ) batch_flush( gui );
280 299076 : batch->insert_time_ns = window_ns;
281 299076 : fd_gui_shred_event_t rec = { .insert_time_ns=now, .event_time_ns=timestamp, .slot=(uint)slot, .idx=(ushort)idx, .event=event };
282 299076 : if( FD_UNLIKELY( !batch_append( builder, &rec ) ) ) {
283 813 : batch_flush( gui );
284 813 : FD_TEST( batch_append( builder, &rec ) );
285 813 : }
286 299076 : }
287 :
288 : int
289 : fd_gui_shred_flush( fd_gui_t * gui,
290 39 : long now ) {
291 39 : if( FD_UNLIKELY( !gui->db || !gui->hist ) ) return 0;
292 39 : fd_gui_shred_batch_t const * batch = &gui->shreds.builder.batch;
293 39 : if( !batch->event_cnt || shred_window( batch->insert_time_ns )>=shred_window( now ) ) return 0;
294 36 : batch_flush( gui );
295 36 : return 1;
296 39 : }
297 :
298 : ulong
299 6605286 : fd_gui_event_closed_slot_wmark( fd_gui_t const * gui ) {
300 6605286 : return gui->shreds.closed_slot_wmark;
301 6605286 : }
302 :
303 : /* fec_slot_hi_wmark returns the FEC staging high watermark: slots at or
304 : above it are not staged. */
305 :
306 : static inline ulong
307 3154587 : fec_slot_hi_wmark( fd_gui_t const * gui ) {
308 3154587 : return fd_ulong_sat_add( gui->shreds.closed_slot_wmark, FD_GUI_FEC_SLOT_WMARK_CNT );
309 3154587 : }
310 :
311 : void
312 : fd_gui_fec_event_iter_begin( fd_gui_t * gui,
313 : fd_gui_shred_event_iter_t * iter,
314 : long after_ns,
315 60 : long before_ns ) {
316 60 : shred_iter_begin( gui, iter, 1, 1, after_ns, before_ns );
317 60 : }
318 :
319 : /* fec_event_skewed tests whether timestamp is more than
320 : FD_GUI_FEC_EVENT_LAG_MAX_NS from the admission time insert_ns.
321 : Skewed events are not stored, so event-time queries can find every
322 : stored event from its admission time. */
323 :
324 : static int
325 : fec_event_skewed( long insert_ns,
326 3148923 : long timestamp ) {
327 3148923 : return timestamp<fd_long_sat_sub( insert_ns, FD_GUI_FEC_EVENT_LAG_MAX_NS ) ||
328 3148923 : timestamp>fd_long_sat_add( insert_ns, FD_GUI_FEC_EVENT_LAG_MAX_NS );
329 3148923 : }
330 :
331 : void
332 : fd_gui_fec_event_append( fd_gui_t * gui,
333 : ulong slot,
334 : ulong idx,
335 : uchar event,
336 : long timestamp,
337 3154614 : long now ) {
338 3154614 : int marker = event==FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE;
339 3154614 : if( FD_UNLIKELY( !gui->db || !gui->hist || slot>UINT_MAX || idx>USHORT_MAX ||
340 3154614 : (marker ? idx!=USHORT_MAX : idx==USHORT_MAX) || timestamp<0L || timestamp==LONG_MAX ||
341 3154614 : now<0L || now==LONG_MAX ||
342 3154614 : (event>FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE && event!=FD_GUI_SLOT_SHRED_SHRED_PUBLISHED) ||
343 3154614 : (!marker && slot<fd_gui_event_closed_slot_wmark( gui )) || slot>=fec_slot_hi_wmark( gui ) ||
344 3154614 : (marker && gui->shreds.fec_marker_sequence==ULONG_MAX) ) ) {
345 5685 : gui->shreds.dropped_fec_event_cnt++;
346 5685 : return;
347 5685 : }
348 : /* Staging admission order must be monotone in admission time, since
349 : it becomes the archive order. */
350 3148929 : now = fd_long_max( now, gui->shreds.fec_insert_time_ns );
351 3148929 : fd_gui_fec_event_key_t key = { .slot=(uint)slot, .idx=(ushort)idx, .event=event };
352 3148929 : fd_gui_fec_event_t * dst = marker ? NULL : fd_gui_hist_kv_get( gui, FD_GUI_HIST_FEC_EVENTS, &key );
353 3148929 : if( dst && timestamp>=dst->event_time_ns ) return;
354 3148923 : if( FD_UNLIKELY( fec_event_skewed( dst ? dst->insert_time_ns : now, timestamp ) ) ) {
355 9 : gui->shreds.clamped_fec_event_cnt++;
356 9 : return;
357 9 : }
358 3148914 : if( dst ) {
359 6 : dst->event_time_ns = timestamp;
360 6 : return;
361 6 : }
362 3148908 : if( marker ) key.marker_sequence = ++gui->shreds.fec_marker_sequence;
363 3148908 : dst = fd_gui_hist_kv_get_or_create( gui, FD_GUI_HIST_FEC_EVENTS, &key );
364 3148908 : if( FD_UNLIKELY( !dst ) ) {
365 3 : gui->shreds.dropped_fec_event_cnt++;
366 3 : return;
367 3 : }
368 3148905 : *dst = (fd_gui_fec_event_t){ .key=key, .insert_time_ns=now, .event_time_ns=timestamp };
369 3148905 : gui->shreds.fec_insert_time_ns = now;
370 3148905 : }
371 :
372 : void
373 : fd_gui_shred_event_slot_complete( fd_gui_t * gui,
374 : ulong slot,
375 : long timestamp,
376 66 : long now ) {
377 66 : if( FD_UNLIKELY( slot>UINT_MAX ) ) return;
378 66 : fd_gui_shred_event_append( gui, slot, USHORT_MAX, FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE, timestamp, now );
379 66 : fd_gui_fec_event_append( gui, slot, USHORT_MAX, FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE, timestamp, now );
380 66 : }
381 :
382 : void
383 : fd_gui_event_closed_slot_wmark_advance( fd_gui_t * gui,
384 39 : ulong wmark ) {
385 39 : if( FD_UNLIKELY( wmark==ULONG_MAX || wmark<=gui->shreds.closed_slot_wmark ) ) return;
386 36 : gui->shreds.closed_slot_wmark = wmark;
387 36 : }
388 :
389 : int
390 : fd_gui_event_bounds( fd_gui_t * gui,
391 : int dbi,
392 : long * lo,
393 27 : long * hi ) {
394 27 : if( dbi==FD_GUI_HIST_FEC_EVENTS ) {
395 : /* Bound by admission time, widened by the lag bound and batch span
396 : rather than scanning event times. */
397 9 : long first = LONG_MAX;
398 9 : long last = LONG_MIN;
399 9 : long a_first, a_last;
400 9 : if( gui->db && fd_gui_store_ts_live_timestamp_bounds( gui->db, FD_GUI_HIST_FEC_EVENT_ARCHIVE, &a_first, &a_last ) ) {
401 6 : first = a_first;
402 6 : last = fd_long_sat_add( a_last, FD_GUI_FEC_BATCH_SPAN_NS );
403 6 : }
404 9 : if( gui->db ) {
405 9 : fd_gui_store_kv_scan_t it;
406 9 : fd_gui_store_kv_scan_begin( gui->db, &it, FD_GUI_HIST_FEC_EVENTS );
407 9 : if( fd_gui_store_kv_scan_next( &it ) ) {
408 6 : first = fd_long_min( first, ((fd_gui_fec_event_t const *)it.rec)->insert_time_ns );
409 6 : last = fd_long_max( last, gui->shreds.fec_insert_time_ns );
410 6 : }
411 9 : }
412 9 : int have = first!=LONG_MAX;
413 9 : *lo = have ? fd_long_max( 0L, fd_long_sat_sub( first, FD_GUI_FEC_EVENT_LAG_MAX_NS ) ) : LONG_MAX;
414 9 : *hi = have ? fd_long_min( LONG_MAX-1L, fd_long_sat_add( last, FD_GUI_FEC_EVENT_LAG_MAX_NS+1L ) ) : LONG_MIN;
415 9 : return have;
416 9 : }
417 18 : long first;
418 18 : long last;
419 18 : int have = gui->db && fd_gui_store_ts_live_timestamp_bounds( gui->db, (ulong)dbi, &first, &last );
420 18 : if( dbi==FD_GUI_HIST_SHRED_EVENTS && gui->shreds.builder.batch.event_cnt ) {
421 9 : long active = gui->shreds.builder.batch.insert_time_ns;
422 9 : first = have ? fd_long_min( first, active ) : active;
423 9 : last = have ? fd_long_max( last, active ) : active;
424 9 : have = 1;
425 9 : }
426 :
427 18 : if( have && dbi==FD_GUI_HIST_SHRED_EVENTS ) last = fd_long_sat_add( last, FD_GUI_HIST_RES_1S_NS-1L );
428 18 : *lo = have ? fd_long_max( 0L, fd_long_sat_sub( first, FD_GUI_HIST_RES_1S_NS ) ) : LONG_MAX;
429 18 : *hi = have ? fd_long_min( LONG_MAX-1L, fd_long_sat_add( last, FD_GUI_HIST_RES_1S_NS+1L ) ) : LONG_MIN;
430 18 : return have;
431 27 : }
432 :
433 : int
434 : fd_gui_shred_window_is_empty( fd_gui_t * gui,
435 : long after_ns,
436 918 : long before_ns ) {
437 918 : fd_gui_shred_event_iter_t it[ 1 ];
438 918 : fd_gui_shred_event_iter_begin( gui, it, after_ns, before_ns );
439 918 : int empty = !fd_gui_shred_event_iter_next( it );
440 918 : fd_gui_shred_event_iter_end( it );
441 918 : return empty;
442 918 : }
|