Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_runtime_fd_txncache_private_h 2 : #define HEADER_fd_src_flamenco_runtime_fd_txncache_private_h 3 : 4 : #include "fd_txncache_shmem.h" 5 : #include "../fd_flamenco_base.h" 6 : #include "../fd_rwlock.h" 7 : 8 : /* The number of transactions in each page. This needs to be high 9 : enough to amoritze the cost of caller code reserving pages from, 10 : and returning pages to the pool, but not so high that the memory 11 : wasted from blockhashes with only one transaction is significant. */ 12 : 13 1053 : #define FD_TXNCACHE_TXNS_PER_PAGE (8192UL) 14 : 15 : /* The maximum distance a transaction blockhash reference can be 16 : (inclusive). For example, if no slots were skipped, and the value is 17 : 151, slot 300 is allowed to reference blockhashes from slots 18 : [149, 300). */ 19 306 : #define FD_TXNCACHE_MAX_BLOCKHASH_DISTANCE (151UL) 20 : 21 : struct __attribute__((packed)) fd_txncache_single_txn { 22 : uint blockcache_next; /* Pointer to the next element in the blockcache hash chain containing this entry from the pool. */ 23 : uint generation; /* The generation of the fork when this transaction was inserted. Used to 24 : determine if the transaction is still valid for a fork that might have 25 : advanced since insertion. */ 26 : 27 : fd_txncache_fork_id_t fork_id; /* Fork that the transaction was executed on. A transaction might be in the cache 28 : multiple times if it was executed on multiple forks. */ 29 : uchar txnhash[ 20UL ]; /* The transaction message hash, truncated to 20 bytes. The hash is not always the first 20 30 : bytes, but is 20 bytes starting at some arbitrary offset given by the txnhash_offset value 31 : of the containing blockcache entry. */ 32 : }; 33 : 34 : typedef struct fd_txncache_single_txn fd_txncache_single_txn_t; 35 : 36 : FD_STATIC_ASSERT( sizeof(fd_txncache_single_txn_t)==30UL, fd_txncache_single_txn ); 37 : 38 : struct fd_txncache_txnpage { 39 : ushort free; /* The number of free txn entries in this page. */ 40 : fd_txncache_single_txn_t txns[ FD_TXNCACHE_TXNS_PER_PAGE][ 1 ]; /* The transactions in the page. */ 41 : }; 42 : 43 : typedef struct fd_txncache_txnpage fd_txncache_txnpage_t; 44 : 45 : struct fd_txncache_blockcache_shmem { 46 : fd_txncache_fork_id_t parent_id; 47 : fd_txncache_fork_id_t child_id; 48 : fd_txncache_fork_id_t sibling_id; 49 : 50 : int frozen; /* This is used to enforce invariants on the caller of the txncache. 51 : -1: invalid 52 : 0: active 53 : 1: semi frozen, only happens during snapshot load 54 : 2: frozen, should not be modified */ 55 : 56 : uint generation; 57 : 58 : fd_hash_t blockhash; /* The blockhash that this entry is for. */ 59 : ulong txnhash_offset; /* To save memory, the Agave validator decided to truncate the hash of transactions stored in 60 : this memory to 20 bytes rather than 32 bytes. The bytes used are not the first 20 as you 61 : might expect, but instead the first 20 starting at some random offset into the transaction 62 : hash (starting between 0 and len(hash)-20, a/k/a 44 for signatures, and 12 for hashes). 63 : 64 : In an unfortunate turn, the offset is also propagated to peers via. snapshot responses, 65 : which only communicate the offset and the respective 20 bytes. To make sure we are 66 : deduplicating incoming transactions correctly, we must replicate this system even though 67 : it would be easier to just always take the first 20 bytes. For transactions that we 68 : insert into the cache ourselves, we do just always use a key_offset of zero, so this is 69 : only nonzero when constructed form a peer snapshot. */ 70 : 71 : ushort pages_cnt; /* The number of txnpages currently in use to store the transactions in this blockcache. */ 72 : 73 : struct { 74 : ulong next; 75 : } pool; 76 : 77 : struct { 78 : ulong next; 79 : } slist; 80 : 81 : struct { 82 : ulong next; 83 : ulong prev; 84 : } blockhash_map; 85 : }; 86 : 87 : typedef struct fd_txncache_blockcache_shmem fd_txncache_blockcache_shmem_t; 88 : 89 : #define POOL_NAME blockcache_pool 90 : #define POOL_T fd_txncache_blockcache_shmem_t 91 : #define POOL_IDX_T ulong 92 8022 : #define POOL_NEXT pool.next 93 : #define POOL_IMPL_STYLE 1 94 : #include "../../util/tmpl/fd_pool.c" 95 : 96 : #define MAP_NAME blockhash_map 97 : #define MAP_KEY blockhash 98 : #define MAP_ELE_T fd_txncache_blockcache_shmem_t 99 : #define MAP_KEY_T fd_hash_t 100 : #define MAP_PREV blockhash_map.prev 101 0 : #define MAP_NEXT blockhash_map.next 102 654 : #define MAP_KEY_EQ(k0,k1) fd_hash_eq( k0, k1 ) 103 4959 : #define MAP_KEY_HASH(key,seed) (__extension__({ (void)(seed); fd_ulong_load_8_fast( (key)->uc ); })) 104 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1 105 : #define MAP_MULTI 1 106 : #define MAP_IMPL_STYLE 1 107 : #include "../../util/tmpl/fd_map_chain.c" 108 : 109 : #define SLIST_NAME root_slist 110 : #define SLIST_ELE_T fd_txncache_blockcache_shmem_t 111 0 : #define SLIST_IDX_T ulong 112 4056 : #define SLIST_NEXT slist.next 113 : #define SLIST_IMPL_STYLE 1 114 : #include "../../util/tmpl/fd_slist.c" 115 : 116 : #define SET_NAME descends_set 117 : #include "../../util/tmpl/fd_set_dynamic.c" 118 : 119 : struct __attribute__((aligned(FD_TXNCACHE_SHMEM_ALIGN))) fd_txncache_shmem_private { 120 : /* The txncache is a concurrent structure and will be accessed by multiple threads 121 : concurrently. Insertion and querying only take a read lock as they can be done 122 : lockless but all other operations will take a write lock internally. 123 : 124 : The lock needs to be aligned to 128 bytes to avoid false sharing with other 125 : data that might be on the same cache line. */ 126 : fd_rwlock_t lock[ 1 ] __attribute__((aligned(128UL))); 127 : 128 : ulong txn_per_slot_max; 129 : ulong active_slots_max; 130 : ulong bucket_cnt; /* Hash buckets per blockcache. Decoupled from txn_per_slot_max (load 131 : factor 8) to reduce the heads arrays' memory footprint. */ 132 : ushort txnpages_per_blockhash_max; 133 : ushort max_txnpages; 134 : 135 : uint blockcache_generation; /* Incremented for every blockcache. */ 136 : ushort txnpages_free_cnt; /* The number of pages in the txnpages that are not currently in use. */ 137 : 138 : ulong root_cnt; 139 : root_slist_t root_ll[1]; /* A singly linked list of the forks that are roots of fork chains. The tail is the 140 : most recently added root, the head is the oldest root. This is used to identify 141 : which forks can be pruned when a new root is added. */ 142 : 143 : ulong seed; 144 : ulong magic; /* ==FD_TXNCACHE_SHMEM_MAGIC */ 145 : }; 146 : 147 : FD_PROTOTYPES_BEGIN 148 : 149 : FD_FN_CONST ushort 150 : fd_txncache_max_txnpages_per_blockhash( ulong max_active_slots, 151 : ulong max_txn_per_slot, 152 : int larger_max_cost_per_block ); 153 : 154 : FD_FN_CONST ushort 155 : fd_txncache_max_txnpages( ulong max_active_slots, 156 : ulong max_txn_per_slot, 157 : int larger_max_cost_per_block ); 158 : 159 : FD_FN_CONST static inline ulong 160 183 : fd_txncache_bucket_cnt( ulong max_txn_per_slot ) { 161 183 : return fd_ulong_max( 1UL, (max_txn_per_slot+7UL)/8UL ); 162 183 : } 163 : 164 : FD_PROTOTYPES_END 165 : 166 : #endif /* HEADER_fd_src_flamenco_runtime_fd_txncache_private_h */