Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions book/api/websocket.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,9 +324,11 @@ Frankendancer client will always publish `null` for this message
| *Once* | `CatchUpHistory` | see below |

This validator records a history of all slots that were received from
turbine as well as slots for which a repair request was made while it is
turbine or repair responses, as well as shred events that occurred while
catching up. After catching up, slots are no longer recorded in this
history.
history. For repair and turbine slots, the history is available for the
lifetime of the validator. Shred events are only available if the
validator is in the catching up phase.

::: details Example

Expand All @@ -336,13 +338,29 @@ history.
"key": "catch_up_history",
"value": {
"repair": [11, 12, 13, ...],
"turbine": [21, 22, 23, ...]
"turbine": [21, 22, 23, ...],
"shreds": {
"reference_slot": 289245044,
"reference_ts": "1739657041588242791",
"slot_delta": [0, 0],
"shred_idx": [1234, null],
"event": [0, 1],
"event_ts_delta": ["1000000", "2000000"]
}
}
}
```

:::

**`CatchUpHistory`**
| Field | Type | Description |
|------------|---------------|-------------|
| repair | `number[]` | A list of all slots for which a repair shred was received that are older than `summary.caught_up_slot` |
| turbine | `number[]` | A list of all slots for which a turbine shred was received that are older than `summary.caught_up_slot` |
| shreds | `SlotShreds` | A list of shred events which have occurred for this validator in the past 15 seconds. If the validator has already caught up, or has not yet started catching up, then `null` |


#### `summary.startup_time_nanos`
| frequency | type | example |
|-----------|----------|---------------------|
Expand Down Expand Up @@ -1594,7 +1612,7 @@ rooted.
#### `slot.live_shreds`
| frequency | type | example |
|-------------|---------------|---------|
| *10ms* | `SlotShred[]` | below |
| *10ms* | `SlotShreds` | below |

The validator sends a continous stream of update messages with detailed
information about the time and duration of different shred state
Expand All @@ -1620,7 +1638,7 @@ and is broadcast to all WebSocket clients.

:::

**`SlotShred`**
**`SlotShreds`**
| Field | Type | Description |
|-----------------|--------------------|-------------|
| reference_slot | `number`   | The smallest slot number across all the shreds in a given message |
Expand All @@ -1633,7 +1651,7 @@ and is broadcast to all WebSocket clients.
#### `slot.query_shreds`
| frequency | type | example |
|-------------|---------------|---------|
| *Request* | `SlotShred[]\null` | below |
| *Request* | `SlotShreds\|null` | below |

| param | type | description |
|-------|----------|-------------|
Expand Down
2 changes: 1 addition & 1 deletion src/disco/gui/fd_gui.c
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,7 @@ fd_gui_request_slot_shreds( fd_gui_t * gui,

fd_gui_slot_t const * slot = fd_gui_get_slot( gui, _slot );
if( FD_UNLIKELY( !slot || gui->shreds.history_tail > slot->shreds.end_offset + FD_GUI_SHREDS_HISTORY_SZ ) ) {
fd_gui_printf_null_query_response( gui->http, "slot", "query_rankings", request_id );
fd_gui_printf_null_query_response( gui->http, "slot", "query_shreds", request_id );
FD_TEST( !fd_http_server_ws_send( gui->http, ws_conn_id ) );
return 0;
}
Expand Down
134 changes: 124 additions & 10 deletions src/disco/gui/fd_gui_printf.c
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,112 @@ fd_gui_printf_catch_up_history( fd_gui_t * gui ) {
}
}
jsonp_close_array( gui->http );

if( FD_LIKELY( gui->summary.boot_progress.phase==FD_GUI_BOOT_PROGRESS_TYPE_CATCHING_UP ) ) {
ulong min_slot = ULONG_MAX;
long min_ts = LONG_MAX;

for( ulong i=gui->shreds.staged_tail; i>0UL && i>gui->shreds.staged_head; i-- ) {
long ts = gui->shreds.staged[ (i-1UL) % FD_GUI_SHREDS_STAGING_SZ ].timestamp;
if( FD_UNLIKELY( ts < gui->summary.boot_progress.catching_up_time_nanos - 15000000000 ) ) break;
ulong slot = gui->shreds.staged[ (i-1UL) % FD_GUI_SHREDS_STAGING_SZ ].slot;
min_slot = fd_ulong_min( min_slot, slot );
min_ts = fd_long_min( min_ts, ts );
}

fd_gui_slot_t * s = fd_gui_get_slot( gui, gui->shreds.history_slot );
while( s
&& s->shreds.start_offset!=ULONG_MAX
&& s->shreds.end_offset!=ULONG_MAX
&& gui->shreds.history[ s->shreds.start_offset % FD_GUI_SHREDS_HISTORY_SZ ].timestamp < (gui->summary.boot_progress.catching_up_time_nanos - 15000000000) ) {
min_slot = fd_ulong_min( min_slot, s->slot );
min_ts = fd_long_min( min_ts, gui->shreds.history[ s->shreds.start_offset % FD_GUI_SHREDS_HISTORY_SZ ].timestamp );
s = fd_gui_get_slot( gui, s->parent_slot );
}

jsonp_open_object( gui->http, "shreds" );
jsonp_ulong ( gui->http, "reference_slot", min_slot );
jsonp_long_as_str( gui->http, "reference_ts", min_ts );

jsonp_open_array( gui->http, "slot_delta" );
for( ulong i=gui->shreds.staged_tail; i>0UL && i>gui->shreds.staged_head; i-- ) {
long ts = gui->shreds.staged[ (i-1UL) % FD_GUI_SHREDS_STAGING_SZ ].timestamp;
if( FD_UNLIKELY( ts < gui->summary.boot_progress.catching_up_time_nanos - 15000000000 ) ) break;
jsonp_ulong( gui->http, NULL, gui->shreds.staged[ (i-1UL) % FD_GUI_SHREDS_STAGING_SZ ].slot-min_slot );
}

s = fd_gui_get_slot( gui, gui->shreds.history_slot );
while( s
&& s->shreds.start_offset!=ULONG_MAX
&& s->shreds.end_offset!=ULONG_MAX
&& gui->shreds.history[ s->shreds.start_offset % FD_GUI_SHREDS_HISTORY_SZ ].timestamp < (gui->summary.boot_progress.catching_up_time_nanos - 15000000000) ) {
for( ulong i=s->shreds.start_offset; i<s->shreds.end_offset; i++ ) {
jsonp_ulong( gui->http, NULL, s->slot-min_slot );
}
s = fd_gui_get_slot( gui, s->parent_slot );
}
jsonp_close_array( gui->http );
jsonp_open_array( gui->http, "shred_idx" );
for( ulong i=gui->shreds.staged_tail; i>0UL && i>gui->shreds.staged_head; i-- ) {
long ts = gui->shreds.staged[ (i-1UL) % FD_GUI_SHREDS_STAGING_SZ ].timestamp;
if( FD_UNLIKELY( ts < gui->summary.boot_progress.catching_up_time_nanos - 15000000000 ) ) break;
if( FD_LIKELY( gui->shreds.staged[ (i-1UL) % FD_GUI_SHREDS_STAGING_SZ ].shred_idx!=USHORT_MAX ) ) jsonp_ulong( gui->http, NULL, gui->shreds.staged[ (i-1UL) % FD_GUI_SHREDS_STAGING_SZ ].shred_idx );
else jsonp_null ( gui->http, NULL );
}

s = fd_gui_get_slot( gui, gui->shreds.history_slot );
while( s
&& s->shreds.start_offset!=ULONG_MAX
&& s->shreds.end_offset!=ULONG_MAX
&& gui->shreds.history[ s->shreds.start_offset % FD_GUI_SHREDS_HISTORY_SZ ].timestamp < (gui->summary.boot_progress.catching_up_time_nanos - 15000000000) ) {
for( ulong i=s->shreds.start_offset; i<s->shreds.end_offset; i++ ) {
if( FD_LIKELY( gui->shreds.history[ i % FD_GUI_SHREDS_HISTORY_SZ ].shred_idx!=USHORT_MAX ) ) jsonp_ulong( gui->http, NULL, gui->shreds.history[ i % FD_GUI_SHREDS_HISTORY_SZ ].shred_idx );
else jsonp_null ( gui->http, NULL );
}
s = fd_gui_get_slot( gui, s->parent_slot );
}
jsonp_close_array( gui->http );
jsonp_open_array( gui->http, "event" );
for( ulong i=gui->shreds.staged_tail; i>0UL && i>gui->shreds.staged_head; i-- ) {
long ts = gui->shreds.staged[ (i-1UL) % FD_GUI_SHREDS_STAGING_SZ ].timestamp;
if( FD_UNLIKELY( ts < gui->summary.boot_progress.catching_up_time_nanos - 15000000000 ) ) break;
jsonp_ulong( gui->http, NULL, gui->shreds.staged[ (i-1UL) % FD_GUI_SHREDS_STAGING_SZ ].event );
}

s = fd_gui_get_slot( gui, gui->shreds.history_slot );
while( s
&& s->shreds.start_offset!=ULONG_MAX
&& s->shreds.end_offset!=ULONG_MAX
&& gui->shreds.history[ s->shreds.start_offset % FD_GUI_SHREDS_HISTORY_SZ ].timestamp < (gui->summary.boot_progress.catching_up_time_nanos - 15000000000) ) {
for( ulong i=s->shreds.start_offset; i<s->shreds.end_offset; i++ ) {
jsonp_ulong( gui->http, NULL, gui->shreds.history[ i % FD_GUI_SHREDS_HISTORY_SZ ].event );
}
s = fd_gui_get_slot( gui, s->parent_slot );
}
jsonp_close_array( gui->http );
jsonp_open_array( gui->http, "event_ts_delta" );
for( ulong i=gui->shreds.staged_tail; i>0UL && i>gui->shreds.staged_head; i-- ) {
long ts = gui->shreds.staged[ (i-1UL) % FD_GUI_SHREDS_STAGING_SZ ].timestamp;
if( FD_UNLIKELY( ts < gui->summary.boot_progress.catching_up_time_nanos - 15000000000 ) ) break;
jsonp_long_as_str( gui->http, NULL, gui->shreds.staged[ (i-1UL) % FD_GUI_SHREDS_STAGING_SZ ].timestamp-min_ts );
}

s = fd_gui_get_slot( gui, gui->shreds.history_slot );
while( s
&& s->shreds.start_offset!=ULONG_MAX
&& s->shreds.end_offset!=ULONG_MAX
&& gui->shreds.history[ s->shreds.start_offset % FD_GUI_SHREDS_HISTORY_SZ ].timestamp < (gui->summary.boot_progress.catching_up_time_nanos - 15000000000) ) {
for( ulong i=s->shreds.start_offset; i<s->shreds.end_offset; i++ ) {
jsonp_long_as_str( gui->http, NULL, gui->shreds.history[ i % FD_GUI_SHREDS_HISTORY_SZ ].timestamp-min_ts );
}
s = fd_gui_get_slot( gui, s->parent_slot );
}
jsonp_close_array( gui->http );
jsonp_close_object( gui->http );
} else {
jsonp_null( gui->http, "shreds" );
}

jsonp_close_object( gui->http );
jsonp_close_envelope( gui->http );
}
Expand Down Expand Up @@ -2120,37 +2226,45 @@ void
fd_gui_printf_slot_shred_updates( fd_gui_t * gui,
ulong _slot,
ulong id ) {
ulong _start_offset = gui->slots[ _slot % FD_GUI_SLOTS_CNT ]->shreds.start_offset;
ulong _end_offset = gui->slots[ _slot % FD_GUI_SLOTS_CNT ]->shreds.end_offset;
fd_gui_slot_t * slot = fd_gui_get_slot( gui, _slot );

if( FD_UNLIKELY( !slot || slot->shreds.end_offset <= gui->shreds.history_tail-FD_GUI_SHREDS_HISTORY_SZ ) ) {
jsonp_open_envelope( gui->http, "slot", "query_shreds" );
jsonp_ulong( gui->http, "id", id );
jsonp_null( gui->http, "value" );
jsonp_close_envelope( gui->http );
return;
}

ulong _start_offset = slot->shreds.start_offset;
ulong _end_offset = slot->shreds.end_offset;

ulong min_slot = ULONG_MAX;
long min_ts = LONG_MAX;

for( ulong i=_start_offset; i<_end_offset; i++ ) {
min_slot = fd_ulong_min( min_slot, gui->shreds.staged[ i % FD_GUI_SHREDS_STAGING_SZ ].slot );
min_ts = fd_long_min ( min_ts, gui->shreds.staged[ i % FD_GUI_SHREDS_STAGING_SZ ].timestamp );
min_ts = fd_long_min ( min_ts, gui->shreds.history[ i % FD_GUI_SHREDS_HISTORY_SZ ].timestamp );
}

jsonp_open_envelope( gui->http, "slot", "query_shreds" );
jsonp_ulong( gui->http, "id", id );
jsonp_open_object( gui->http, "value" );
jsonp_ulong ( gui->http, "reference_slot", min_slot );
jsonp_ulong ( gui->http, "reference_slot", _slot );
jsonp_long_as_str( gui->http, "reference_ts", min_ts );

jsonp_open_array( gui->http, "slot_delta" );
for( ulong i=_start_offset; i<_end_offset; i++ ) jsonp_ulong( gui->http, NULL, gui->shreds.staged[ i % FD_GUI_SHREDS_STAGING_SZ ].slot-min_slot );
for( ulong i=_start_offset; i<_end_offset; i++ ) jsonp_ulong( gui->http, NULL, 0UL );
jsonp_close_array( gui->http );
jsonp_open_array( gui->http, "shred_idx" );
for( ulong i=_start_offset; i<_end_offset; i++ ) {
if( FD_LIKELY( gui->shreds.staged[ i % FD_GUI_SHREDS_STAGING_SZ ].shred_idx!=USHORT_MAX ) ) jsonp_ulong( gui->http, NULL, gui->shreds.staged[ i % FD_GUI_SHREDS_STAGING_SZ ].shred_idx );
if( FD_LIKELY( gui->shreds.staged[ i % FD_GUI_SHREDS_STAGING_SZ ].shred_idx!=USHORT_MAX ) ) jsonp_ulong( gui->http, NULL, gui->shreds.history[ i % FD_GUI_SHREDS_HISTORY_SZ ].shred_idx );
else jsonp_null ( gui->http, NULL );
}
jsonp_close_array( gui->http );
jsonp_open_array( gui->http, "event" );
for( ulong i=_start_offset; i<_end_offset; i++ ) jsonp_ulong( gui->http, NULL, gui->shreds.staged[ i % FD_GUI_SHREDS_STAGING_SZ ].event );
for( ulong i=_start_offset; i<_end_offset; i++ ) jsonp_ulong( gui->http, NULL, gui->shreds.history[ i % FD_GUI_SHREDS_HISTORY_SZ ].event );
jsonp_close_array( gui->http );
jsonp_open_array( gui->http, "event_ts_delta" );
for( ulong i=_start_offset; i<_end_offset; i++ ) jsonp_long_as_str( gui->http, NULL, gui->shreds.staged[ i % FD_GUI_SHREDS_STAGING_SZ ].timestamp-min_ts );
for( ulong i=_start_offset; i<_end_offset; i++ ) jsonp_long_as_str( gui->http, NULL, gui->shreds.history[ i % FD_GUI_SHREDS_HISTORY_SZ ].timestamp-min_ts );
jsonp_close_array( gui->http );
jsonp_close_object( gui->http );
jsonp_close_envelope( gui->http );
Expand Down
Loading