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