Line data Source code
1 : #ifndef HEADER_fd_src_discof_restore_utils_fd_sspeer_selector_h 2 : #define HEADER_fd_src_discof_restore_utils_fd_sspeer_selector_h 3 : 4 : /* The snapshot peer selector (sspeer_selector) continuously selects 5 : the most optimal snapshot peer to download snapshots from. The 6 : most optimal peer is defined as the closest peer that serves the 7 : most recent snapshot. */ 8 : 9 : #include "../../../util/fd_util_base.h" 10 : #include "../../../util/net/fd_net_headers.h" 11 : #include "../../../flamenco/types/fd_types_custom.h" 12 : #include "fd_sspeer.h" 13 : 14 90 : #define FD_SSPEER_SELECTOR_MAGIC (0xF17EDA2CE5593350) /* FIREDANCE SSPING V0 */ 15 : 16 : /* Sentinel score returned by fd_sspeer_selector_best when no peer was 17 : found and by fd_sspeer_selector_add on failure. */ 18 87 : #define FD_SSPEER_SCORE_INVALID (ULONG_MAX) 19 : 20 : /* Maximum score a valid peer can have. FD_SSPEER_SCORE_MAX ensures a 21 : valid peer's score is never confused with FD_SSPEER_SCORE_INVALID. */ 22 525 : #define FD_SSPEER_SCORE_MAX (ULONG_MAX-1UL) 23 : 24 : /* Sentinel value indicating that a snapshot slot (full or incremental) 25 : is unknown or absent. */ 26 1314 : #define FD_SSPEER_SLOT_UNKNOWN (ULONG_MAX) 27 : 28 : /* Sentinel value indicating that peer latency has not been measured. */ 29 654 : #define FD_SSPEER_LATENCY_UNKNOWN (ULONG_MAX) 30 : 31 : /* Return codes for fd_sspeer_selector_update_on_resolve. */ 32 114 : #define FD_SSPEER_UPDATE_SUCCESS ( 0) 33 3 : #define FD_SSPEER_UPDATE_ERR_NULL_KEY (-1) 34 9 : #define FD_SSPEER_UPDATE_ERR_NOT_FOUND (-2) 35 6 : #define FD_SSPEER_UPDATE_ERR_INVALID_ARG (-3) 36 : 37 : /* fd_sscluster_slot stores the highest full and incremental slot pair 38 : seen in the cluster. */ 39 : struct fd_sscluster_slot { 40 : ulong full; 41 : ulong incremental; 42 : }; 43 : 44 : typedef struct fd_sscluster_slot fd_sscluster_slot_t; 45 : 46 : /* fd_sspeer_t represents a selected peer from the snapshot peer 47 : selector, including the peer's address, resolved snapshot slots, 48 : and selector score. */ 49 : struct fd_sspeer { 50 : fd_ip4_port_t addr; /* address of the peer */ 51 : ulong full_slot; 52 : ulong incr_slot; 53 : ulong score; /* selector score of peer */ 54 : uchar full_hash[ FD_HASH_FOOTPRINT ]; 55 : uchar incr_hash[ FD_HASH_FOOTPRINT ]; 56 : }; 57 : 58 : typedef struct fd_sspeer fd_sspeer_t; 59 : 60 : struct fd_sspeer_selector_private; 61 : typedef struct fd_sspeer_selector_private fd_sspeer_selector_t; 62 : 63 : FD_PROTOTYPES_BEGIN 64 : 65 : FD_FN_CONST ulong 66 : fd_sspeer_selector_align( void ); 67 : 68 : FD_FN_CONST ulong 69 : fd_sspeer_selector_footprint( ulong max_peers ); 70 : 71 : void * 72 : fd_sspeer_selector_new( void * shmem, 73 : ulong max_peers, 74 : int incremental_snapshot_fetch, 75 : ulong seed ); 76 : 77 : fd_sspeer_selector_t * 78 : fd_sspeer_selector_join( void * shselector ); 79 : 80 : void * 81 : fd_sspeer_selector_leave( fd_sspeer_selector_t * selector ); 82 : 83 : void * 84 : fd_sspeer_selector_delete( void * shselector ); 85 : 86 : /* Update the selector when an http server is resolved. The peer is 87 : identified by key. The values that can be updated are slot and 88 : hash, for both full and incremental snapshots. Returns 89 : FD_SSPEER_UPDATE_SUCCESS on success, FD_SSPEER_UPDATE_ERR_NULL_KEY 90 : if key==NULL, FD_SSPEER_UPDATE_ERR_NOT_FOUND if the key was not 91 : found, and FD_SSPEER_UPDATE_ERR_INVALID_ARG if the update failed 92 : due to invalid arguments (e.g. incr_slot < full_slot). 93 : 94 : Slot-based incremental clearing: when the caller provides 95 : incr_slot==FD_SSPEER_SLOT_UNKNOWN and full_slot!=FD_SSPEER_SLOT_UNKNOWN, 96 : the peer's existing incremental data is cleared if it is stale 97 : (peer->incr_slot < full_slot). Otherwise, existing incremental 98 : data is preserved. */ 99 : int 100 : fd_sspeer_selector_update_on_resolve( fd_sspeer_selector_t * selector, 101 : fd_sspeer_key_t const * key, 102 : ulong full_slot, 103 : ulong incr_slot, 104 : uchar const full_hash[ FD_HASH_FOOTPRINT ], 105 : uchar const incr_hash[ FD_HASH_FOOTPRINT ] ); 106 : 107 : /* Update the selector when a ping response is received. The only 108 : value that can be updated is the latency. If multiple peers 109 : advertise the same address, the update is applied to all of them, 110 : since ssping cannot distinguish between these peers. It returns 111 : the number of peers that have been updated. */ 112 : ulong 113 : fd_sspeer_selector_update_on_ping( fd_sspeer_selector_t * selector, 114 : fd_ip4_port_t addr, 115 : ulong latency ); 116 : 117 : /* Add a peer to the selector. If the peer already exists, 118 : fd_sspeer_selector_add updates the existing peer's score using the 119 : given peer latency and snapshot info. Returns the updated score. 120 : 121 : Slot-based incremental clearing: for an existing peer, when 122 : incr_slot==FD_SSPEER_SLOT_UNKNOWN and full_slot!=FD_SSPEER_SLOT_UNKNOWN, 123 : the peer's incremental data is cleared if it is stale 124 : (peer->incr_slot < full_slot). For a new peer, full_hash and 125 : incr_hash are handled independently. 126 : 127 : Returns the updated score, or FD_SSPEER_SCORE_INVALID on failure. */ 128 : ulong 129 : fd_sspeer_selector_add( fd_sspeer_selector_t * selector, 130 : fd_sspeer_key_t const * key, 131 : fd_ip4_port_t addr, 132 : ulong peer_latency, 133 : ulong full_slot, 134 : ulong incr_slot, 135 : uchar const full_hash[ FD_HASH_FOOTPRINT ], 136 : uchar const incr_hash[ FD_HASH_FOOTPRINT ] ); 137 : 138 : /* Remove a peer from the selector. Peers are removed when they are 139 : not reachable or serving corrupted/malformed snapshots. This is a 140 : no-op if the peer does not exist in the selector. When removing by 141 : address, all peers advertising that address will be removed. */ 142 : void 143 : fd_sspeer_selector_remove( fd_sspeer_selector_t * selector, 144 : fd_sspeer_key_t const * key ); 145 : 146 : void 147 : fd_sspeer_selector_remove_by_addr( fd_sspeer_selector_t * selector, 148 : fd_ip4_port_t addr ); 149 : 150 : /* Select the best peer to download a snapshot from. incremental 151 : indicates to select a peer to download an incremental snapshot. If 152 : incremental is set, base_slot must be a valid full snapshot slot. 153 : Peers that do not offer an incremental snapshot 154 : (incr_slot==FD_SSPEER_SLOT_UNKNOWN) are excluded from incremental 155 : selection. */ 156 : fd_sspeer_t 157 : fd_sspeer_selector_best( fd_sspeer_selector_t * selector, 158 : int incremental, 159 : ulong base_slot ); 160 : 161 : /* Updates the selector's internal cluster slot and re-score all peers 162 : when the cluster slot updates (moves forward) */ 163 : void 164 : fd_sspeer_selector_process_cluster_slot( fd_sspeer_selector_t * selector, 165 : ulong full_slot, 166 : ulong incr_slot ); 167 : 168 : /* Obtain the cluster slot from the selector. It is the highest 169 : resolved full/incremental slot pair seen from snapshot hashes or 170 : from resolved http peers. */ 171 : fd_sscluster_slot_t 172 : fd_sspeer_selector_cluster_slot( fd_sspeer_selector_t * selector ); 173 : 174 : /* Helper functions to count how many elements exist in both peer maps 175 : (by_key and by_addr). Mainly used in unit tests. These are not 176 : optimized for performance. */ 177 : ulong 178 : fd_sspeer_selector_peer_map_by_key_ele_cnt( fd_sspeer_selector_t * selector ); 179 : 180 : ulong 181 : fd_sspeer_selector_peer_map_by_addr_ele_cnt( fd_sspeer_selector_t * selector ); 182 : 183 : FD_PROTOTYPES_END 184 : 185 : #endif /* HEADER_fd_src_discof_restore_utils_fd_sspeer_selector_h */