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 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 progcache_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 "fd_progcache_lineage.h" 56 : #include "../runtime/fd_runtime_const.h" 57 : 58 : struct fd_progcache_metrics { 59 : ulong lookup_cnt; 60 : ulong hit_cnt; 61 : ulong miss_cnt; 62 : ulong oom_heap_cnt; 63 : ulong oom_desc_cnt; 64 : ulong fill_cnt; 65 : ulong fill_tot_sz; 66 : ulong spill_cnt; 67 : ulong spill_tot_sz; 68 : ulong evict_cnt; 69 : ulong evict_tot_sz; 70 : ulong cum_pull_ticks; 71 : ulong cum_load_ticks; 72 : }; 73 : 74 : typedef struct fd_progcache_metrics fd_progcache_metrics_t; 75 : 76 : /* fd_progcache_t is a thread-local client to a program cache instance. 77 : This struct is quite large and therefore not local/stack 78 : declaration-friendly. */ 79 : 80 : struct fd_progcache { 81 : fd_progcache_join_t join[1]; 82 : fd_progcache_lineage_t lineage[1]; 83 : 84 : fd_progcache_metrics_t * metrics; 85 : 86 : uchar * scratch; 87 : ulong scratch_sz; 88 : 89 : uint spill_active; 90 : }; 91 : 92 : FD_PROTOTYPES_BEGIN 93 : 94 : extern FD_TL fd_progcache_metrics_t fd_progcache_metrics_default; 95 : 96 : /* Constructor */ 97 : 98 : /* fd_progcache_join joins the caller to a program cache shmem instance. 99 : scratch points to a FD_PROGCACHE_SCRATCH_ALIGN aligned scratch buffer 100 : and scratch_sz is the size of the largest program/ELF binary that is 101 : going to be loaded (typically max account data sz). */ 102 : 103 : fd_progcache_t * 104 : fd_progcache_join( fd_progcache_t * ljoin, 105 : fd_progcache_shmem_t * shmem, 106 : uchar * scratch, 107 : ulong scratch_sz ); 108 : 109 0 : #define FD_PROGCACHE_SCRATCH_ALIGN (64UL) 110 0 : #define FD_PROGCACHE_SCRATCH_FOOTPRINT FD_RUNTIME_ACC_SZ_MAX 111 : 112 : /* fd_progcache_leave detaches the caller from a program cache. */ 113 : 114 : void * 115 : fd_progcache_leave( fd_progcache_t * cache, 116 : fd_progcache_shmem_t ** opt_shmem ); 117 : 118 : /* fd_progcache_peek queries the program cache for an existing cache 119 : entry. Does not fill the cache. Returns a pointer to the entry on 120 : cache hit. Returns NULL on cache miss. It is the caller's 121 : responsibility to release the returned record with 122 : fd_progcache_rec_close. */ 123 : 124 : fd_progcache_rec_t * /* read locked */ 125 : fd_progcache_peek( fd_progcache_t * cache, 126 : fd_progcache_fork_id_t fork_id, 127 : fd_pubkey_t const * prog_addr, 128 : ulong feature_slot, 129 : ulong deploy_slot ); 130 : 131 : /* fd_progcache_pull loads a program from cache, filling the cache if 132 : necessary. The load operation can have a number of outcomes: 133 : - Returns a pointer to an existing cache entry (cache hit, state 134 : either "Loaded" or "FailedVerification") 135 : - Returns a pointer to a newly created cache entry (cache fill, 136 : state either "Loaded" or "FailedVerification") 137 : - Returns NULL if the requested program account is not deployed (i.e. 138 : account is missing, the program is under visibility delay, or user 139 : has not finished uploading the program) 140 : In other words, this method guarantees to return a cache entry if a 141 : deployed program was found in the account database, and the program 142 : either loaded successfully, or failed ELF/bytecode verification. 143 : It is the caller's responsibility to release the returned record with 144 : fd_progcache_rec_close. */ 145 : 146 : fd_progcache_rec_t * /* read locked */ 147 : fd_progcache_pull( fd_progcache_t * cache, 148 : fd_progcache_fork_id_t fork_id, 149 : fd_pubkey_t const * prog_addr, 150 : fd_prog_load_env_t const * env, 151 : fd_acc_t const * progdata_ro ); 152 : 153 : /* fd_progcache_rec_close releases a cache record handle returned by 154 : fd_progcache_{pull,peek}. */ 155 : 156 : void 157 : fd_progcache_rec_close( fd_progcache_t * cache, 158 : fd_progcache_rec_t * rec ); 159 : 160 : FD_PROTOTYPES_END 161 : 162 : #endif /* HEADER_fd_src_flamenco_progcache_fd_progcache_user_h */