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