LCOV - code coverage report
Current view: top level - discof/restore/utils - fd_ssctrl.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 64 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 72 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_discof_restore_utils_fd_ssctrl_h
       2             : #define HEADER_fd_src_discof_restore_utils_fd_ssctrl_h
       3             : 
       4             : #include "../../../util/net/fd_net_headers.h"
       5             : #include "../../../waltz/fd_fqdn.h"
       6             : #include "../../../flamenco/runtime/fd_runtime_const.h"
       7             : 
       8             : /* The snapshot tiles have a somewhat involved state machine, which is
       9             :    controlled by snapct.  Imagine first the following sequence:
      10             : 
      11             :     1. snapct is reading a full snapshot from the network and sends some
      12             :        data to snapdc to be decompressed.
      13             :     2. snapct hits a network error, and resets the connection to a new
      14             :        peer.
      15             :     3. The decompressor fails on data from the old peer, and sends a
      16             :        malformed message to snapct.
      17             :     4. snapct receives the malformed message, and abandons the new
      18             :        connection, even though it was not malformed.
      19             : 
      20             :    There are basically two ways to prevent this.  Option A is the tiles
      21             :    can pass not just control messages to one another, but also tag them
      22             :    with some xid indicating which "attempt" the control message is for.
      23             : 
      24             :    This is pretty hard to reason about, and the state machine can grow
      25             :    quite complicated.
      26             : 
      27             :    There's an easier way: the tiles just are fully synchronized with
      28             :    snapct.  Whatever "attempt" snapct is on, we ensure all other tiles
      29             :    are on it too.  This means when any tile fails a snapshot, all tiles
      30             :    must fail it and fully flush all frags in the pipeline before snapct
      31             :    can proceed with a new attempt.
      32             : 
      33             :    The control flow then is basically,
      34             : 
      35             :      1. All tiles start in the IDLE state.
      36             :      2. snapct initializes the pipeline by sending an INIT message.
      37             :         Each tile enters the PROCESSING state and then forwards the INIT
      38             :         message down the pipeline.  When snapct receives this INIT
      39             :         message from every path, the entire pipeline is in PROCESSING
      40             :         state.  snapct then sends START with the fetch target to snapld,
      41             :         which begins producing snapshot data.
      42             :      3. Tiles continue to process data / frags as applicable.  If an
      43             :         error occurs, the tile enters the ERROR state and also sends an
      44             :         ERROR message downstream.  All downstream tiles also enter the
      45             :         ERROR state and forward the message.  Note that upstream tiles
      46             :         will not be in an ERROR state and will continue producing frags.
      47             :         When snapct receives the ERROR message, it will send a FAIL
      48             :         message.  snapct then waits for this FAIL message to be
      49             :         propagated through the pipeline and received back.  snapct
      50             :         also guarantees to flush any pending load data in the
      51             :         pipeline.  It then knows that all tiles are synchronized back
      52             :         in an IDLE state and it can try again with a new INIT.
      53             :      4. Once all snapshot data has entered the pipeline, snapct sends
      54             :         a FINI message through the pipeline and waits for it to be
      55             :         received back.  This synchronizes tiles to finish any
      56             :         remaining in-flight work.  It then sends either a NEXT or a
      57             :         DONE message through the pipeline, again waiting for it to be
      58             :         received back.  NEXT means another snapshot follows, whereas
      59             :         DONE means a shutdown will follow.
      60             : 
      61             :    That keeps the tiles in lockstep, and simplifies the state machine
      62             :    to a manageable level.
      63             : 
      64             :    In more detail, all tiles in the feedback loop of the pipeline must
      65             :    comply with the following rules; snapct enforces them; tiles outside
      66             :    of the feedback loop may support a subset of these:
      67             :      - Allowed state transitions on given control message:
      68             :         IDLE       to PROCESSING: on INIT_FULL / INIT_INCR ctrl msg.
      69             :         PROCESSING to FINISHING : on FINI ctrl msg (*).
      70             :         FINISHING  to IDLE      : on NEXT / DONE ctrl msg.
      71             :         IDLE       to SHUTDOWN  : on SHUTDOWN ctrl msg.
      72             :      - Control messages that apply to all states (except SHUTDOWN):
      73             :         On ERROR msg, always transition to ERROR.
      74             :         On FAIL msg, always transition to IDLE.
      75             :      - START is sent only from snapct to snapld.  It is not forwarded
      76             :        through the feedback loop and does not change snapld's state.
      77             :      - When in SHUTDOWN state, no data or control message is allowed.
      78             :      - Error handling:
      79             :         A tile that enters ERROR state on its own must forward an
      80             :         ERROR control message and discard any incoming message.  When
      81             :         in ERROR state, data is discarded, and only a FAIL control
      82             :         message is processed and forwarded (all others are discarded).
      83             :         As a result, only one ERROR message can propagate through the
      84             :         pipeline at any given time.
      85             :      - Holding onto a control message:
      86             :         A tile may hold onto a control message and forward it later
      87             :         after performing some asynchronous routine.  The tile's state
      88             :         transition may also be deferred until that control message is
      89             :         forwarded.
      90             :      - (*) A tile may self-transition from PROCESSING to FINISHING
      91             :         early, if it can detect end-of-stream.  The FINI message is
      92             :         used to synchronize the transition.
      93             : 
      94             :    Each non-ERROR control message is only generated once in snapct and
      95             :    will not be re-sent.  Except for START, the pipeline will be locked
      96             :    on flushing that control message until all tiles forward it on, or an
      97             :    ERROR message is triggered by any of the tiles and forwarded. */
      98             : 
      99             : /* Snapshot data MTU and snapld-to-snapdc link depth. */
     100           0 : #define FD_SNAPSHOT_DATA_DEPTH                 (1024UL)
     101             : 
     102             : /* Every snapin is a reliable consumer on every snapdc lane.  This
     103             :    runway keeps one stalled snapin from blocking all decompressors. */
     104           0 : #define FD_SNAPSHOT_DC_IN_DEPTH                (2048UL)
     105             : 
     106           0 : #define FD_SNAPSHOT_DATA_MTU                   (65408UL)
     107             : 
     108           0 : #define FD_SNAPSHOT_STATE_IDLE                 (0UL) /* Performing no work and should receive no data frags */
     109           0 : #define FD_SNAPSHOT_STATE_PROCESSING           (1UL) /* Performing usual work, no errors / EoF condition encountered */
     110           0 : #define FD_SNAPSHOT_STATE_FINISHING            (2UL) /* Tile has observed EoF, expects no additional data frags */
     111           0 : #define FD_SNAPSHOT_STATE_ERROR                (3UL) /* Some error occurred, will wait for a FAIL command to reset */
     112           0 : #define FD_SNAPSHOT_STATE_SHUTDOWN             (4UL) /* All work finished, tile can perform final cleanup and exit */
     113             : 
     114           0 : #define FD_SNAPSHOT_MSG_DATA                   (0UL) /* Fragment represents some snapshot data */
     115           0 : #define FD_SNAPSHOT_MSG_META                   (1UL) /* Fragment represents a fd_ssctrl_meta_t message */
     116             : 
     117           0 : #define FD_SNAPSHOT_MSG_CTRL_INIT_FULL         (2UL) /* Pipeline should start processing a full snapshot */
     118           0 : #define FD_SNAPSHOT_MSG_CTRL_INIT_INCR         (3UL) /* Pipeline should start processing an incremental snapshot */
     119           0 : #define FD_SNAPSHOT_MSG_CTRL_START             (4UL) /* snapct -> snapld: all INIT paths acknowledged */
     120           0 : #define FD_SNAPSHOT_MSG_CTRL_FAIL              (5UL) /* Current snapshot failed, queue rollback and reset to idle */
     121           0 : #define FD_SNAPSHOT_MSG_CTRL_NEXT              (6UL) /* Current snapshot succeeded, commit work, go idle, and expect another snapshot */
     122           0 : #define FD_SNAPSHOT_MSG_CTRL_DONE              (7UL) /* Current snapshot succeeded, commit work, go idle, and expect shutdown */
     123           0 : #define FD_SNAPSHOT_MSG_CTRL_SHUTDOWN          (8UL) /* Snapshot load successful, no work left to do, perform final cleanup and shut down*/
     124           0 : #define FD_SNAPSHOT_MSG_CTRL_ERROR             (9UL) /* Some tile encountered an error with the current stream */
     125           0 : #define FD_SNAPSHOT_MSG_CTRL_FINI             (10UL) /* Current snapshot has been fully loaded, finish processing */
     126             : 
     127             : /* snapld -> snapct (via snapld_dc) */
     128           0 : #define FD_SNAPSHOT_MSG_LOAD_COMPLETE         (11UL) /* snapld finished reading/downloading all data */
     129             : 
     130             : /* Sigs passed from snapdc to snapin contain the frame index of the
     131             :    frag so snapin knows how to order frames as they arrive. */
     132             : 
     133           0 : #define FD_SNAPDC_SIG_TYPE_BITS (8UL)
     134             : 
     135             : FD_FN_CONST static inline ulong
     136           0 : fd_snapdc_data_sig( ulong frame_idx ) {
     137           0 :   return FD_SNAPSHOT_MSG_DATA | (frame_idx<<FD_SNAPDC_SIG_TYPE_BITS);
     138           0 : }
     139             : 
     140             : FD_FN_CONST static inline ulong
     141           0 : fd_snapdc_sig_type( ulong sig ) {
     142           0 :   return sig & ((1UL<<FD_SNAPDC_SIG_TYPE_BITS)-1UL);
     143           0 : }
     144             : 
     145             : FD_FN_CONST static inline ulong
     146           0 : fd_snapdc_sig_frame( ulong sig ) {
     147           0 :   return sig>>FD_SNAPDC_SIG_TYPE_BITS;
     148           0 : }
     149             : 
     150             : /* Pipeline initialization, forwarded by snapld through snapdc to
     151             :    snapin. */
     152             : typedef struct fd_ssctrl_init {
     153             :   int           file;
     154             :   int           zstd;
     155             :   ulong         slot; /* slot advertised by the snapshot peer */
     156             :   uchar         snapshot_hash[ FD_HASH_FOOTPRINT ]; /* advertised snapshot hash from snapshot file name */
     157             :   int           is_redirect; /* 1 if using well-known redirect path */
     158             :   ulong         file_sz;     /* file size in bytes (file loads only, 0 for HTTP) */
     159             : } fd_ssctrl_init_t;
     160             : 
     161             : /* Fetch target sent only from snapct to snapld after all INIT acks.
     162             :    For file loads, START has no payload. */
     163             : typedef struct fd_ssctrl_start {
     164             :   fd_ip4_port_t addr;
     165             :   int           is_https;
     166             :   ulong         path_len;
     167             :   char          hostname[ FD_FQDN_BUF_MAX ];
     168             :   char          path[ PATH_MAX ];
     169             : } fd_ssctrl_start_t;
     170             : 
     171             : /* The fragment signature selects the payload.  Publish only the
     172             :    selected member's size; the union sizes the shared snapct -> snapld
     173             :    buffer. */
     174             : typedef union fd_ssctrl_msg {
     175             :   fd_ssctrl_init_t  init;
     176             :   fd_ssctrl_start_t start;
     177             : } fd_ssctrl_msg_t;
     178             : 
     179             : /* Sent by snapld to tell snapct metadata about a downloaded snapshot. */
     180             : typedef struct fd_ssctrl_meta {
     181             :   ulong total_sz;
     182             :   ulong resolved_slot;
     183             :   uchar resolved_hash[ FD_HASH_FOOTPRINT ];
     184             :   char  resolved_name[ PATH_MAX ];
     185             : } fd_ssctrl_meta_t;
     186             : 
     187             : struct fd_snapshot_account_hdr {
     188             :   uchar   pubkey[ FD_PUBKEY_FOOTPRINT ];
     189             :   uchar   owner[ FD_PUBKEY_FOOTPRINT ];
     190             :   ulong   lamports;
     191             :   uchar   executable;
     192             :   ulong   data_len;
     193             : };
     194             : typedef struct fd_snapshot_account_hdr fd_snapshot_account_hdr_t;
     195             : 
     196             : /* fd_snapshot_account_hdr_init initializes a fd_snapshot_account_hdr_t struct
     197             :    with the appropriate account metadata fields. */
     198             : static inline void
     199             : fd_snapshot_account_hdr_init( fd_snapshot_account_hdr_t * account,
     200             :                            uchar const                    pubkey[ FD_PUBKEY_FOOTPRINT ],
     201             :                            uchar const                    owner[ FD_PUBKEY_FOOTPRINT ],
     202             :                            ulong                          lamports,
     203             :                            uchar                          executable,
     204           0 :                            ulong                          data_len ) {
     205           0 :   fd_memcpy( account->pubkey, pubkey, FD_PUBKEY_FOOTPRINT );
     206           0 :   fd_memcpy( account->owner,  owner,  FD_PUBKEY_FOOTPRINT );
     207           0 :   account->lamports   = lamports;
     208           0 :   account->executable = executable;
     209           0 :   account->data_len   = data_len;
     210           0 : }
     211             : 
     212             : static inline const char *
     213           0 : fd_ssctrl_state_str( ulong state ) {
     214           0 :   switch( state ) {
     215           0 :     case FD_SNAPSHOT_STATE_IDLE:        return "idle";
     216           0 :     case FD_SNAPSHOT_STATE_PROCESSING:  return "processing";
     217           0 :     case FD_SNAPSHOT_STATE_FINISHING:   return "finishing";
     218           0 :     case FD_SNAPSHOT_STATE_ERROR:       return "error";
     219           0 :     case FD_SNAPSHOT_STATE_SHUTDOWN:    return "shutdown";
     220           0 :     default:                            return "unknown";
     221           0 :   }
     222           0 : }
     223             : 
     224             : static inline const char *
     225           0 : fd_ssctrl_msg_ctrl_str( ulong sig ) {
     226           0 :   switch( fd_snapdc_sig_type( sig ) ) {
     227           0 :     case FD_SNAPSHOT_MSG_DATA:                  return "data";
     228           0 :     case FD_SNAPSHOT_MSG_META:                  return "meta";
     229           0 :     case FD_SNAPSHOT_MSG_CTRL_INIT_FULL:        return "init_full";
     230           0 :     case FD_SNAPSHOT_MSG_CTRL_INIT_INCR:        return "init_incr";
     231           0 :     case FD_SNAPSHOT_MSG_CTRL_START:            return "start";
     232           0 :     case FD_SNAPSHOT_MSG_CTRL_FAIL:             return "fail";
     233           0 :     case FD_SNAPSHOT_MSG_CTRL_NEXT:             return "next";
     234           0 :     case FD_SNAPSHOT_MSG_CTRL_DONE:             return "done";
     235           0 :     case FD_SNAPSHOT_MSG_CTRL_SHUTDOWN:         return "shutdown";
     236           0 :     case FD_SNAPSHOT_MSG_CTRL_ERROR:            return "error";
     237           0 :     case FD_SNAPSHOT_MSG_CTRL_FINI:             return "fini";
     238           0 :     case FD_SNAPSHOT_MSG_LOAD_COMPLETE:         return "load_complete";
     239           0 :     default:                                    return "unknown";
     240           0 :   }
     241           0 : }
     242             : 
     243             : #endif /* HEADER_fd_src_discof_restore_utils_fd_ssctrl_h */

Generated by: LCOV version 1.14