Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_accdb_fd_accdb_h
2 : #define HEADER_fd_src_flamenco_accdb_fd_accdb_h
3 :
4 : #include "fd_accdb_base.h"
5 : #include "fd_accdb_shmem.h"
6 :
7 : /* The accdb is a fork aware database that can be queried to get the
8 : current state of any accounts as-of a given fork, and update them. */
9 :
10 48 : #define FD_ACCDB_ALIGN (128UL)
11 :
12 : /* Well-known file descriptor numbers for the accounts database backing
13 : file. Tiles inherit these from the parent process which dups the
14 : accounts file to these fds before fork+exec, so seccomp filters can
15 : pin syscalls to a fixed fd. fd 123462 is reserved by XDP. */
16 :
17 0 : #define FD_ACCDB_FD_RW (123461)
18 0 : #define FD_ACCDB_FD_RO (123460)
19 :
20 : struct fd_accdb_entry {
21 : uchar pubkey[ 32UL ];
22 : uchar owner[ 32UL ];
23 : ulong lamports;
24 : int executable;
25 :
26 : ulong data_len;
27 : uchar * data;
28 :
29 : uchar prior_owner[ 32UL ];
30 : ulong prior_lamports;
31 : int prior_executable;
32 : ulong prior_data_len;
33 : uchar * prior_data;
34 :
35 : int commit;
36 : int pd_write;
37 :
38 : int _writable;
39 : int _overwrite;
40 :
41 : ushort _fork_id;
42 : uint _generation;
43 : ulong _acc_map_idx;
44 :
45 : ulong _original_size_class;
46 : ulong _original_cache_idx;
47 :
48 : /* Writable only. _staging_cache_idx is the 10 MiB line the account
49 : is written in. _final_cache_idx is the line of the account's final
50 : size class, taken at release (ULONG_MAX if none). */
51 : ulong _staging_cache_idx;
52 : ulong _final_cache_idx;
53 : };
54 :
55 : typedef struct fd_accdb_entry fd_acc_t;
56 :
57 : FD_PROTOTYPES_BEGIN
58 :
59 : FD_FN_CONST ulong
60 : fd_accdb_align( void );
61 :
62 : FD_FN_CONST ulong
63 : fd_accdb_footprint( ulong max_live_slots,
64 : int compaction );
65 :
66 : /* fd_accdb_new constructs the local joiner state for an accdb writer
67 : (or compaction tile). fd is an O_RDWR fd of the on-disk file.
68 :
69 : external_epoch_cnt and external_epoch_slots provide a list of
70 : additional epoch publish slots to scan during compaction's
71 : deferred-free reclamation. These point at memory owned by other
72 : processes (typically the per-tile fseq of read-only consumers like
73 : the rpc tile), mapped read-only into this joiner's address space.
74 : Each *external_epoch_slots[i] is updated by the owning RO joiner
75 : on each epoch-protected operation (and reset to ULONG_MAX when
76 : idle), and is used by this joiner's compaction scan to determine
77 : when on-disk partitions can be safely reclaimed.
78 :
79 : For joiners that do not need to track external RO consumers (i.e.
80 : any joiner that is not the compaction tile, or a writer-only
81 : topology), pass external_epoch_cnt=0 and external_epoch_slots=NULL.
82 : The pointer array is borrowed and must remain valid for the
83 : lifetime of the join.
84 :
85 : sleep, if non-NULL, is the sleep object the accdb tile parks on and
86 : sleep_tile_id its tile id there. The join rings it whenever it hands
87 : the tile work (a command, or the cache free list crossing its low
88 : water mark). NULL when the accdb tile spins.
89 :
90 : compaction must match the value given to fd_accdb_footprint. */
91 :
92 : struct fd_sleep_private;
93 :
94 : void *
95 : fd_accdb_new( void * ljoin,
96 : fd_accdb_shmem_t * shmem,
97 : int fd,
98 : ulong external_epoch_cnt,
99 : ulong const ** external_epoch_slots,
100 : struct fd_sleep_private * sleep,
101 : ulong sleep_tile_id,
102 : int compaction );
103 :
104 : fd_accdb_t *
105 : fd_accdb_join( void * shaccdb );
106 :
107 : /* fd_accdb_join_readonly is the read-only counterpart of fd_accdb_new +
108 : fd_accdb_join. shmem_ro may point into a read-only mapping of the
109 : shmem region; the function will not write to it. my_epoch_slot_rw
110 : must point at a ulong owned by this joiner that it can write to
111 : (typically a private per-tile fseq that the accdb tile maps read-only
112 : and passes through external_epoch_slots[] in fd_accdb_new). fd_ro
113 : must be opened O_RDONLY on the same file the writer joiner opened RW.
114 :
115 : The joiner publishes its current epoch into *my_epoch_slot_rw on
116 : entry to each epoch-protected operation (and resets to ULONG_MAX on
117 : exit). The accdb tile's compaction scan observes this slot via its
118 : external_epoch_slots[] pointer and defers partition reclamation
119 : accordingly, the same way it does for in-shmem joiner_epochs[].
120 :
121 : Only fd_accdb_read_one_nocache, fd_accdb_read_one_meta, fd_accdb_exists,
122 : fd_accdb_lamports, and fd_accdb_data_len are supported on a readonly
123 : join; any other API is undefined behavior. */
124 :
125 : fd_accdb_t *
126 : fd_accdb_join_readonly( void * ljoin,
127 : fd_accdb_shmem_t * shmem_ro,
128 : ulong * my_epoch_slot_rw,
129 : int fd_ro );
130 :
131 : /* fd_accdb_snapshot_load_{begin,end} suspend compaction while snapshot
132 : writers build the index. New layer-0 partitions are tagged Cold;
133 : load_end opens a fresh Hot partition for runtime writes. Call begin
134 : before any writer starts and end after all writers stop. */
135 :
136 : void
137 : fd_accdb_snapshot_load_begin( fd_accdb_t * accdb );
138 :
139 : void
140 : fd_accdb_snapshot_load_end( fd_accdb_t * accdb );
141 :
142 : /* fd_accdb_snapshot_recover_delta appends into the accdb delta set the
143 : accounts modified at fork_id.
144 :
145 : This is intended to be used after booting off an incremental snapshot
146 : and allows the validator to create additional incremental snaps.
147 :
148 : Not thread safe: assumes no one but the calling thread is accessing
149 : accdb deltas. Returns 0 on success, -1 if the delta map is too small. */
150 :
151 : int
152 : fd_accdb_snapshot_recover_delta( fd_accdb_t * accdb,
153 : fd_accdb_fork_id_t fork_id );
154 :
155 : /* fd_accdb_snapshot_recovery_t captures layer-0 write head metadata.
156 : Used by fd_accdb_snapshot_{save,revert}_whead to save and restore
157 : accdb state across an incremental snapshot attempt. */
158 :
159 : struct fd_accdb_snapshot_recovery {
160 : ulong whead_val; /* whead[0].val */
161 : int has_partition; /* has_partition[0] */
162 : ulong partition_max; /* partition_max */
163 : ulong disk_current_bytes; /* disk_current_bytes metric */
164 : ulong savepoint_bytes_freed; /* bytes_freed of the save-point partition */
165 : };
166 :
167 : typedef struct fd_accdb_snapshot_recovery fd_accdb_snapshot_recovery_t;
168 :
169 : /* fd_accdb_snapshot_save_whead captures the current layer-0 write head,
170 : partition state, and disk_current_bytes metric into the provided
171 : recovery struct. Also captures the save-point partition's
172 : bytes_freed. */
173 :
174 : void
175 : fd_accdb_snapshot_save_whead( fd_accdb_t * accdb,
176 : fd_accdb_snapshot_recovery_t * out );
177 :
178 : /* fd_accdb_snapshot_revert_whead restores the layer-0 write head to a
179 : previously saved position.
180 :
181 : It waits for the accdb tile to apply pending root / purge requests
182 : (including the purge of the incremental fork) and release what they
183 : retired before releasing partitions.
184 :
185 : Previously allocated partitions (with indices in the range
186 : [saved_partition_max, current partition_max)) are released back
187 : to the partition pool. disk_current_bytes is restored to the saved
188 : value rather than computed per-partition, and the save-point
189 : partition's bytes_freed and write_offset are reset. */
190 :
191 : void
192 : fd_accdb_snapshot_revert_whead( fd_accdb_t * accdb,
193 : fd_accdb_snapshot_recovery_t const * recover );
194 :
195 : /* fd_accdb_attach_child allocates a new fork as a child of
196 : parent_fork_id and returns the new fork's id. This must be done
197 : any time a new fork is being inserted into the accounts database,
198 : so that the accounts database can maintain ancestry information
199 : in order to support queries correctly.
200 :
201 : To create the initial root fork, pass a sentinel value with
202 : val==USHORT_MAX as parent_fork_id. This must be done exactly
203 : once, before any other fork operations.
204 :
205 : For non-root forks, parent_fork_id must refer to a fork that has
206 : already been attached. The ancestry must form a tree and it is
207 : undefined behavior to create cycles.
208 :
209 : Forks removed by advance_root or purge hold their pool slot until
210 : the accdb tile has applied the request and concurrent readers have
211 : drained. If the pool is full, this call blocks until the accdb tile
212 : has applied all pending requests and released retired slots (which
213 : waits on concurrent readers). It is a fatal error if the pool is
214 : still full afterwards. */
215 :
216 : fd_accdb_fork_id_t
217 : fd_accdb_attach_child( fd_accdb_t * accdb,
218 : fd_accdb_fork_id_t parent_fork_id );
219 :
220 : /* fd_accdb_advance_root advances the root of the accounts database to
221 : the given fork_id. fork_id must be a direct child of the root most
222 : recently passed to advance_root (or the initial root).
223 :
224 : Any competing sibling forks (and their entire subtrees) are removed.
225 : For accounts updated on the newly rooted fork, any older versions on
226 : ancestor forks are tombstoned for later compaction.
227 :
228 : Does not block. The fork tree is updated immediately (fork_id may
229 : be advanced past right away); the index work is done asynchronously
230 : by the accdb tile. Reads on fork_id and its descendants are correct
231 : before that work completes. The old root fork slot is recycled
232 : after the accdb tile applies the request and reader epochs drain.
233 :
234 : IMPORTANT: The caller must guarantee that all outstanding
235 : acquire/release pairs on every sibling of fork_id (and their entire
236 : subtrees) have completed before calling advance_root. advance_root
237 : implicitly purges those sibling subtrees, which frees their fork pool
238 : slots for recycling. Acquires on any ancestor of fork_id must also
239 : have completed: rooting fork_id unlinks the ancestor versions it
240 : replaces from the index. Read-only acquires on fork_id and its
241 : descendants may continue.
242 :
243 : Once a fork is rooted, its generation becomes the new
244 : root_generation. Concurrent acquires that observe the new root will
245 : use the generation fast path (generation <= root_generation) for all
246 : accounts from that fork and its ancestors, bypassing descends_set
247 : entirely. This is what makes fork pool slot recycling safe: by the
248 : time a slot is freed and reusable, no reader will ever consult
249 : descends_set for the old fork_id. */
250 :
251 : void
252 : fd_accdb_advance_root( fd_accdb_t * accdb,
253 : fd_accdb_fork_id_t fork_id );
254 :
255 : /* fd_accdb_requests_pending returns 1 if the accdb tile has not yet
256 : applied every advance_root / purge request and released the
257 : accounts and fork slots they retired, 0 otherwise. */
258 :
259 : int
260 : fd_accdb_requests_pending( fd_accdb_t const * accdb );
261 :
262 : /* fd_accdb_purge removes the provided fork and all of its descendants
263 : from the accounts database.
264 :
265 : All accounts written on the purged fork and any child or
266 : grandchild forks are removed from the index, and their disk
267 : space is freed for compaction. The ancestry information for all
268 : purged forks is also removed.
269 :
270 : Does not block; like advance_root, the index work is done
271 : asynchronously by the accdb tile. fork_id must not be the root or
272 : one of its ancestors, and no fork may be attached under a purged
273 : fork afterwards.
274 :
275 : IMPORTANT: The caller must guarantee that all outstanding
276 : acquire/release pairs on the purged fork and every descendant
277 : have completed before calling purge. The same fork pool slot
278 : recycling hazard described for advance_root applies here. */
279 :
280 : void
281 : fd_accdb_purge( fd_accdb_t * accdb,
282 : fd_accdb_fork_id_t fork_id );
283 :
284 : /* fd_accdb_acquire brings all of the requested accounts as-of the given
285 : fork_idx into the cache, and refcnts them in the cache so they cannot
286 : be evicted until later released.
287 :
288 : fork_idx is the fork index from replay to query as-of. The caller
289 : must keep the fork live until every acquired account is released.
290 : Cache pins prevent eviction; they do not prevent fork recycling.
291 : advance_root(fork_idx) may run concurrently with read-only acquires
292 : on that fork, but advancing past it or purging it is forbidden while
293 : any acquire on it is outstanding.
294 :
295 : pubkeys_cnt is the number of accounts to acquire, and pubkeys is an
296 : array of pointers to the 32-byte pubkeys of the accounts to acquire.
297 : writable is an array of flags indicating whether each corresponding
298 : account in pubkeys is being acquired for read (0) or write (1).
299 : Writes provide a temporary buffer of 10MiB in all cases, which the
300 : caller can use for staging changes to the data, and this allows
301 : account resizing, or cancelling of any data written (for example if a
302 : transaction fails) without needing to restore it. If an account is
303 : acquired for write, the caller must set the commit bit on the acc
304 : to non-zero to have the changes written back to the database on
305 : release, or leave it at zero to discard the changes. The commit bit
306 : must be set even if only the metadata has changed.
307 :
308 : IMPORTANT: The caller must guarantee that for any given (pubkey,
309 : fork) pair, there is no concurrent acquire that holds a writable
310 : acc while another acquire for the same account on the same fork is
311 : outstanding (whether readable or writable). Specifically:
312 :
313 : - Multiple concurrent read-only acquires of the same account on the
314 : same fork are permitted.
315 : - A writable acquire of an account on a given fork must not overlap
316 : with any other acquire (read or write) of that same account on
317 : that same fork.
318 : - Acquires of the same account on _different_ forks are always safe
319 : and may overlap freely, provided that all releases on an ancestor
320 : fork have completed before any acquire on a descendant fork
321 : begins. In particular, a fork must finish all of its transaction
322 : execution (including committing or cancelling every writable
323 : account) before a child fork is attached and begins acquiring.
324 : This is naturally guaranteed by the replay scheduler, which does
325 : not activate a child block until the parent block is fully done.
326 : Concurrent acquires across unrelated sibling forks have no
327 : ordering requirement.
328 : - A read-only acquire on a frozen ancestor fork may begin after
329 : descendant-fork acquires (read or write) have already begun. The
330 : executor relies on this: while a child bank executes (writably
331 : acquiring its own-fork accounts), it also read-only acquires the
332 : program's implicit programdata on the frozen parent fork. This
333 : is safe because the parent is frozen, no writer ever commits to
334 : it so the read-only acquire cannot overlap any same-fork write,
335 : and read-only acquires never mutate acc pool or fork state.
336 :
337 : Violating this contract is undefined behavior and will likely crash
338 : with an assertion failure inside the cache refcount logic. In
339 : practice, these constraints are naturally satisfied by the Solana
340 : execution model: each transaction has exclusive write locks on its
341 : writable accounts within a slot, the scheduler ensures no two
342 : concurrent transactions write to the same account on the same fork,
343 : and the replay scheduler serializes parent block completion before
344 : child block activation on the same fork chain.
345 :
346 : When a writable account is committed as an "overwrite" (same
347 : fork), the acc pool element's metadata fields (size, lamports,
348 : offset) are mutated in place, and the cache line's owner field is
349 : updated. This is safe because these mutations
350 : only happen on the acc element whose generation matches the
351 : committing fork. A concurrent acquire on a different fork cannot
352 : observe an in-place mutation of the same acc element for a child fork
353 : to even exist, the parent must be frozen and no longer undergoing
354 : modifications. All acc pool fields are effectively immutable from
355 : the perspective of any concurrent cross-fork reader.
356 :
357 : out_accs is an array of pubkeys_cnt cache accs to be filled in
358 : with the acquired accounts. The cache will fill the owner, lamports,
359 : data_len, executable, and data fields of each acc. A missing or
360 : zero-lamport account is returned as the default account. data is
361 : NULL for a read-only acquire of a missing account. */
362 :
363 : void
364 : fd_accdb_acquire( fd_accdb_t * accdb,
365 : fd_accdb_fork_id_t fork_id,
366 : ulong pubkeys_cnt,
367 : uchar const * const * pubkeys,
368 : int * writable,
369 : fd_acc_t * out_accs );
370 :
371 : void
372 : fd_accdb_acquire_a( fd_accdb_t * accdb,
373 : fd_accdb_fork_id_t fork_id,
374 : ulong pubkeys_cnt,
375 : uchar const * const * pubkeys,
376 : int * writable,
377 : fd_acc_t * out_accs );
378 :
379 : void
380 : fd_accdb_acquire_b( fd_accdb_t * accdb,
381 : fd_accdb_fork_id_t fork_id,
382 : ulong reserved_cnt,
383 : ulong pubkeys_cnt,
384 : uchar const * const * pubkeys,
385 : fd_acc_t * out_accs );
386 :
387 : /* fd_accdb_release releases previously acquired accounts back to the
388 : cache, and if any of the released writable accounts have their commit
389 : bit set, the cache will write the changes back to the database. The
390 : caller must guarantee that the accs being released were previously
391 : acquired and not yet released, and that the pubkeys in the accs
392 : match the pubkeys of the acquired accounts. Release the complete set
393 : from one acquire on the same joiner; do not split or combine sets.
394 : Use fd_accdb_release_ab for a two-phase acquire. The fork that each
395 : acc refers to must still exist (not yet purged or advanced past) at
396 : the time of release. This
397 : includes forks that would be implicitly purged by a concurrent
398 : advance_root on a sibling — the caller must ensure advance_root
399 : is not called until all releases on affected forks have completed.
400 : Releasing accounts for a fork that has been purged or recycled is
401 : undefined behavior. */
402 :
403 : void
404 : fd_accdb_release( fd_accdb_t * accdb,
405 : ulong accs_cnt,
406 : fd_acc_t * accs );
407 :
408 : void
409 : fd_accdb_release_ab( fd_accdb_t * accdb,
410 : ulong accs_cnt,
411 : fd_acc_t * accs,
412 : ulong execs_cnt,
413 : fd_acc_t * execs );
414 :
415 : fd_acc_t
416 : fd_accdb_read_one( fd_accdb_t * accdb,
417 : fd_accdb_fork_id_t fork_id,
418 : uchar const * pubkey );
419 :
420 : fd_acc_t
421 : fd_accdb_write_one( fd_accdb_t * accdb,
422 : fd_accdb_fork_id_t fork_id,
423 : uchar const * pubkey );
424 :
425 : void
426 : fd_accdb_unwrite_one( fd_accdb_t * accdb,
427 : fd_acc_t * acc );
428 :
429 : void
430 : fd_accdb_unread_one( fd_accdb_t * accdb,
431 : fd_acc_t * acc );
432 :
433 : int
434 : fd_accdb_exists( fd_accdb_t * accdb,
435 : fd_accdb_fork_id_t fork_id,
436 : uchar const * pubkey );
437 :
438 : /* fd_accdb_probe_pd_this_fork checks whether the newest version of
439 : pubkey visible on fork_id was committed on fork_id itself. If so,
440 : returns 1 and sets *out_pd_write to that version's pd_write flag,
441 : *out_data_len to its data length, and *out_lamports to its lamport
442 : balance. Otherwise returns 0, sets *out_pd_write to 0, and leaves
443 : *out_data_len and *out_lamports untouched.
444 :
445 : Reads only metadata, not account data. out_pd_write deliberately
446 : ignores lamports: a programdata closed this slot is a lamports==0
447 : tombstone that must still report pd_write=1 so the loader's
448 : DelayVisibility gate fires. out_lamports is reported separately so
449 : that callers doing account deadness checks have the current fork's
450 : deadness.
451 :
452 : Note that out_lamports and out_data_len are not read atomically.
453 : Caller is responsible for ensuring ordering if racing is not
454 : acceptable.
455 :
456 : Full join only. */
457 :
458 : int
459 : fd_accdb_probe_pd_this_fork( fd_accdb_t * accdb,
460 : fd_accdb_fork_id_t fork_id,
461 : uchar const * pubkey,
462 : int * out_pd_write,
463 : ulong * out_data_len,
464 : ulong * out_lamports );
465 :
466 : /* fd_accdb_read_one_nocache reads one account at fork_id into
467 : caller-provided output buffers. Suitable for processes that mmap the
468 : accdb data region read-only: it never mutates any cache line, index
469 : entry, or record. It publishes this joiner's epoch to hold off
470 : compaction for the duration of the read. Read-only joiners publish
471 : to a separate writable fseq supplied at join time and scanned via
472 : external_epoch_slots.
473 :
474 : out_owner must point at a 32-byte buffer. If non-NULL, out_data must
475 : point at a buffer of at least FD_RUNTIME_ACC_SZ_MAX (10 MiB) bytes,
476 : the maximum account data size; the function does not bound-check
477 : against the account's actual length. On a cache hit the bytes are
478 : memcpy'd from the cache slot using a try-read-test (ABA) loop; on a miss the owner
479 : and data are preadv2'd from the disk fd passed at join time, scattered
480 : into out_owner and out_data via iovec (looping on short reads).
481 :
482 : If out_data is NULL, only account metadata is read: no account data
483 : is copied or read from disk. out_data_len still reports the full
484 : account data length. This permits bounded owner checks on accounts
485 : that are too large to load.
486 :
487 : If the account does not exist, *out_lamports is set to zero and the
488 : other outputs are undefined; otherwise *out_lamports is non-zero and
489 : out_executable, out_owner, out_data, and out_data_len are all filled
490 : in.
491 :
492 : Returns FD_ACCDB_READ_ONE_NOCACHE_MISS when the account does not
493 : exist, FD_ACCDB_READ_ONE_NOCACHE_CACHE when read from cache, or
494 : FD_ACCDB_READ_ONE_NOCACHE_DISK when read from disk.
495 :
496 : The function takes no reference; nothing needs to be released. */
497 :
498 40 : #define FD_ACCDB_READ_ONE_NOCACHE_MISS (0)
499 4925 : #define FD_ACCDB_READ_ONE_NOCACHE_CACHE (1)
500 0 : #define FD_ACCDB_READ_ONE_NOCACHE_DISK (2)
501 :
502 : int
503 : fd_accdb_read_one_nocache( fd_accdb_t * accdb,
504 : fd_accdb_fork_id_t fork_id,
505 : uchar const * pubkey,
506 : ulong * out_lamports,
507 : int * out_executable,
508 : uchar * out_owner,
509 : uchar * out_data,
510 : ulong * out_data_len );
511 :
512 : /* Reads owner (32 bytes) and data length without acquiring account data.
513 : Uses cache when available, otherwise reads only the owner from disk.
514 : Returns FD_ACCDB_READ_ONE_NOCACHE_*; outputs are undefined on MISS. */
515 :
516 : int
517 : fd_accdb_read_one_meta( fd_accdb_t * accdb,
518 : fd_accdb_fork_id_t fork_id,
519 : uchar const * pubkey,
520 : uchar * out_owner,
521 : ulong * out_data_len );
522 :
523 : /* fd_accdb_lamports returns the lamports of the account at fork_id, or
524 : zero if the account does not exist. */
525 :
526 : ulong
527 : fd_accdb_lamports( fd_accdb_t * accdb,
528 : fd_accdb_fork_id_t fork_id,
529 : uchar const * pubkey );
530 :
531 : /* fd_accdb_data_len returns the data length of the account at fork_id,
532 : or zero if the account does not exist. Reads only metadata, not
533 : account data. */
534 :
535 : ulong
536 : fd_accdb_data_len( fd_accdb_t * accdb,
537 : fd_accdb_fork_id_t fork_id,
538 : uchar const * pubkey );
539 :
540 : /* fd_accdb_reset reinitializes the accdb to the state immediately after
541 : fd_accdb_new. All in-memory index state is cleared and all pool
542 : joins are re-established. The on-disk file is not truncated.
543 :
544 : Waits for the accdb tile to apply pending root / purge requests and
545 : release what they retired. The caller must guarantee that no other
546 : thread is concurrently accessing the accdb (no outstanding
547 : acquires). It is a fatal error to reset while partitions are
548 : queued for compaction; they are only queued once a snapshot load
549 : has ended. */
550 :
551 : void
552 : fd_accdb_reset( fd_accdb_t * accdb );
553 :
554 : /* Reserves one contiguous range of sz bytes in the layer-0 account
555 : log. The last dead_tail_sz bytes are padding with no live record
556 : and are counted as freed. */
557 :
558 : ulong
559 : fd_accdb_snapshot_reserve_write( fd_accdb_t * accdb,
560 : ulong sz,
561 : ulong dead_tail_sz );
562 :
563 : /* fd_accdb_snapshot_write_batch processes up to 8 accounts at once,
564 : using software prefetching to overlap hash chain memory latency with
565 : useful work. It is thread safe across distinct joins and locks each
566 : pubkey's hash chain across lookup and commit. Snapshot loading
567 : excludes non-snapshot accdb operations while these locks are held.
568 : Each pubkey[i] points to a 32-byte public key.
569 : *out_replaced_lamports is set to the sum of the lamports of all
570 : accounts replaced by this batch (i.e. the previous lamports value
571 : of each account whose acc was overwritten). *out_ignored_lamports
572 : is set to the sum of the lamports of all accounts ignored by this
573 : batch (i.e. the lamports of each input account whose write was
574 : dropped because an acc with a higher slot already exists).
575 : Returns 0 on success, -1 if a pubkey appears more than once at the
576 : same slot, either in this batch or an earlier one (a corrupt-snapshot
577 : signal — the caller should flag the snapshot malformed). Output
578 : counters are not meaningful when -1 is returned.
579 :
580 : Each slots[i] must be <= UINT_MAX. Slots are held in a 32-bit
581 : scratch field during snapshot loading; the accdb format must be
582 : widened before Solana reaches slot 2^32. Passing a larger slot
583 : crashes the process.
584 :
585 : file_offsets[i] is the pre-reserved on-disk location for pubkeys[i].
586 :
587 : results receives one FD_ACCDB_SNAPSHOT_WRITE_* code per account.
588 : Not meaningful when -1 is returned.
589 :
590 : fork_id controls recovery behavior:
591 :
592 : USHORT_MAX, full-snapshot mode. Existing entries with the same
593 : pubkey are replaced in-place. No txn entries are
594 : created.
595 :
596 : other, incremental-snapshot mode. Cross-snapshot overrides
597 : insert a new acc_pool entry alongside the old one and
598 : create a txn record on fork_id, so fd_accdb_purge can
599 : revert the incremental writes on failure. Intra-fork
600 : duplicates are replaced in-place. */
601 :
602 0 : #define FD_ACCDB_SNAPSHOT_WRITE_IGNORED (0) /* a newer version existed, write dropped */
603 0 : #define FD_ACCDB_SNAPSHOT_WRITE_LOADED (1) /* no prior funded version */
604 0 : #define FD_ACCDB_SNAPSHOT_WRITE_REPLACED (2) /* superseded a funded version from this load */
605 0 : #define FD_ACCDB_SNAPSHOT_WRITE_REPLACED_CROSS (3) /* superseded a funded version from an earlier snapshot */
606 :
607 : int
608 : fd_accdb_snapshot_write_batch( fd_accdb_t * accdb,
609 : fd_accdb_fork_id_t fork_id,
610 : ulong cnt,
611 : uchar const * const pubkeys[],
612 : ulong const slots[],
613 : ulong const lamports[],
614 : ulong const data_lens[],
615 : int const executables[],
616 : ulong const file_offsets[],
617 : ulong * accounts_ignored,
618 : ulong * accounts_replaced,
619 : ulong * accounts_loaded,
620 : ulong * out_replaced_lamports,
621 : ulong * out_ignored_lamports,
622 : uchar * results );
623 :
624 : /* fd_accdb_background performs one unit of background work.
625 :
626 : THREADING MODEL
627 :
628 : The accdb API is split across three thread roles:
629 :
630 : T1 (replay): calls attach_child, advance_root, purge, acquire, and
631 : release. T1 alone updates the fork tree. advance_root and
632 : purge update it, publish a request and return immediately; the
633 : index work is deferred to T2.
634 :
635 : T2 (accdb tile / background): calls fd_accdb_background repeatedly.
636 : This is the only function T2 should call.
637 :
638 : T3 (executor tiles, 1..N): call acquire and release.
639 :
640 : acquire and release may be called concurrently from T1 and any number
641 : of T3 threads.
642 :
643 : Read-only acquire/release on a fork F or its descendants may run
644 : concurrently with advance_root(F) and with T2 applying it, but not
645 : acquires on an ancestor of F.
646 :
647 : fd_accdb_background must be called from exactly one thread (T2). It
648 : must not be called concurrently with itself.
649 :
650 : BEHAVIOR
651 :
652 : Applies pending root / purge requests and snapshot state changes
653 : first. During
654 : snapshot production, pre-evicts cache lines without compacting. Otherwise
655 : pre-evicts, grows the file if needed, and attempts compaction at each
656 : layer. Sets *charge_busy when work is done. */
657 :
658 : void
659 : fd_accdb_background( fd_accdb_t * accdb,
660 : int * charge_busy );
661 :
662 : /* fd_accdb_shmetrics returns a pointer to the shared metrics counters
663 : for the given accdb instance. The returned pointer remains valid
664 : for the lifetime of the underlying shmem. */
665 :
666 : fd_accdb_shmem_metrics_t const *
667 : fd_accdb_shmetrics( fd_accdb_t * accdb );
668 :
669 : /* fd_accdb_metrics returns a pointer to the per-thread metrics counters
670 : for the given accdb instance. The returned pointer remains valid
671 : for the lifetime of the underlying shmem. */
672 :
673 : fd_accdb_metrics_t const *
674 : fd_accdb_metrics( fd_accdb_t * accdb );
675 :
676 : /* fd_accdb_flush_metrics publishes this joiner's pending layer-0 write
677 : metrics. Normal layer-0 writes defer these metrics by default.
678 :
679 : NOTE: A flush delayed past partition reuse can credit old counters
680 : to the new partition. The impact on metrics accuracy is expected
681 : to be rare and small because partitions are rarely reused and
682 : metrics flush often. */
683 :
684 : void
685 : fd_accdb_flush_metrics( fd_accdb_t * accdb );
686 :
687 : /* fd_accdb_cache_class_occupancy snapshots the current per-size-class
688 : cache occupancy and capacity into the caller-provided arrays, each
689 : of which must have FD_ACCDB_CACHE_CLASS_CNT entries. used[c] is the
690 : number of slots in class c that currently hold a cache acc (i.e.
691 : slots that have been allocated lazily and are not sitting in the
692 : free list). max[c] is the total slot capacity of class c.
693 : reserved[c] is the slots reserved by in-flight acquires, ULONG_MAX
694 : when reservation tracking is disabled for the class. Reads
695 : are done with relaxed (volatile) loads and may be momentarily
696 : inconsistent with each other under contention. */
697 :
698 : void
699 : fd_accdb_cache_class_occupancy( fd_accdb_t * accdb,
700 : ulong * used,
701 : ulong * max,
702 : ulong * reserved );
703 :
704 : /* fd_accdb_cache_class_thresholds returns the per-size-class preeviction
705 : thresholds as free-slot counts. Each output array must have
706 : FD_ACCDB_CACHE_CLASS_CNT entries. free_target[c] is the free count
707 : the background preevict pass tries to restore. free_low_water[c] is
708 : the free count below which the preevict pass starts. Both are set
709 : once at init and are stable for the lifetime of the cache. */
710 :
711 : void
712 : fd_accdb_cache_class_thresholds( fd_accdb_t * accdb,
713 : ulong * free_target,
714 : ulong * free_low_water );
715 :
716 : /* FD_ACCDB_METRICS_WRITE publishes the per-joiner accdb runtime metrics
717 : for tile prefix TILE. TILE must be a tile that declares the
718 : AccdbAccountReadonlyAcquired/... counters in metrics.xml (e.g. EXECLE,
719 : EXECRP, REPLAY, TOWER, ACCDB). m must be a fd_accdb_metrics_t const *
720 : for the joiner whose counters should be published. */
721 :
722 0 : #define FD_ACCDB_METRICS_WRITE( TILE, m ) do { \
723 0 : fd_accdb_metrics_t const * _m = (m); \
724 0 : FD_MCNT_ENUM_COPY( TILE, ACCDB_ACCOUNT_READONLY_ACQUIRED, _m->accounts_acquired_per_class ); \
725 0 : FD_MCNT_ENUM_COPY( TILE, ACCDB_ACCOUNT_WRITABLE_ACQUIRED, _m->writable_accounts_acquired_per_class ); \
726 0 : FD_MCNT_ENUM_COPY( TILE, ACCDB_ACCOUNT_EVICTED, _m->accounts_evicted_per_class ); \
727 0 : FD_MCNT_ENUM_COPY( TILE, ACCDB_ACCOUNT_NEW_COMMITTED, _m->accounts_committed_new_per_class ); \
728 0 : FD_MCNT_ENUM_COPY( TILE, ACCDB_ACCOUNT_OVERWRITE_COMMITTED, _m->accounts_committed_overwrite_per_class ); \
729 0 : FD_MCNT_ENUM_COPY( TILE, ACCDB_ACCOUNT_CACHE_MISSED, _m->accounts_not_found_per_class ); \
730 0 : FD_MCNT_SET ( TILE, ACCDB_ACCOUNT_WAITED, _m->accounts_waited ); \
731 0 : FD_MCNT_SET ( TILE, ACCDB_BATCH_ACQUIRED, _m->acquire_calls ); \
732 0 : FD_MCNT_SET ( TILE, ACCDB_CACHE_RESERVE_RETRIED, _m->acquire_failed ); \
733 0 : FD_MCNT_SET ( TILE, ACCDB_DISK_READ_BYTES, _m->bytes_read ); \
734 0 : FD_MCNT_SET ( TILE, ACCDB_DISK_READ, _m->read_ops ); \
735 0 : FD_MCNT_SET ( TILE, ACCDB_DISK_WRITTEN_BYTES, _m->bytes_written ); \
736 0 : FD_MCNT_SET ( TILE, ACCDB_DISK_WRITTEN, _m->write_ops ); \
737 0 : FD_MCNT_SET ( TILE, ACCDB_COPIED_BYTES, _m->bytes_copied ); \
738 0 : } while(0)
739 :
740 : /* FD_ACCDB_METRICS_WRITE_RO is the read-only joiner subset of
741 : FD_ACCDB_METRICS_WRITE. It only emits the counters that
742 : fd_accdb_read_one_nocache touches; tiles that join readonly
743 : (e.g. RPC) declare only this subset of counters in metrics.xml. */
744 :
745 0 : #define FD_ACCDB_METRICS_WRITE_RO( TILE, m ) do { \
746 0 : fd_accdb_metrics_t const * _m = (m); \
747 0 : FD_MCNT_ENUM_COPY( TILE, ACCDB_ACCOUNT_READONLY_ACQUIRED, _m->accounts_acquired_per_class ); \
748 0 : FD_MCNT_ENUM_COPY( TILE, ACCDB_ACCOUNT_CACHE_MISSED, _m->accounts_not_found_per_class ); \
749 0 : FD_MCNT_SET ( TILE, ACCDB_ACCOUNT_WAITED, _m->accounts_waited ); \
750 0 : FD_MCNT_SET ( TILE, ACCDB_DISK_READ_BYTES, _m->bytes_read ); \
751 0 : FD_MCNT_SET ( TILE, ACCDB_DISK_READ, _m->read_ops ); \
752 0 : FD_MCNT_SET ( TILE, ACCDB_COPIED_BYTES, _m->bytes_copied ); \
753 0 : } while(0)
754 :
755 : FD_PROTOTYPES_END
756 :
757 : #endif /* HEADER_fd_src_flamenco_accdb_fd_accdb_h */
|