Line data Source code
1 : #ifndef HEADER_fd_src_discof_replay_fd_rdisp_h
2 : #define HEADER_fd_src_discof_replay_fd_rdisp_h
3 :
4 : #include "../../disco/fd_disco_base.h"
5 :
6 : /* fd_rdisp defines methods for building a DAG (directed acyclic graph)
7 : of transactions, executing them in the appropriate order with the
8 : maximum amount of parallelism, and keeping track of the accounts they
9 : write to compute the LtHash efficiently.
10 :
11 : Transactions must appear to execute in the order in which they occur
12 : in the block (the "serial fiction"). However, when two transactions
13 : are independent, they can actually be scheduled in either order, or
14 : in parallel. Two transactions are independent unless one writes to
15 : an account that the other reads or writes. If two transactions are
16 : not independent, the one that comes earlier in the block is a
17 : predecessor transaction of the one that comes later in the block. In
18 : order to ensure correct execution, a transaction cannot be executed
19 : until all its predecessor transactions have completed. In this file,
20 : a transaction completing means that the results of its account writes
21 : will be visible to a subsequent transaction executing on any core.
22 :
23 : Shockingly, this dispatcher does not need to keep a copy of
24 : transactions (more on this later), which means it operates almost
25 : entirely on transaction indices. An index is a positive integer
26 : (uint) up to some maximum. The 0 index is a sentinel, and doesn't
27 : correspond to a real transaction. It's up to the caller to maintain
28 : the mapping between each transaction index and the transaction itself.
29 :
30 : A transaction index is in one of the following states:
31 : * FREE, which means it doesn't correspond to a transaction.
32 : * PENDING, which means it corresponds to a transaction that can't
33 : be scheduled yet because it must come after transactions that
34 : have not completed execution yet.
35 : * READY, which means all predecessor transactions have completed
36 : execution, but this transaction index has not been returned by
37 : get_next_ready yet.
38 : * ZOMBIE, which means the transaction completed execution, and
39 : successor transactions may be transitioned to the READY state,
40 : but the transaction index should not be recycled yet, because
41 : there are outstanding non-execution tasks associated with this
42 : transaction.
43 : * DISPATCHED, which means this transaction index was returned by
44 : get_next_ready but has not been completed yet.
45 :
46 :
47 : --------------> PENDING
48 : add_txn | |
49 : FREE ---------| |
50 : ^ | V get_next_ready
51 : | --------------> READY ----------------
52 : | |
53 : | |
54 : | complete_txn(reclaim) V
55 : |<-------------------------------------------- DISPATCHED
56 : | |
57 : | | complete_txn(noreclaim)
58 : | complete_txn(reclaim) V
59 : |<---------------------------------------------- ZOMBIE
60 :
61 :
62 : Additionally, fd_rdisp is block-aware, and even somewhat fork-aware.
63 : Prior to inserting a transaction, a block must be added. Then, when
64 : a transaction is inserted, the caller must specify the block it is
65 : part of.
66 :
67 : At all times, each block is either STAGED or UNSTAGED. Blocks that
68 : are STAGED benefit from the full parallelization-maximizing dispatch,
69 : but at most four linear chains of blocks can be STAGED at any time.
70 : Assuming the limit of four is still satisfied, when a block is added,
71 : it may be added as STAGED. Otherwise, if it's added as UNSTAGED, it
72 : may be promoted to STAGED later, though this is worse for performance
73 : than adding it as STAGED initially. Blocks may be demoted from
74 : STAGED to UNSTAGED only if the linear chain doesn't contain any
75 : PENDING, READY, or DISPATCHED transactions.
76 :
77 : Prior to properly introducing staging lanes, we need to introduce two
78 : more concepts. A block can be insert-ready, schedule-ready, neither,
79 : or both. A block must be insert-ready to call add_txn on
80 : it; a block must be schedule-ready to call get_next_ready on it.
81 : Various functions either require or modify these properties of a
82 : block. These properties are necessary but not sufficient for the
83 : respective functions to succeed; for example, a block may be
84 : schedule-ready but empty, in which case, scheduling will still not
85 : succeed. An UNSTAGED block is always both insert-ready and
86 : schedule-ready.
87 :
88 : A staging lane can contain a single block or a sequence of blocks.
89 : When a staging lane contains more than one block, some restrictions
90 : apply, namely, only the first block in the sequence is
91 : schedule-ready, and only the last block in the sequence is
92 : insert-ready, where first and last are determined by the order in
93 : which the block is staged. Although this restriction makes the
94 : interface a bit more complicated, it's driven by performance
95 : requirements and common use cases. Basically, there's no use for
96 : being able to replay a block before we've replayed its parent block.
97 :
98 : Basically, rdisp is designed to handle three situations well:
99 : * The normal case, where replay is pretty much caught up, and there
100 : are only one or two forks, containing only one or two un-replayed
101 : blocks each.
102 : * The startup case, where repair is far ahead of replay, but there's
103 : only a single fork, or very few forks
104 : * The DoS case, where there are many forks, perhaps duplicate
105 : blocks, and we don't know which to prioritize yet, but we will
106 : execute some, and prune the rest later. This includes the case
107 : where we stop receiving transactions from some of the forks but
108 : don't know that the block has ended.
109 :
110 : In the normal case, there are few enough unreplayed blocks that it
111 : doesn't really matter how the staging lanes are used. For the
112 : startup case, the long linear chains of blocks can all be STAGED using
113 : the same lane with no performance degradation. In the DoS case, most
114 : of the forks will be UNSTAGED, and using some combination of
115 : replaying, cancelling, and demoting blocks, staging lanes can be freed
116 : up so that the canonical chain can emerge.
117 :
118 : Consider the following example of storing a fork tree in staging
119 : lanes:
120 : - Slot 10's parent is not specified
121 : - Slot 11's parent is slot 10
122 : - Slot 13's parent is slot 11
123 : - Slot 14's parent is also slot 11
124 :
125 : Since the caller chooses the staging lane, the caller may choose
126 : between
127 : Lane 0: 10 --> 11 --> 13
128 : Lane 1: 14
129 : and
130 : Lane 0: 10 --> 11 --> 14
131 : Lane 1: 13.
132 : or any of the various combinations that consume more staging lanes.
133 : Note that the concept of staging lanes is a performance optimization,
134 : not a safety feature. With the first arrangement, the caller cannot
135 : call get_next_ready on slot 13 in between slots 10 and 11, but
136 : there's no issue with calling it on slot 14 then, which would
137 : obviously result in an incorrect replay. It's ultimately the caller's
138 : responsibility to ensure correct replay.
139 :
140 : In addition to scheduling transactions efficiently, rdisp also
141 : provides functionality for computing the LtHash efficiently. For
142 : each account that is written to in the block, we need to subtract the
143 : account's initial hash value as of the start of the block and then
144 : add in the account's final hash value as of the end of the block to
145 : compute the LtHash. Thus, at least naively, there are two conceptual
146 : tasks per writable account, named for how the value is used: the
147 : subtraction task and the addition task.
148 :
149 : The subtraction task is a bit simpler, since we know we're going to
150 : need it for a given account once we see the first transaction that
151 : marks it as writable. In addition, because it reads the previous
152 : value of the account, it never conflicts with executing transactions.
153 : The subtraction tasks are only on the critical path in the way
154 : transaction sigverify tasks are; they must be done before voting on
155 : the block, but that's it.
156 :
157 : The addition tasks are much trickier to get right. We want to
158 : dispatch the addition task for an account as soon as the last write
159 : completes, but we don't know a priori when a write was the last
160 : write, unless we wait until the slot ends. However, waiting until
161 : the slot ends means it's much more likely addition tasks end up on
162 : the critical path, and our main goal is to avoid that. Furthermore,
163 : if we schedule an addition task, then subsequently receive a
164 : transaction that writes to that account, we need to be careful not to
165 : cause a data race. Even though we wouldn't care about the value
166 : returned, the current implementation of the acctdb does not tolerate
167 : races.
168 :
169 : All of this leads to a split and slightly complicated API. Addition
170 : tasks are represented with pseudo-transactions the caller can create
171 : and then schedule when idle (see below). These pseudo-transactions
172 : can be returned when scheduling the next transaction. On the other
173 : hand, when adding a transaction, rdisp optionally populates a bitmask
174 : indicating which writable accounts have not been marked as writable
175 : in a transaction inserted since the most recent pseudo-transaction
176 : was created for it. That's a little obtuse, but essentially creating
177 : a pseudo-transaction for an account address makes it new again. */
178 :
179 : #define FD_RDISP_MAX_DEPTH 0x7FFFFFUL /* 23 bit numbers, approx 8M */
180 : #define FD_RDISP_MAX_BLOCK_DEPTH 0xFFFFUL /* 16 bits */
181 1000 : #define FD_RDISP_UNSTAGED ULONG_MAX
182 5031 : #define FD_RDISP_MAX_ACCT_PER_TXN FD_TXN_ACCT_ADDR_MAX
183 :
184 : /* 62,500,000 CUs gets 3137 transactions with 64 writers and one with 34
185 : writers for a total of 200,802. Then we add 8k for inflation
186 : rewards, 10 for sysvars, 4000 for vote accounts, and a bit of
187 : padding. */
188 25085 : #define FD_RDISP_MAX_WRITERS_PER_BLOCK 217354UL
189 :
190 0 : #define FD_RDISP_LTHASH_PSEUDO_TXN 0x80000000UL
191 :
192 : /* FD_RDISP_MAX_SCORE is the largest score that will be assigned to a
193 : transaction internally. Lower scores are better. It should be close
194 : to 1 to maximize the scoring function's dynamic range, but small
195 : enough that when added to relevant sized integers, it is distinct
196 : from the next integer. That stops being true at 2^17, where
197 : I+0.996f rounds to I+1 in float. Pseudo-transactions can push a
198 : block's integer parts (last_serializing, inserted_cnt) that high, so
199 : fd_rdisp_score clamps the sum. */
200 0 : #define FD_RDISP_MAX_SCORE 0.996f
201 :
202 : /* FD_RDISP_PTXN_SCORE is the fractional score of every staged LtHash
203 : pseudo-transaction. It is above FD_RDISP_MAX_SCORE, so within a
204 : serializing class every READY transaction is handed out before any
205 : READY pseudo-transaction. Pseudo-transactions are not ordered among
206 : themselves. fd_rdisp_score keeps the sum below integer_part+1 for
207 : every integer part below 2^24. From 2^14 the largest transaction
208 : fraction and this constant round to the same float, and from 2^16 the
209 : clamp sets in. A block of a few thousand transactions and additions
210 : stays well below 2^14. */
211 0 : #define FD_RDISP_PTXN_SCORE 0.997f
212 :
213 : /* FD_RDISP_READY_ACCT_EMA_MAX bounds the reference EMA of an account
214 : that fd_rdisp_add_ready_pseudo_txn may hand out, which is to say an
215 : account still worth speculating on once its writers have completed.
216 : The EMA adds 0.005 per transaction referencing the account and decays
217 : per transaction inserted, so an account referenced by one in ten
218 : recent transactions sits near 0.1, one in twenty near 0.05. An
219 : account over the threshold is considered hot and likely written again
220 : before the block ends. The EMA is always less than 1.0, so we can
221 : use a threshold of 1.0 to state a policy of speculating on every
222 : account. */
223 1008 : #define FD_RDISP_READY_ACCT_EMA_MAX (1.0f)
224 :
225 : /* fd_rdisp_score returns frac+integer_part as a float that is
226 : >=integer_part and strictly <integer_part+1. The integer part of a
227 : score is how many completions the node waits for, so a sum that
228 : rounded up to integer_part+1 would wait for one completion too many,
229 : possibly one that never comes. For a fraction of at most
230 : FD_RDISP_MAX_SCORE the plain sum is below integer_part+1 until 2^17
231 : and is returned bit for bit. A sum that rounds up, which the
232 : pseudo-transaction fraction does from 2^16, is replaced by the
233 : largest float below integer_part+1. Requires 0<=frac<1 and
234 : integer_part<2^24. */
235 : FD_FN_CONST static inline float
236 : fd_rdisp_score( float frac,
237 1542 : uint integer_part ) {
238 1542 : float s = frac + (float)integer_part;
239 1542 : float next = (float)(integer_part+1U);
240 1542 : if( FD_UNLIKELY( s>=next ) ) {
241 : /* next>=1.0f is a positive normal, so the float just below it is
242 : one bit pattern down. */
243 0 : union { float f; uint u; } t;
244 0 : t.f = next;
245 0 : t.u--;
246 0 : s = t.f;
247 0 : }
248 1542 : return s;
249 1542 : }
250 :
251 : struct fd_rdisp;
252 : typedef struct fd_rdisp fd_rdisp_t;
253 :
254 : /* fd_rdisp_neww_* is a bitset used to indicate which accounts are new
255 : writers, where new has the specific meaning described above and in
256 : the comment for fd_rdisp_add_txn. Accounts marked read-only will
257 : have the corresponding bit clear. */
258 : #define SET_NAME fd_rdisp_neww
259 : #define SET_MAX FD_RDISP_MAX_ACCT_PER_TXN
260 : #include "../../util/tmpl/fd_set.c"
261 :
262 : /* fd_rdisp is set up so that the tag of a block can be adjusted to
263 : account for differences in handling duplicate blocks/equivocation. */
264 : #define FD_RDISP_BLOCK_TAG_T ulong
265 :
266 : FD_PROTOTYPES_BEGIN
267 :
268 : /* fd_rdisp_{align,footprint} return the required alignment and
269 : footprint in bytes for a region of memory to be used as a dispatcher.
270 : depth is the maximum number of transaction indices that can be
271 : tracked at a time. Depth must be at least 2 and cannot exceed
272 : FD_RDISP_MAX_DEPTH. block_depth is the maximum number of blocks that
273 : this dispatcher can track. block_depth must be at least 4 and cannot
274 : exceed FD_RDISP_MAX_BLOCK_DEPTH. The account pool holds the
275 : FD_RDISP_MAX_ACCT_PER_TXN accounts each of the depth transactions can
276 : reference, plus acct_extra entries for the accounts a staging lane
277 : keeps in its writable set past the life of their transactions. See
278 : fd_rdisp_add_pseudo_txn. Pass 0 for acct_extra when the writable
279 : sets are not used. Returns 0 if the account pool would exceed the
280 : index space of an account edge. */
281 : ulong fd_rdisp_align ( void );
282 : ulong fd_rdisp_footprint( ulong depth, ulong block_depth, ulong acct_extra );
283 :
284 :
285 : /* fd_rdisp_new formats a region of memory that satisfies the required
286 : footprint and alignment for use as a dispatcher. depth and
287 : block_depth are as explained in fd_rdisp_footprint. mem is a pointer
288 : to the first byte of a region of memory with the required alignment
289 : and footprint. seed is an arbitrary ulong that is used to determine
290 : a seed of various internal hash tables. On return, the caller will
291 : not be joined.
292 :
293 : fd_rdisp_join joins the caller to the dispatcher, enabling it for
294 : use. */
295 : void *
296 : fd_rdisp_new( void * mem,
297 : ulong depth,
298 : ulong block_depth,
299 : ulong acct_extra,
300 : ulong seed );
301 :
302 : fd_rdisp_t *
303 : fd_rdisp_join( void * mem );
304 :
305 : /* fd_rdisp_suggest_staging_lane recommends a staging lane to use for a
306 : potential new block that has a parent block with block tag
307 : parent_block. duplicate is non-zero if this is not the first block
308 : we've seen for its slot.
309 :
310 : This function uses the following logic:
311 : 1. If it's a duplicate, suggest FD_RDISP_UNSTAGED
312 : 2. If parent is the last block in any existing staging lane, suggest
313 : that lane
314 : 3. If there is at least one free lane, suggest a free lane
315 : 4. Else, suggest FD_RDISP_UNSTAGED
316 : Note that this function does not add the block (use add_block) for
317 : that, and does not modify the state of the dispatcher. The caller
318 : should feel free to use or not use the suggested staging lane. */
319 : ulong
320 : fd_rdisp_suggest_staging_lane( fd_rdisp_t const * disp,
321 : FD_RDISP_BLOCK_TAG_T parent_block,
322 : int duplicate );
323 :
324 :
325 : /* fd_rdisp_add_block allocates a new block with the tag new_block from
326 : disp's internal pool. new_block must not be the invalid block tag
327 : value, and it must be distinct from all other values passed as
328 : new_block in all prior calls to fd_rdisp_add_block.
329 :
330 : staging_lane must be either [0,4) or FD_RDISP_UNSTAGED. If
331 : staging_lane is FD_RDISP_UNSTAGED, the block will be UNSTAGED (see
332 : the long comment at the beginning of this header), schedule-ready,
333 : and insert-ready.
334 : If staging_lane is in [0, 4), the block will be STAGED, and it will
335 : be insert-ready. If the specified staging lane contained any blocks
336 : at the time of the call, the last one will no longer be insert-ready,
337 : making this the only insert-ready block in the lane. If the
338 : specified staging lane did not contain any blocks at the time of the
339 : call, then the newly added block will also be schedule-ready.
340 :
341 : Note: if staging lane is in [0, 4) and the specified staging lane
342 : contains a block, that block must have all of its LtHash
343 : pseudo-transactions created prior to calling this function. See
344 : fd_rdisp_add_all_pseudo_txn for more details.
345 :
346 : On successful return, the tag new_block will be usable for other
347 : functions that take a block tag block.
348 :
349 : Returns 0 on success, and -1 on error, which can only happen if out
350 : of resources (the number of unremoved blocks is greater than or equal
351 : to the block_depth) or if new_block was already known. */
352 : int
353 : fd_rdisp_add_block( fd_rdisp_t * disp,
354 : FD_RDISP_BLOCK_TAG_T new_block,
355 : ulong staging_lane );
356 :
357 : /* fd_rdisp_remove_block deallocates a previously-allocated block with
358 : the block tag block, freeing all resources associated with it. block
359 : must be empty (not contain any transactions or pseudo-transactions in
360 : the PENDING, READY, DISPATCHED, or ZOMBIE states), not have any
361 : createable pseudo-transaction, and be schedule-ready. Returns 0 on
362 : success, and -1 if block is not known. After a successful return,
363 : the block tag block will not be known. */
364 : int
365 : fd_rdisp_remove_block( fd_rdisp_t * disp,
366 : FD_RDISP_BLOCK_TAG_T block );
367 :
368 :
369 : /* fd_rdisp_abandon_block is similar to remove_block, but works when the
370 : block contains transactions. It immediately transitions all
371 : transactions part of the block to FREE, and then removes the block as
372 : in fd_rdisp_remove_block. Note that if a transaction is DISPATCHED
373 : at the time of the call complete_txn should NOT be called on that
374 : transaction index when it completes. The specified block must be
375 : schedule-ready.
376 :
377 : In V1 of the dispatcher, this only works if there are no DISPATCHED
378 : transactions.
379 :
380 : Returns 0 on success, and -1 if block is not known. After a
381 : successful return, the block tag block will not be known. */
382 : int
383 : fd_rdisp_abandon_block( fd_rdisp_t * disp,
384 : FD_RDISP_BLOCK_TAG_T block );
385 :
386 :
387 : /* fd_rdisp_{promote,demote}_block modify whether a block is STAGED or
388 : UNSTAGED. Specifically, promote_block promotes the specified block
389 : from UNSTAGED to STAGED, using the specified staging_lane.
390 : demote_block demotes the specified block from STAGED to UNSTAGED.
391 : disp must be a valid local join. If the block tag block is not
392 : known, or is not in the requisite state (UNSTAGED from promote,
393 : STAGED for demote), returns -1.
394 :
395 : When promote_block promotes the specified block, it is placed at the
396 : end of the linear chain in the specified staging_lane. That means
397 : the operation is as if abandon_block were called on the specified
398 : block, then add_block with the specified staging_lane, and then all
399 : transactions in the PENDING and READY states in this block were
400 : re-added in the same order they were originally added. It is
401 : undefined behavior if the specified block contains any transactions
402 : in the DISPATCHED stage. As in add_block, upon successful return,
403 : the specified block will be insert-ready, but will only be
404 : schedule-ready if the specified staging lane was empty at the time of
405 : the call.
406 :
407 : demote_block has the additional requirement that the specified block
408 : must be schedule-ready and empty, that is, not containing any
409 : transactions in the PENDING, READY, or DISPATCHED states.
410 :
411 : For both functions, every LtHash pseudo-transaction created for the
412 : block must have completed, and information about which accounts need
413 : addition tasks is carried over. */
414 :
415 : int
416 : fd_rdisp_promote_block( fd_rdisp_t * disp,
417 : FD_RDISP_BLOCK_TAG_T block,
418 : ulong staging_lane );
419 : int
420 : fd_rdisp_demote_block( fd_rdisp_t * disp,
421 : FD_RDISP_BLOCK_TAG_T block );
422 :
423 : /* fd_rdisp_clear_writable_set forgets every account in the writable set
424 : of the block's lane. No addition pseudo-transaction will be created
425 : for them and demotion registers none of them as unstaged writable
426 : accounts. Transactions added afterwards with a NULL new_writable
427 : keep the set empty. Returns 0 on success and -1 if the block is not
428 : found, not staged, or not schedule-ready. */
429 : int
430 : fd_rdisp_clear_writable_set( fd_rdisp_t * disp,
431 : FD_RDISP_BLOCK_TAG_T block );
432 :
433 :
434 : /* fd_rdisp_rekey_block renames the block with tag old_tag so that it
435 : has tag new_tag instead. The block retains all transactions, its
436 : STAGED/UNSTAGED state, etc. On successful return, tag old_tag will
437 : no longer be a known tag, and new_tag must be used in any future
438 : calls to refer to the block previously known as old_tag.
439 :
440 : disp must be a valid local join. new_tag must not be a known tag,
441 : but old_tag must be a known tag.
442 :
443 : Return 0 on success and -1 on error. The only error cases are if
444 : new_tag is already a known tag or old_tag is not a known tag. */
445 : int
446 : fd_rdisp_rekey_block( fd_rdisp_t * disp,
447 : FD_RDISP_BLOCK_TAG_T new_tag,
448 : FD_RDISP_BLOCK_TAG_T old_tag );
449 :
450 : /* fd_rdisp_add_txn adds a transaction to the block with tag
451 : insert_block in serial order. That means that this dispatcher will
452 : ensure this transaction appears to execute after each transaction
453 : added to this block in a prior call.
454 :
455 : insert_block must be a known block that is insert-ready. txn,
456 : payload, and alts describe the transaction to be added. txn must be
457 : the result of parsing payload, and alts contains the expansion and
458 : selection of the address lookup tables mentioned in the transaction
459 : (i.e. all the writable accounts followed by all the read-only
460 : accounts). alts may be NULL, even if the transaction specifies that
461 : it loads accounts from an address lookup table; in this case,
462 : addresses from ALTs are ignored. There must be no duplicate accounts
463 : in the transaction to be added.
464 :
465 : Shockingly, this dispatcher does not retain any read interest (much
466 : less write interest) in the transaction (txn, payload, or alts). On
467 : success, it returns a transaction index that was previously in the
468 : FREE state. This API is designed to facilitate a model of use where
469 : the replay tile copies the incoming transaction to a region of
470 : private memory, adds it to this dispatcher, and then copies it (using
471 : non-temporal stores) to the output dcache at a location determined by
472 : the returned index. Although there are two memcpys in this approach,
473 : it should result in fewer cache misses.
474 :
475 : If serializing is non-zero, this transaction will be a serialization
476 : point: all transactions added prior to this one must complete before
477 : this transaction can be scheduled, and this transaction must complete
478 : before any subsequently added transactions can be scheduled. This is
479 : not good for performance, but is useful for the rare case when
480 : repair/turbine is several slots ahead of replay, and a transaction
481 : loads some accounts from an address lookup table, but we haven't
482 : executed the transaction to populate that part of the address lookup
483 : table yet. This is the primary use for alts==NULL.
484 :
485 : If new_writable is non-NULL, the provided bitset will be populated.
486 : Upon return, bit i will be set in new_writable if account i (in the
487 : standard order) was writable, and this is the first transaction in
488 : this block to record it as writable since the most recent call to
489 : fd_rdisp_add_pseudo_txn that created a pseudo-transaction for this
490 : account address; otherwise bit i will be cleared. If new_writable is
491 : NULL, the accounts in this transaction will be ignored for the
492 : purposes of LtHash tracking. Obviously, if alts==NULL, accounts in
493 : alts will not be included in LtHash tracking.
494 :
495 : rdisp can track depth*FD_RDISP_MAX_ACCT_PER_TXN writable accounts,
496 : which is enough so it doesn't run out even if the whole depth is full
497 : with transactions that write to the maximum number of unique
498 : accounts. However, it is possible to run out of state for tracking
499 : accounts by adding transactions, scheduling and completing them
500 : repeatedly. If depth is less than
501 : max CUs per block
502 : -------------------------------,
503 : 300 * FD_RDISP_MAX_ACCT_PER_TXN
504 :
505 : the caller MUST create and schedule pseudo-transactions every depth
506 : transactions or depth*MAX_ACCT_PER_TXN writable accounts. For
507 : example, at 75M CUs and MAX_ACCT_PER_TXN=128, care needs to be taken
508 : if depth is less than 2000.
509 :
510 : Returns 0 and does not add the transaction on failure. Fails if
511 : there were no free transaction indices, if the block with tag
512 : insert_block did not exist, or if it was not schedule-ready.
513 :
514 : At the time this function returns, the returned transaction index
515 : will be in the PENDING or READY state, depending on whether it
516 : conflicts with something previously inserted. */
517 : ulong
518 : fd_rdisp_add_txn( fd_rdisp_t * disp,
519 : FD_RDISP_BLOCK_TAG_T insert_block,
520 : fd_txn_t const * txn,
521 : uchar const * payload,
522 : fd_acct_addr_t const * alts,
523 : int serializing,
524 : fd_rdisp_neww_t * new_writable );
525 :
526 : /* fd_rdisp_peek_free_txn returns the transaction index the next
527 : successful fd_rdisp_add_txn will return if no transaction is
528 : completed and reclaimed in between, or 0 if there are no free
529 : transaction indices. A hint for prefetching the caller's per
530 : transaction state; it does not change the dispatcher. */
531 : ulong
532 : fd_rdisp_peek_free_txn( fd_rdisp_t const * disp );
533 :
534 : /* fd_rdisp_get_next_ready returns the transaction index of a READY
535 : transaction that was inserted with block tag schedule_block if one
536 : exists, otherwise an LtHash pseudo-transaction if one exists,
537 : otherwise 0. The block with the tag schedule_block must be
538 : schedule-ready.
539 :
540 : If there are multiple READY transactions, which exact one is returned
541 : is arbitrary. That said, this function does make some effort to pick
542 : one that (upon completion) will unlock more parallelism. Similarly,
543 : if there are multiple READY LtHash pseudo-transactions, this function
544 : makes some effort to return one that is less likely to be written to
545 : again in this block. You can identify whether the returned value is
546 : a normal transaction or an LtHash pseudo-transaction by examining the
547 : high bit (returned value & FD_RDISP_LTHASH_PSEUDO_TXN non-zero), and
548 : decode it with fd_rdisp_pseudo_txn_to_addr.
549 :
550 : disp must be a valid local join. At the time this function returns,
551 : the returned transaction or pseudo-transaction index (if nonzero)
552 : will transition to the DISPATCHED state. */
553 : ulong
554 : fd_rdisp_get_next_ready( fd_rdisp_t * disp,
555 : FD_RDISP_BLOCK_TAG_T schedule_block );
556 :
557 : /* fd_rdisp_complete_txn notifies the dispatcher that the specified
558 : transaction or pseudo-transaction (which must have been in the
559 : DISPATCHED state) has completed. Logs warning and returns on error
560 : (invalid txn_idx, not in DISPATCHED state). This function may cause
561 : other transactions to transition from PENDING to READY.
562 :
563 : At the time this function returns, the specified transaction index
564 : will be in the FREE state, if reclaim!=0. Otherwise, if reclaim==0,
565 : the specified transaction index will be in the ZOMBIE state. A
566 : ZOMBIE transaction has the exact same effect as a FREE transaction on
567 : causing other transactions to transition from PENDING to READY.
568 : However, the dispatcher will not reclaim the specified transaction
569 : index until a future invocation of fd_rdisp_complete_txn with the
570 : same txn_idx, which must have reclaim!=0. This is useful when there
571 : is non-execution work to be done asynchronously for the transaction,
572 : but the caller would like to unblock the execution of transactions
573 : that depend on this one. If txn_idx is an LtHash pseudo-transaction,
574 : this function will ignore the value of reclaim and behave like
575 : reclaim!=0. */
576 : void
577 : fd_rdisp_complete_txn( fd_rdisp_t * disp,
578 : ulong txn_idx,
579 : int reclaim );
580 :
581 : /* fd_rdisp_pseudo_txn_to_addr stores to out the account address
582 : (pubkey) associated with a pseudo-transaction index txn_idx. txn_idx
583 : must be in the PENDING, READY, or DISPATCHED state, which means
584 : txn_idx must have been returned from get_next_ready or
585 : fd_rdisp_add_pseudo_txn and must have FD_RDISP_LTHASH_PSEUDO_TXN set.
586 : Note that this means once you call complete_txn on a txn_idx, you
587 : cannot call this function on it any more. disp must be a valid local
588 : join. out must be non-NULL. */
589 : void
590 : fd_rdisp_pseudo_txn_to_addr( fd_rdisp_t const * disp,
591 : ulong txn_idx,
592 : fd_acct_addr_t * out );
593 :
594 : /* fd_rdisp_add_pseudo_txn creates an LtHash addition pseudo-transaction
595 : for an arbitrary account that was marked as writable by a transaction
596 : added to insert_block with add_txn since the last call to
597 : add_pseudo_txn that returned that address (if any). If no account
598 : meets that condition, returns 0.
599 :
600 : fd_rdisp_add_all_pseudo_txn repeats calls to add_pseudo_txn until it
601 : returns 0. The transaction indices of the pseudo-transactions that
602 : get created in the process are not provided to the caller.
603 :
604 : disp must be a valid local join, and insert_block must be a known
605 : block that is insert-ready. Returns the transaction index for the
606 : pseudo-txn that was created, but for most uses, this value is not
607 : very meaningful.
608 :
609 : Accounts are classified into two classes. The first class of
610 : accounts, whose every writer in the block has completed, and whose
611 : reference EMA is under the threshold, come out first, last completed
612 : first. A pseudo-transaction for one of them is READY on creation,
613 : barring an outstanding serializing transaction. The second class of
614 : accounts come out coldest first, by reference EMA, and their
615 : pseudo-transactions are PENDING behind the writer still outstanding.
616 : fd_rdisp_add_ready_pseudo_txn pops from the first class only.
617 :
618 : Summing all that up: if you're catching up or otherwise not at the
619 : tip of the chain, just wait until the end of the block and call
620 : add_all_pseudo_txn. If you are at the tip of the chain, dispatch all
621 : READY transactions, then all the sigverify, PoH verify, and LtHash
622 : subtraction tasks first, since the results of those are definitely
623 : needed. If you're still idle, call add_ready_pseudo_txn once, then
624 : get_next_ready once, and repeat.
625 :
626 : IMPORTANT: if insert_block is staged, add_pseudo_txn must be called
627 : until it returns 0 prior to inserting another block in the same
628 : staging lane. You may also use fd_rdisp_add_all_pseudo_txn for this
629 : purpose.
630 :
631 : Note: If insert_block is unstaged, pseudo-transactions are always
632 : after normal transactions. This behavior differs slightly from
633 : staged blocks (where the pseudo-transaction is inserted in a way that
634 : preserves serial fiction) and may change in the future. This
635 : shouldn't cause problems, but it probably means that the best
636 : approach for unstaged blocks is to call add_all_pseudo_txn after the
637 : last transaction is received. Anything else is trying to optimize
638 : for dispatching performance, but in a situation where that doesn't
639 : really apply.
640 :
641 : Note: the criteria described in the first paragraph is a bit
642 : complicated, so here's an easier way to think about it. You can
643 : imagine that when a transaction is added with add_txn, all its
644 : writable account addresses get added to a per-block set. Then this
645 : function removes one address from that set and creates a
646 : pseudo-transaction for it. If add_txn is subsequently called with a
647 : transaction that writes to that same account, it goes back into the
648 : set, so add_pseudo_txn may return it again. */
649 : ulong
650 : fd_rdisp_add_pseudo_txn( fd_rdisp_t * disp,
651 : FD_RDISP_BLOCK_TAG_T insert_block );
652 :
653 : void
654 : fd_rdisp_add_all_pseudo_txn( fd_rdisp_t * disp,
655 : FD_RDISP_BLOCK_TAG_T insert_block );
656 :
657 : /* fd_rdisp_add_ready_pseudo_txn is add_pseudo_txn restricted to the
658 : accounts whose addition can be hashed now, meaning every writer of
659 : the account in insert_block has completed and its reference EMA is
660 : under the threshold of fd_rdisp_set_ready_acct_ema_max. The
661 : pseudo-transaction it creates is READY on creation, unless a
662 : serializing transaction is still outstanding ahead of it. Returns 0
663 : if no account qualifies, or if insert_block is unstaged. */
664 : ulong
665 : fd_rdisp_add_ready_pseudo_txn( fd_rdisp_t * disp,
666 : FD_RDISP_BLOCK_TAG_T insert_block );
667 :
668 : /* fd_rdisp_set_ready_acct_ema_max sets the reference EMA threshold of
669 : fd_rdisp_add_ready_pseudo_txn, FD_RDISP_READY_ACCT_EMA_MAX by
670 : default. An account qualifies when its last writer completes under
671 : the threshold in force at that moment, so a later change does not
672 : reclassify accounts already in the set. */
673 : void
674 : fd_rdisp_set_ready_acct_ema_max( fd_rdisp_t * disp, float ema_max );
675 :
676 :
677 : /* fd_rdisp_add_writable and add_extra_pseudo_txn informs rdisp that the
678 : account address pointed to by addr should be considered having been
679 : written at this point in insertion order for the sake of LtHash
680 : tracking. The primary use case for these two functions is tracking
681 : LtHash tasks for serializing transactions, but it is also useful for
682 : accounts that are written by non-transaction processes (e.g. rewards
683 : distribution). add_extra_pseudo_txn takes the additional step of
684 : creating a pseudo transaction for this account address unless it is
685 : sure that an appropriate one already exists.
686 :
687 : disp must be a valid local join. insert_block must be insert-ready.
688 : schedule_block must be schedule-ready. If the block is both
689 : insert-ready and schedule-ready, prefer add_writable. addr is a
690 : pointer to the account address of interest.
691 :
692 : add_writable returns -1 on error (block not found or not
693 : insert-ready), 1 if this is the first write to the specified account
694 : since the most recent call to fd_rdisp_add_pseudo_txn that added a
695 : pseudo-transaction for this account address, and 0 otherwise. This
696 : is essentially the value of the bit in new_writable.
697 :
698 : On the other hand, add_extra_pseudo_txn returns the
699 : pseudo-transaction index that was created. WARNING:
700 : add_extra_pseudo_txn can result in two addition pseudo-transaction
701 : without an intervening write. This function shouldn't really exist
702 : because normally it's not possible to add to a block unless it is
703 : insert-ready, so it is a bit of a hack. */
704 : int
705 : fd_rdisp_add_writable( fd_rdisp_t * disp,
706 : FD_RDISP_BLOCK_TAG_T insert_block,
707 : fd_acct_addr_t const * addr );
708 : ulong
709 : fd_rdisp_add_extra_pseudo_txn( fd_rdisp_t * disp,
710 : FD_RDISP_BLOCK_TAG_T schedule_block,
711 : fd_acct_addr_t const * addr );
712 :
713 : typedef struct {
714 : FD_RDISP_BLOCK_TAG_T schedule_ready_block;
715 : FD_RDISP_BLOCK_TAG_T insert_ready_block;
716 : } fd_rdisp_staging_lane_info_t;
717 :
718 : /* fd_rdisp_staging_lane_info copies the current staging lane info to
719 : out. Returns a 4-bit bitset, where bit i being set means that
720 : staging lane i is occupied. If staging lane i is occupied, then
721 : out_sched[i] is populated. */
722 : ulong
723 : fd_rdisp_staging_lane_info( fd_rdisp_t const * disp,
724 : fd_rdisp_staging_lane_info_t out_sched[ static 4 ] );
725 :
726 : /* fd_rdisp_lane_writable_cnt returns the number of accounts in the
727 : writable set of staging_lane, which must be in [0,4).
728 :
729 : fd_rdisp_lane_ready_acct_cnt returns how many of them
730 : fd_rdisp_add_ready_pseudo_txn would currently pick from. */
731 : ulong
732 : fd_rdisp_lane_writable_cnt( fd_rdisp_t * disp, ulong staging_lane );
733 :
734 : ulong
735 : fd_rdisp_lane_ready_acct_cnt( fd_rdisp_t * disp, ulong staging_lane );
736 :
737 : /* fd_rdisp_verify does some light verification and internal consistency
738 : checks of some internal data structures. Aborts with an error
739 : message if anything fails verification. disp is a pointer to a valid
740 : local join, and scratch is a pointer to a region of scratch memory
741 : with at least depth+depth*FD_RDISP_MAX_ACCT_PER_TXN+acct_extra+2
742 : elements, one per transaction and pseudo-transaction slot, that will
743 : be clobbered (its contents at the time of the function call are
744 : ignored). */
745 : void
746 : fd_rdisp_verify( fd_rdisp_t const * disp,
747 : uint * scratch );
748 :
749 : void *
750 : fd_rdisp_leave( fd_rdisp_t * disp );
751 :
752 : void *
753 : fd_rdisp_delete( void * mem );
754 :
755 : FD_PROTOTYPES_END
756 :
757 : #endif /* HEADER_fd_src_discof_replay_fd_rdisp_h */
|