Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_progcache_fd_progcache_user_h 2 : #define HEADER_fd_src_flamenco_progcache_fd_progcache_user_h 3 : 4 : /* fd_progcache_user.h provides an API for managing a cache of loaded 5 : Solana on-chain program. 6 : 7 : ### Background 8 : 9 : Solana on-chain programs are rarely updated but frequently executed. 10 : Before a program can be executed, it must be loaded and verified, 11 : which is costly. 12 : 13 : ### Fork management 14 : 15 : The program cache is fork-aware (using funk transactions). Txn-level 16 : operations take an exclusive lock over the cache (record ops are 17 : stalled indefinitely until the txn completes). 18 : 19 : ### Cache entry 20 : 21 : Each Solana program can have a number of program cache entries 22 : (typically only zero or one, in rare cases where the program content 23 : differs across forks multiple). 24 : 25 : A cache entry consists of a funk_rec object (from a preallocated 26 : object pool), and a variable-sized fd_progcache_entry struct 27 : (from an fd_alloc heap). 28 : 29 : ### Cache fill policy 30 : 31 : fd_progcache is lazily filled on reads, and eagerly invalidated 32 : if underlying programs are written to. 33 : 34 : ### Cache evict policy 35 : 36 : Cache eviction (i.e. force removal of potentially useful records) 37 : happens on fill. Specifically, cache eviction is triggered when a 38 : cache fill fails to allocate from the wksp (fd_alloc) heap. 39 : 40 : fd_progcache further has a concept of "generations" (gen). Each 41 : cache fill operation specifies a 'gen' number. Only entries with a 42 : lower 'gen' number may get evicted. 43 : 44 : ### Garbage collect policy 45 : 46 : fd_progcache cleans up unused entries eagerly when: 47 : 48 : 1. a database fork is cancelled (e.g. slot is rooted and competing 49 : history dies, or consensus layer prunes a fork) 50 : 2. a cache entry is orphaned (updated or invalidated by an epoch 51 : boundary) */ 52 : 53 : #include "fd_progcache.h" 54 : #include "fd_prog_load.h" 55 : #include "../runtime/fd_runtime_const.h" 56 : 57 : struct fd_progcache_metrics { 58 : ulong lookup_cnt; 59 : ulong hit_cnt; 60 : ulong miss_cnt; 61 : ulong oom_heap_cnt; 62 : ulong oom_desc_cnt; 63 : ulong fill_cnt; 64 : ulong fill_tot_sz; 65 : ulong spill_cnt; 66 : ulong spill_tot_sz; 67 : ulong evict_cnt; 68 : ulong evict_tot_sz; 69 : ulong cum_pull_ticks; 70 : ulong cum_load_ticks; 71 : }; 72 : 73 : typedef struct fd_progcache_metrics fd_progcache_metrics_t; 74 : 75 : /* fd_progcache_t is a thread-local client to a program cache funk 76 : instance. This struct is quite large and therefore not local/stack 77 : declaration-friendly. */ 78 : 79 : struct fd_progcache { 80 : fd_progcache_join_t join[1]; 81 : fd_accdb_lineage_t lineage[1]; 82 : 83 : fd_progcache_metrics_t * metrics; 84 : 85 : uchar * scratch; 86 : ulong scratch_sz; 87 : 88 : uint spill_active; 89 : }; 90 : 91 : FD_PROTOTYPES_BEGIN 92 : 93 : extern FD_TL fd_progcache_metrics_t fd_progcache_metrics_default; 94 : 95 : /* Constructor */ 96 : 97 : static inline ulong 98 0 : fd_progcache_align( void ) { 99 0 : return alignof(fd_progcache_t); 100 0 : } 101 : 102 : static inline ulong 103 0 : fd_progcache_footprint( void ) { 104 0 : return sizeof(fd_progcache_t); 105 0 : } 106 : 107 : static inline fd_progcache_t * 108 0 : fd_progcache_new( void * ljoin ) { 109 0 : return ljoin; 110 0 : } 111 : 112 : static inline void * 113 0 : fd_progcache_delete( void * ljoin ) { 114 0 : return ljoin; 115 0 : } 116 : 117 : /* fd_progcache_join joins the caller to a program cache shmem instance. 118 : scratch points to a FD_PROGCACHE_SCRATCH_ALIGN aligned scratch buffer 119 : and scratch_sz is the size of the largest program/ELF binary that is 120 : going to be loaded (typically max account data sz). */ 121 : 122 : fd_progcache_t * 123 : fd_progcache_join( fd_progcache_t * ljoin, 124 : fd_progcache_shmem_t * shmem, 125 : uchar * scratch, 126 : ulong scratch_sz ); 127 : 128 99 : #define FD_PROGCACHE_SCRATCH_ALIGN (64UL) 129 99 : #define FD_PROGCACHE_SCRATCH_FOOTPRINT FD_RUNTIME_ACC_SZ_MAX 130 : 131 : /* fd_progcache_leave detaches the caller from a program cache. */ 132 : 133 : void * 134 : fd_progcache_leave( fd_progcache_t * cache, 135 : fd_progcache_shmem_t ** opt_shmem ); 136 : 137 : /* fd_progcache_revision_slot returns the slot number under which a 138 : progcache entry is indexed at. epoch_slot0 is the first slot number 139 : of the epoch. deploy_slot is the slot at which the program was 140 : deployed using the program loader. */ 141 : 142 : static inline ulong 143 : fd_progcache_revision_slot( ulong epoch_slot0, 144 165 : ulong deploy_slot ) { 145 165 : return fd_ulong_max( epoch_slot0, deploy_slot ); 146 165 : } 147 : 148 : /* fd_progcache_peek queries the program cache for an existing cache 149 : entry. Does not fill the cache. Returns a pointer to the entry on 150 : cache hit. Returns NULL on cache miss. It is the caller's 151 : responsibility to release the returned record with 152 : fd_progcache_rec_close. */ 153 : 154 : fd_progcache_rec_t * /* read locked */ 155 : fd_progcache_peek( fd_progcache_t * cache, 156 : fd_xid_t const * xid, 157 : fd_pubkey_t const * prog_addr, 158 : ulong revision_slot ); 159 : 160 : /* fd_progcache_pull loads a program from cache, filling the cache if 161 : necessary. The load operation can have a number of outcomes: 162 : - Returns a pointer to an existing cache entry (cache hit, state 163 : either "Loaded" or "FailedVerification") 164 : - Returns a pointer to a newly created cache entry (cache fill, 165 : state either "Loaded" or "FailedVerification") 166 : - Returns NULL if the requested program account is not deployed (i.e. 167 : account is missing, the program is under visibility delay, or user 168 : has not finished uploading the program) 169 : In other words, this method guarantees to return a cache entry if a 170 : deployed program was found in the account database, and the program 171 : either loaded successfully, or failed ELF/bytecode verification. 172 : It is the caller's responsibility to release the returned record with 173 : fd_progcache_rec_close. */ 174 : 175 : fd_progcache_rec_t * /* read locked */ 176 : fd_progcache_pull( fd_progcache_t * cache, 177 : fd_xid_t const * xid, 178 : fd_pubkey_t const * prog_addr, 179 : fd_prog_load_env_t const * env, 180 : fd_accdb_ro_t * progdata_ro, 181 : fd_pubkey_t const * program_owner ); 182 : 183 : /* fd_progcache_rec_close releases a cache record handle returned by 184 : fd_progcache_{pull,peek}. */ 185 : 186 : void 187 : fd_progcache_rec_close( fd_progcache_t * cache, 188 : fd_progcache_rec_t * rec ); 189 : 190 : FD_PROTOTYPES_END 191 : 192 : #endif /* HEADER_fd_src_flamenco_progcache_fd_progcache_user_h */