LCOV - code coverage report
Current view: top level - discof/repair - fd_inflight.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 62 0.0 %
Date: 2026-10-15 04:28:26 Functions: 0 40 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_discof_repair_fd_inflight_h
       2             : #define HEADER_fd_src_discof_repair_fd_inflight_h
       3             : 
       4             : #include "fd_policy.h"
       5             : #include "../../ballet/shred/fd_shred.h"
       6             : 
       7             : /* fd_inflight tracks repair requests that are inflight to other
       8             :    validators, so that a response can be credited to the request (and
       9             :    peer) that solicited it and a request that gets no response can be
      10             :    redispatched after a timeout.  Request kinds:
      11             : 
      12             :    - Shred requests -- positional FD_REPAIR_KIND_SHRED and Alpenglow
      13             :      ShredForBlockId, which are indistinguishable on response -- are
      14             :      keyed by (slot, shred_idx, nonce, kind, fec_root).  fec_root is
      15             :      all zero for a positional request (i.e., we didnt know the FEC
      16             :      root when the request was issued).  For a ShredForBlockId request,
      17             :      it is the 20-byte prefix of the known FEC root.
      18             : 
      19             :    - Metadata requests -- getParentAndFecSetCount and getFecSetRoot --
      20             :      are matched by nonce alone.  Their kind, slot and FEC set index
      21             :      ride in the key for redispatch (and so the caller can reject a
      22             :      response of the wrong kind) but do not participate in matching.
      23             : 
      24             :    Exact updates of shred requests are critical: the repair policy does
      25             :    not request any shred twice, so re-requests come only from this
      26             :    table.  Whether a shred belongs to a version is decided structurally
      27             :    by the rotor, which keys FECs by root; this table is request
      28             :    accounting only.
      29             : 
      30             :    Each record is FREE, OUTSTANDING, or POPPED:
      31             : 
      32             :             insert                  pop
      33             :      FREE  --------> OUTSTANDING -----------> POPPED
      34             :       ^                  |                      |
      35             :       |     match        |  match, or evicted   |
      36             :       -------------------------------------------
      37             : 
      38             :    OUTSTANDING records are in map and outstanding_dl (insertion order,
      39             :    oldest at head).  A record that ages past FD_REQLIM_DEDUP_TIMEOUT is
      40             :    popped: the caller redispatches it under a fresh nonce and the record
      41             :    moves to popped_map / popped_dl rather than being released, so a late
      42             :    response to the old nonce still matches.  When the pool is exhausted
      43             :    the oldest POPPED record is evicted first.  A caller that does not
      44             :    redispatch from the table releases aged records with
      45             :    fd_inflights_expire instead of popping them. */
      46             : 
      47             : /* Max number of pending requests */
      48           0 : #define FD_INFLIGHT_REQ_MAX (1<<20)
      49             : 
      50             : struct fd_inflight_key {
      51             :   ulong slot;
      52             :   uint  idx;        /* shred idx (shred kinds) or fec_set_idx (AG_REPAIR_KIND_FEC_ROOT) */
      53             :   uint  nonce;      /* rnonce or counter nonce (metadata) */
      54             :   uint  kind;       /* FD_REPAIR_KIND_SHRED, AG_REPAIR_KIND_SHRED_FOR_BLOCK_ID, AG_REPAIR_KIND_PARENT_FEC_COUNT or AG_REPAIR_KIND_FEC_ROOT */
      55             :   /* shred kinds: first FD_SHRED_MERKLE_NODE_SZ bytes of the FEC root a
      56             :      ShredForBlockId request was issued against, all-zero for a
      57             :      positional request.  All-zero for metadata kinds. */
      58             :   uchar fec_root[ FD_SHRED_MERKLE_NODE_SZ ];
      59             : };
      60             : typedef struct fd_inflight_key fd_inflight_key_t;
      61             : FD_STATIC_ASSERT( sizeof(fd_inflight_key_t)==40UL, fd_inflight_key_sz );
      62             : 
      63             : /* fd_inflight_key_init fills key.  For shred requests fec_root is the
      64             :    (full or 20-byte-padded) FEC root the request targets, or NULL for a
      65             :    positional request; only the first FD_SHRED_MERKLE_NODE_SZ bytes are
      66             :    kept.  For metadata kinds pass NULL. */
      67             : static inline void
      68             : fd_inflight_key_init( fd_inflight_key_t * key,
      69             :                       uint                kind,
      70             :                       ulong               slot,
      71             :                       ulong               idx,
      72             :                       ulong               nonce,
      73           0 :                       fd_hash_t const *   fec_root ) {
      74           0 :   key->slot  = slot;
      75           0 :   key->idx   = (uint)idx;
      76           0 :   key->nonce = (uint)nonce;
      77           0 :   key->kind  = kind;
      78           0 :   if( FD_LIKELY( fec_root ) ) memcpy( key->fec_root, fec_root->uc, FD_SHRED_MERKLE_NODE_SZ );
      79           0 :   else                        memset( key->fec_root, 0,            FD_SHRED_MERKLE_NODE_SZ );
      80           0 : }
      81             : 
      82             : /* Key equality and hashing.  A shred request is identified by the whole
      83             :    key.  A metadata request is identified by its nonce alone -- the
      84             :    counter nonce is unique -- so its kind, slot and idx are not
      85             :    considered when matching. */
      86             : 
      87             : static inline int
      88           0 : fd_inflight_key_is_shred( fd_inflight_key_t const * k ) {
      89           0 :   return k->kind==FD_REPAIR_KIND_SHRED || k->kind==AG_REPAIR_KIND_SHRED_FOR_BLOCK_ID;
      90           0 : }
      91             : 
      92             : static inline int
      93             : fd_inflight_key_eq( fd_inflight_key_t const * k0,
      94           0 :                     fd_inflight_key_t const * k1 ) {
      95           0 :   if( FD_UNLIKELY( k0->nonce!=k1->nonce ) )                                           return 0;
      96           0 :   if( FD_UNLIKELY( fd_inflight_key_is_shred( k0 )!=fd_inflight_key_is_shred( k1 ) ) ) return 0;
      97           0 :   if( FD_UNLIKELY( !fd_inflight_key_is_shred( k0 ) ) )                                return 1;
      98           0 :   return ( k0->slot==k1->slot ) & ( k0->idx==k1->idx ) & ( k0->kind==k1->kind ) & !memcmp( k0->fec_root, k1->fec_root, FD_SHRED_MERKLE_NODE_SZ );
      99           0 : }
     100             : 
     101             : static inline ulong
     102             : fd_inflight_key_hash( fd_inflight_key_t const * k,
     103           0 :                       ulong                     seed ) {
     104           0 :   if( FD_UNLIKELY( !fd_inflight_key_is_shred( k ) ) ) return fd_hash( seed, &k->nonce, sizeof(uint) );
     105           0 :   return fd_hash( seed, k, sizeof(fd_inflight_key_t) );
     106           0 : }
     107             : 
     108             : struct __attribute__((aligned(128UL))) fd_inflight {
     109             :   fd_inflight_key_t key;
     110             :   uint              next;          /* reserved for internal use by fd_pool and fd_map_chain */
     111             :   uint              prev;          /* for fd_map_chain */
     112             :   uint              prevll;        /* for fd_inflight_dlist */
     113             :   uint              nextll;
     114             :   long              timestamp_ns;  /* when the request was created (caller's clock, nanoseconds) */
     115             : 
     116             :   fd_pubkey_t       pubkey;        /* peer the request went to (all-zero if parked unsent) */
     117             : 
     118             :   /* Version being repaired: the block_id of a ShredForBlockId or
     119             :      metadata request, all-zero for a positional shred request. */
     120             :   fd_hash_t         block_id;
     121             : };
     122             : typedef struct fd_inflight fd_inflight_t;
     123             : FD_STATIC_ASSERT( sizeof(fd_inflight_t)==128UL, fd_inflight_sz );
     124             : 
     125             : #define POOL_NAME   fd_inflight_pool
     126           0 : #define POOL_T      fd_inflight_t
     127             : #define POOL_IDX_T  uint
     128             : #include "../../util/tmpl/fd_pool.c"
     129             : 
     130             : #define MAP_NAME            fd_inflight_map
     131           0 : #define MAP_KEY             key
     132           0 : #define MAP_ELE_T           fd_inflight_t
     133             : #define MAP_KEY_T           fd_inflight_key_t
     134           0 : #define MAP_IDX_T           uint
     135           0 : #define MAP_KEY_EQ(k0, k1)  fd_inflight_key_eq( (k0), (k1) )
     136           0 : #define MAP_KEY_HASH(k,s)   fd_inflight_key_hash( (k), (s) )
     137             : #define MAP_MULTI           1 /* the same shred request within one rnonce time bucket */
     138             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     139             : #include "../../util/tmpl/fd_map_chain.c"
     140             : 
     141             : #define DLIST_NAME      fd_inflight_dlist
     142             : #define DLIST_ELE_T     fd_inflight_t
     143             : #define DLIST_IDX_T     uint
     144           0 : #define DLIST_PREV      prevll
     145           0 : #define DLIST_NEXT      nextll
     146             : #include "../../util/tmpl/fd_dlist.c"
     147             : 
     148             : struct fd_inflights {
     149             :   fd_inflight_t       * pool;
     150             :   fd_inflight_map_t   * map;             /* OUTSTANDING */
     151             :   fd_inflight_map_t   * popped_map;      /* POPPED */
     152             :   fd_inflight_dlist_t   outstanding_dl[1];
     153             :   fd_inflight_dlist_t   popped_dl[1];
     154             :   ulong                 popped_cnt;
     155             : };
     156             : typedef struct fd_inflights fd_inflights_t;
     157             : 
     158             : FD_FN_CONST static inline ulong
     159           0 : fd_inflights_align( void ) { return 128UL; }
     160             : 
     161             : FD_FN_CONST static inline ulong
     162           0 : fd_inflights_footprint( void ) {
     163           0 :   ulong chain_cnt = fd_inflight_map_chain_cnt_est( FD_INFLIGHT_REQ_MAX );
     164           0 :   return FD_LAYOUT_FINI(
     165           0 :     FD_LAYOUT_APPEND(
     166           0 :     FD_LAYOUT_APPEND(
     167           0 :     FD_LAYOUT_APPEND(
     168           0 :     FD_LAYOUT_APPEND(
     169           0 :     FD_LAYOUT_INIT,
     170           0 :       alignof(fd_inflights_t),  sizeof(fd_inflights_t)                            ),
     171           0 :       fd_inflight_pool_align(), fd_inflight_pool_footprint( FD_INFLIGHT_REQ_MAX ) ),
     172           0 :       fd_inflight_map_align(),  fd_inflight_map_footprint ( chain_cnt           ) ),
     173           0 :       fd_inflight_map_align(),  fd_inflight_map_footprint ( chain_cnt           ) ),
     174           0 :     fd_inflights_align() );
     175           0 : }
     176             : 
     177             : void *
     178             : fd_inflights_new( void * shmem,
     179             :                   ulong  seed );
     180             : 
     181             : fd_inflights_t *
     182             : fd_inflights_join( void * shmem );
     183             : 
     184             : /* Timestamps.  Every insert and match takes now, the caller's current
     185             :    time in nanoseconds, rather than reading a clock itself: records are
     186             :    stamped with it on insert, fd_inflights_shred_match reports RTT
     187             :    against it, and fd_inflights_should_drain compares against it.  All
     188             :    calls on a table must use the same clock (the tile clock,
     189             :    fd_clock_tile_now) so age and RTT are consistent. */
     190             : 
     191             : /* fd_inflights_shred_insert records a shred request of kind
     192             :    FD_REPAIR_KIND_SHRED (positional) or AG_REPAIR_KIND_SHRED_FOR_BLOCK_ID
     193             :    to pubkey.  block_id is the ShredForBlockId version being repaired
     194             :    and fec_root the root the rotor holds for that version at the
     195             :    requested FEC set (the key a response is matched by); both NULL (or
     196             :    all-zero) for a positional request. */
     197             : 
     198             : void
     199             : fd_inflights_shred_insert( fd_inflights_t *    table,
     200             :                            uint                kind,
     201             :                            ulong               nonce,
     202             :                            fd_pubkey_t const * pubkey,
     203             :                            ulong               slot,
     204             :                            ulong               shred_idx,
     205             :                            fd_hash_t const *   block_id,
     206             :                            fd_hash_t const *   fec_root,
     207             :                            long                now );
     208             : 
     209             : /* fd_inflights_shred_match matches a shred response against requests
     210             :    of kind (FD_REPAIR_KIND_SHRED or AG_REPAIR_KIND_SHRED_FOR_BLOCK_ID).
     211             :    fec_root is the response shred's merkle root (only the first
     212             :    FD_SHRED_MERKLE_NODE_SZ bytes are keyed on), or NULL to match a
     213             :    positional request.  Removes
     214             :    every record with that key from both the outstanding and popped sets
     215             :    and credits the response to the oldest: returns its RTT in
     216             :    nanoseconds relative to now (>0), or 0 if nothing matched.  On a
     217             :    match *peer_out is set, and *block_id_out (if non-NULL) to the
     218             :    matched request's block_id. */
     219             : 
     220             : long
     221             : fd_inflights_shred_match( fd_inflights_t *  table,
     222             :                           uint              kind,
     223             :                           ulong             nonce,
     224             :                           ulong             slot,
     225             :                           ulong             shred_idx,
     226             :                           fd_hash_t const * fec_root,
     227             :                           fd_pubkey_t *     peer_out,
     228             :                           fd_hash_t *       block_id_out,
     229             :                           long              now );
     230             : 
     231             : /* fd_inflights_meta_insert records a metadata request to pubkey.  kind
     232             :    is AG_REPAIR_KIND_PARENT_FEC_COUNT or AG_REPAIR_KIND_FEC_ROOT;
     233             :    fec_set_idx is meaningful for the latter only. */
     234             : 
     235             : void
     236             : fd_inflights_meta_insert( fd_inflights_t *    table,
     237             :                           ulong               nonce,
     238             :                           uint                kind,
     239             :                           fd_pubkey_t const * pubkey,
     240             :                           ulong               slot,
     241             :                           fd_hash_t const *   block_id,
     242             :                           uint                fec_set_idx,
     243             :                           long                now );
     244             : 
     245             : /* fd_inflights_meta_match matches a metadata response by nonce in the
     246             :    outstanding then the popped set.  On a match the record is copied to
     247             :    *out (out->key.kind is the kind that was requested), removed, and 1
     248             :    is returned; 0 otherwise. */
     249             : 
     250             : int
     251             : fd_inflights_meta_match( fd_inflights_t * table,
     252             :                          ulong            nonce,
     253             :                          fd_inflight_t *  out );
     254             : 
     255             : /* fd_inflights_should_drain returns 1 if the oldest outstanding request
     256             :    has aged past FD_REQLIM_DEDUP_TIMEOUT and should be redispatched. */
     257             : 
     258             : static inline int
     259           0 : fd_inflights_should_drain( fd_inflights_t * table, long now ) {
     260           0 :   if( FD_UNLIKELY( fd_inflight_dlist_is_empty( table->outstanding_dl, table->pool ) ) ) return 0;
     261           0 :   fd_inflight_t * head = fd_inflight_dlist_ele_peek_head( table->outstanding_dl, table->pool );
     262           0 :   return head->timestamp_ns + FD_REQLIM_DEDUP_TIMEOUT < now;
     263           0 : }
     264             : 
     265             : /* fd_inflights_pop copies the oldest outstanding request to *out and
     266             :    moves it to the popped set, so a late response to its nonce still
     267             :    matches.  A record with nonce 0 (parked by the caller, never sent) is
     268             :    released instead, since nothing can match it.  The outstanding set
     269             :    must be non-empty: only call this after fd_inflights_should_drain
     270             :    returns 1. */
     271             : 
     272             : void
     273             : fd_inflights_pop( fd_inflights_t * table,
     274             :                   fd_inflight_t *  out );
     275             : 
     276             : /* fd_inflights_expire releases, oldest first, up to max OUTSTANDING or
     277             :    POPPED records inserted before cutoff and returns how many.  For
     278             :    callers that never redispatch from the table. */
     279             : 
     280             : ulong
     281             : fd_inflights_expire( fd_inflights_t * table,
     282             :                      long             cutoff,
     283             :                      ulong            max );
     284             : 
     285             : /* fd_inflights_oldest_ts returns the insert time of the oldest
     286             :    OUTSTANDING or POPPED record (the next one fd_inflights_expire
     287             :    releases), LONG_MAX if there is none. */
     288             : 
     289             : static inline long
     290           0 : fd_inflights_oldest_ts( fd_inflights_t * table ) {
     291           0 :   long ts = LONG_MAX;
     292           0 :   if( FD_LIKELY( !fd_inflight_dlist_is_empty( table->outstanding_dl, table->pool ) ) ) ts = fd_inflight_dlist_ele_peek_head( table->outstanding_dl, table->pool )->timestamp_ns;
     293           0 :   if( FD_UNLIKELY( !fd_inflight_dlist_is_empty( table->popped_dl, table->pool ) ) ) ts = fd_long_min( ts, fd_inflight_dlist_ele_peek_head( table->popped_dl, table->pool )->timestamp_ns );
     294           0 :   return ts;
     295           0 : }
     296             : 
     297             : /* fd_inflights_outstanding_free returns how many new requests can be
     298             :    inserted before an insert would have to evict an OUTSTANDING record
     299             :    (FREE records plus evictable POPPED ones). */
     300             : 
     301             : static inline ulong
     302           0 : fd_inflights_outstanding_free( fd_inflights_t * table ) {
     303           0 :   return fd_inflight_pool_free( table->pool ) + table->popped_cnt;
     304           0 : }
     305             : 
     306             : /* fd_inflights_outstanding_cnt returns the number of OUTSTANDING
     307             :    records. */
     308             : 
     309             : static inline ulong
     310           0 : fd_inflights_outstanding_cnt( fd_inflights_t * table ) {
     311           0 :   return fd_inflight_pool_used( table->pool ) - table->popped_cnt;
     312           0 : }
     313             : 
     314             : void
     315             : fd_inflights_print( fd_inflight_dlist_t * dlist, fd_inflight_t * pool );
     316             : 
     317             : #endif /* HEADER_fd_src_discof_repair_fd_inflight_h */

Generated by: LCOV version 1.14