LCOV - code coverage report
Current view: top level - discof/repair - fd_inflight.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 54 54 100.0 %
Date: 2026-09-17 04:28:31 Functions: 15 63 23.8 %

          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, 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 chainer, 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. */
      44             : 
      45             : /* Max number of pending requests */
      46         192 : #define FD_INFLIGHT_REQ_MAX (1<<20)
      47             : 
      48             : struct fd_inflight_key {
      49             :   ulong slot;
      50             :   uint  idx;        /* shred idx (shred kinds) or fec_set_idx (AG_REPAIR_KIND_FEC_ROOT) */
      51             :   uint  nonce;      /* rnonce or counter nonce (metadata) */
      52             :   uint  kind;       /* FD_REPAIR_KIND_SHRED for every shred request, else AG_REPAIR_KIND_{PARENT_FEC_COUNT,FEC_ROOT} */
      53             :   /* shred kinds: first FD_SHRED_MERKLE_NODE_SZ bytes of the FEC root a
      54             :      ShredForBlockId request was issued against, all-zero for a
      55             :      positional request.  All-zero for metadata kinds. */
      56             :   uchar fec_root[ FD_SHRED_MERKLE_NODE_SZ ];
      57             : };
      58             : typedef struct fd_inflight_key fd_inflight_key_t;
      59             : FD_STATIC_ASSERT( sizeof(fd_inflight_key_t)==40UL, fd_inflight_key_sz );
      60             : 
      61             : /* fd_inflight_key_init fills key.  For shred requests fec_root is the
      62             :    (full or 20-byte-padded) FEC root the request targets, or NULL for a
      63             :    positional request; only the first FD_SHRED_MERKLE_NODE_SZ bytes are
      64             :    kept.  For metadata kinds pass NULL. */
      65             : static inline void
      66             : fd_inflight_key_init( fd_inflight_key_t * key,
      67             :                       uint                kind,
      68             :                       ulong               slot,
      69             :                       ulong               idx,
      70             :                       ulong               nonce,
      71        4047 :                       fd_hash_t const *   fec_root ) {
      72        4047 :   key->slot  = slot;
      73        4047 :   key->idx   = (uint)idx;
      74        4047 :   key->nonce = (uint)nonce;
      75        4047 :   key->kind  = kind;
      76        4047 :   if( FD_LIKELY( fec_root ) ) memcpy( key->fec_root, fec_root->uc, FD_SHRED_MERKLE_NODE_SZ );
      77         375 :   else                        memset( key->fec_root, 0,            FD_SHRED_MERKLE_NODE_SZ );
      78        4047 : }
      79             : 
      80             : /* Key equality and hashing.  A shred request is identified by the whole
      81             :    key.  A metadata request is identified by its nonce alone -- the
      82             :    counter nonce is unique -- so its kind, slot and idx are not
      83             :    considered when matching. */
      84             : 
      85             : static inline int
      86       13587 : fd_inflight_key_is_shred( fd_inflight_key_t const * k ) { return k->kind==FD_REPAIR_KIND_SHRED; }
      87             : 
      88             : static inline int
      89             : fd_inflight_key_eq( fd_inflight_key_t const * k0,
      90        1896 :                     fd_inflight_key_t const * k1 ) {
      91        1896 :   if( FD_UNLIKELY( k0->nonce!=k1->nonce ) )                                           return 0;
      92        1896 :   if( FD_UNLIKELY( fd_inflight_key_is_shred( k0 )!=fd_inflight_key_is_shred( k1 ) ) ) return 0;
      93        1896 :   if( FD_UNLIKELY( !fd_inflight_key_is_shred( k0 ) ) )                                return 1;
      94        1740 :   return ( k0->slot==k1->slot ) & ( k0->idx==k1->idx ) & !memcmp( k0->fec_root, k1->fec_root, FD_SHRED_MERKLE_NODE_SZ );
      95        1896 : }
      96             : 
      97             : static inline ulong
      98             : fd_inflight_key_hash( fd_inflight_key_t const * k,
      99        7899 :                       ulong                     seed ) {
     100        7899 :   if( FD_UNLIKELY( !fd_inflight_key_is_shred( k ) ) ) return fd_hash( seed, &k->nonce, sizeof(uint) );
     101        7209 :   return fd_hash( seed, k, sizeof(fd_inflight_key_t) );
     102        7899 : }
     103             : 
     104             : struct __attribute__((aligned(128UL))) fd_inflight {
     105             :   fd_inflight_key_t key;
     106             :   uint              next;          /* reserved for internal use by fd_pool and fd_map_chain */
     107             :   uint              prev;          /* for fd_map_chain */
     108             :   uint              prevll;        /* for fd_inflight_dlist */
     109             :   uint              nextll;
     110             :   long              timestamp_ns;  /* when the request was created (caller's clock, nanoseconds) */
     111             : 
     112             :   fd_pubkey_t       pubkey;        /* peer the request went to (all-zero if parked unsent) */
     113             : 
     114             :   /* Version being repaired: the block_id of a ShredForBlockId or
     115             :      metadata request, all-zero for a positional shred request. */
     116             :   fd_hash_t         block_id;
     117             : };
     118             : typedef struct fd_inflight fd_inflight_t;
     119             : FD_STATIC_ASSERT( sizeof(fd_inflight_t)==128UL, fd_inflight_sz );
     120             : 
     121             : #define POOL_NAME   fd_inflight_pool
     122          96 : #define POOL_T      fd_inflight_t
     123             : #define POOL_IDX_T  uint
     124             : #include "../../util/tmpl/fd_pool.c"
     125             : 
     126             : #define MAP_NAME            fd_inflight_map
     127        2181 : #define MAP_KEY             key
     128          33 : #define MAP_ELE_T           fd_inflight_t
     129             : #define MAP_KEY_T           fd_inflight_key_t
     130        7983 : #define MAP_IDX_T           uint
     131        1896 : #define MAP_KEY_EQ(k0, k1)  fd_inflight_key_eq( (k0), (k1) )
     132        7899 : #define MAP_KEY_HASH(k,s)   fd_inflight_key_hash( (k), (s) )
     133             : #define MAP_MULTI           1 /* the same shred request within one rnonce time bucket */
     134             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     135             : #include "../../util/tmpl/fd_map_chain.c"
     136             : 
     137             : #define DLIST_NAME      fd_inflight_dlist
     138             : #define DLIST_ELE_T     fd_inflight_t
     139             : #define DLIST_IDX_T     uint
     140        4035 : #define DLIST_PREV      prevll
     141        4068 : #define DLIST_NEXT      nextll
     142             : #include "../../util/tmpl/fd_dlist.c"
     143             : 
     144             : struct fd_inflights {
     145             :   fd_inflight_t       * pool;
     146             :   fd_inflight_map_t   * map;             /* OUTSTANDING */
     147             :   fd_inflight_map_t   * popped_map;      /* POPPED */
     148             :   fd_inflight_dlist_t   outstanding_dl[1];
     149             :   fd_inflight_dlist_t   popped_dl[1];
     150             :   ulong                 popped_cnt;
     151             : };
     152             : typedef struct fd_inflights fd_inflights_t;
     153             : 
     154             : FD_FN_CONST static inline ulong
     155         432 : fd_inflights_align( void ) { return 128UL; }
     156             : 
     157             : FD_FN_CONST static inline ulong
     158          96 : fd_inflights_footprint( void ) {
     159          96 :   ulong chain_cnt = fd_inflight_map_chain_cnt_est( FD_INFLIGHT_REQ_MAX );
     160          96 :   return FD_LAYOUT_FINI(
     161          96 :     FD_LAYOUT_APPEND(
     162          96 :     FD_LAYOUT_APPEND(
     163          96 :     FD_LAYOUT_APPEND(
     164          96 :     FD_LAYOUT_APPEND(
     165          96 :     FD_LAYOUT_INIT,
     166          96 :       alignof(fd_inflights_t),  sizeof(fd_inflights_t)                            ),
     167          96 :       fd_inflight_pool_align(), fd_inflight_pool_footprint( FD_INFLIGHT_REQ_MAX ) ),
     168          96 :       fd_inflight_map_align(),  fd_inflight_map_footprint ( chain_cnt           ) ),
     169          96 :       fd_inflight_map_align(),  fd_inflight_map_footprint ( chain_cnt           ) ),
     170          96 :     fd_inflights_align() );
     171          96 : }
     172             : 
     173             : void *
     174             : fd_inflights_new( void * shmem,
     175             :                   ulong  seed );
     176             : 
     177             : fd_inflights_t *
     178             : fd_inflights_join( void * shmem );
     179             : 
     180             : /* Timestamps.  Every insert and match takes now, the caller's current
     181             :    time in nanoseconds, rather than reading a clock itself: records are
     182             :    stamped with it on insert, fd_inflights_shred_match reports RTT
     183             :    against it, and fd_inflights_should_drain compares against it.  All
     184             :    calls on a table must use the same clock (the tile clock,
     185             :    fd_clock_tile_now) so age and RTT are consistent. */
     186             : 
     187             : /* fd_inflights_shred_insert records a shred request to pubkey.
     188             :    block_id is the ShredForBlockId version being repaired and fec_root
     189             :    the root the chainer holds for that version at the requested FEC set
     190             :    (the key a response is matched by); both NULL (or all-zero) for a
     191             :    positional request. */
     192             : 
     193             : void
     194             : fd_inflights_shred_insert( fd_inflights_t *    table,
     195             :                            ulong               nonce,
     196             :                            fd_pubkey_t const * pubkey,
     197             :                            ulong               slot,
     198             :                            ulong               shred_idx,
     199             :                            fd_hash_t const *   block_id,
     200             :                            fd_hash_t const *   fec_root,
     201             :                            long                now );
     202             : 
     203             : /* fd_inflights_shred_match matches a shred response.  fec_root is the
     204             :    response shred's merkle root (only the first FD_SHRED_MERKLE_NODE_SZ
     205             :    bytes are keyed on), or NULL to match a positional request.  Removes
     206             :    every record with that key from both the outstanding and popped sets
     207             :    and credits the response to the oldest: returns its RTT in
     208             :    nanoseconds relative to now (>0), or 0 if nothing matched.  On a
     209             :    match *peer_out is set, and *block_id_out (if non-NULL) to the
     210             :    matched request's block_id. */
     211             : 
     212             : long
     213             : fd_inflights_shred_match( fd_inflights_t *  table,
     214             :                           ulong             nonce,
     215             :                           ulong             slot,
     216             :                           ulong             shred_idx,
     217             :                           fd_hash_t const * fec_root,
     218             :                           fd_pubkey_t *     peer_out,
     219             :                           fd_hash_t *       block_id_out,
     220             :                           long              now );
     221             : 
     222             : /* fd_inflights_meta_insert records a metadata request to pubkey.  kind
     223             :    is AG_REPAIR_KIND_PARENT_FEC_COUNT or AG_REPAIR_KIND_FEC_ROOT;
     224             :    fec_set_idx is meaningful for the latter only. */
     225             : 
     226             : void
     227             : fd_inflights_meta_insert( fd_inflights_t *    table,
     228             :                           ulong               nonce,
     229             :                           uint                kind,
     230             :                           fd_pubkey_t const * pubkey,
     231             :                           ulong               slot,
     232             :                           fd_hash_t const *   block_id,
     233             :                           uint                fec_set_idx,
     234             :                           long                now );
     235             : 
     236             : /* fd_inflights_meta_match matches a metadata response by nonce in the
     237             :    outstanding then the popped set.  On a match the record is copied to
     238             :    *out (out->key.kind is the kind that was requested), removed, and 1
     239             :    is returned; 0 otherwise. */
     240             : 
     241             : int
     242             : fd_inflights_meta_match( fd_inflights_t * table,
     243             :                          ulong            nonce,
     244             :                          fd_inflight_t *  out );
     245             : 
     246             : /* fd_inflights_should_drain returns 1 if the oldest outstanding request
     247             :    has aged past FD_REQLIM_DEDUP_TIMEOUT and should be redispatched. */
     248             : 
     249             : static inline int
     250        2226 : fd_inflights_should_drain( fd_inflights_t * table, long now ) {
     251        2226 :   if( FD_UNLIKELY( fd_inflight_dlist_is_empty( table->outstanding_dl, table->pool ) ) ) return 0;
     252        2130 :   fd_inflight_t * head = fd_inflight_dlist_ele_peek_head( table->outstanding_dl, table->pool );
     253        2130 :   return head->timestamp_ns + FD_REQLIM_DEDUP_TIMEOUT < now;
     254        2226 : }
     255             : 
     256             : /* fd_inflights_pop copies the oldest outstanding request to *out and
     257             :    moves it to the popped set, so a late response to its nonce still
     258             :    matches.  A record with nonce 0 (parked by the caller, never sent) is
     259             :    released instead, since nothing can match it.  The outstanding set
     260             :    must be non-empty: only call this after fd_inflights_should_drain
     261             :    returns 1. */
     262             : 
     263             : void
     264             : fd_inflights_pop( fd_inflights_t * table,
     265             :                   fd_inflight_t *  out );
     266             : 
     267             : /* fd_inflights_outstanding_free returns how many new requests can be
     268             :    inserted before an insert would have to evict an OUTSTANDING record
     269             :    (FREE records plus evictable POPPED ones). */
     270             : 
     271             : static inline ulong
     272        2211 : fd_inflights_outstanding_free( fd_inflights_t * table ) {
     273        2211 :   return fd_inflight_pool_free( table->pool ) + table->popped_cnt;
     274        2211 : }
     275             : 
     276             : /* fd_inflights_outstanding_cnt returns the number of OUTSTANDING
     277             :    records. */
     278             : 
     279             : static inline ulong
     280          18 : fd_inflights_outstanding_cnt( fd_inflights_t * table ) {
     281          18 :   return fd_inflight_pool_used( table->pool ) - table->popped_cnt;
     282          18 : }
     283             : 
     284             : void
     285             : fd_inflights_print( fd_inflight_dlist_t * dlist, fd_inflight_t * pool );
     286             : 
     287             : #endif /* HEADER_fd_src_discof_repair_fd_inflight_h */

Generated by: LCOV version 1.14