Skip to content

pre-flight fcu for speed bump#4194

Draft
advaita-saha wants to merge 8 commits into
masterfrom
preflight-fcu
Draft

pre-flight fcu for speed bump#4194
advaita-saha wants to merge 8 commits into
masterfrom
preflight-fcu

Conversation

@advaita-saha
Copy link
Copy Markdown
Contributor

@advaita-saha advaita-saha commented Apr 30, 2026

makes fcu call super-fast with minimal downside
Upon testing shows significant performance improvement, and improvement in attestations from CL side ( fewer attestation misses )

Comment thread execution_chain/core/chain/forked_chain.nim

# Enqueue the actual forkchoice apply. For ack-only fCUs we don't await
# the result. This avoids the response getting blocked behind the shared queue worker
let fcuFut = chain.queueForkChoice(headHash, finalizedBlockHash, safeBlockHash)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible for the queueForkChoice background job to fail in the background? Or after running preflightForkChoice and returning a success to the caller , should the job always succeed?

Copy link
Copy Markdown
Contributor Author

@advaita-saha advaita-saha May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theoretically it is not possible to fail.
The only possibility is if the db is corrupt, or somehow the db connection is broken ( or other db level failures )

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it might be possible for it to fail when processing multiple heads. The removeBlockFromCache function can remove blocks from the hashToBlock table which can cause forkChoice to fail. removeBlockFromCache gets called from two places updateBase and updateFinalized. If those functions get called asynchronously before the forkChoice task runs then you get a silent failure.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well the queueForkChoice just adds the fCU call to the asyncQueue. So nothing else calls it async. And the queue is processed one by one

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removeBlockFromCache gets called from two places updateBase and updateFinalized. If those functions get called asynchronously before the forkChoice task runs then you get a silent failure.

I think not possible in the current design with asyncQueue implementation

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm not sure. Maybe its ok because the single queue enforces the ordering as long as there are no await calls which yield to other tasks before the forkChoice task is added to the queue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jangko any thoughts on this ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm not sure. Maybe its ok because the single queue enforces the ordering as long as there are no await calls which yield to other tasks before the forkChoice task is added to the queue.

Since the queueForkChoice is a template and contains an await call (which is good for rate limiting purposes), then the await will yield the event loop meaning something else can run concurrently perhaps before the forkChoice task is added to the queue. Maybe this is fine as long as no other code calls updateBase or updateFinalized outside of the task queue flow.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that is true.
For now by design updateBase & updateFinalized is not called outside the queue

Will add some comments on this

@jangko
Copy link
Copy Markdown
Contributor

jangko commented May 1, 2026

LGTM

return err("Cannot find head block: " & headHash.short)

if safeHash != zeroHash32 and c.hashToBlock.getOrDefault(safeHash).isNil:
return err("Cannot find safe block: " & safeHash.short)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is more strict now than the implementation in forkChoice which would silently ignore if the safeHash doesn't exist in the hashToBlock map. Not sure if this change is desired or not.


# Enqueue the actual forkchoice apply. For ack-only fCUs we don't await
# the result. This avoids the response getting blocked behind the shared queue worker
let fcuFut = chain.queueForkChoice(headHash, finalizedBlockHash, safeBlockHash)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there is nothing logging the result of the forkChoice call. Even if we don't expect any failures we should probably log something. Perhaps the queueForkChoice asyncHandler could log the result of forkChoice.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surely yeah, will add some logs. Will help identify issues

Comment thread execution_chain/beacon/api_handler/api_forkchoice.nim
# Payload generation reads `chain.latestHeader`, so we must wait for the
# apply to complete before assembling the block.
(await fcuFut).isOkOr:
return invalidFCU(error, chain, header)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we end up awaiting here for the block building it might be better to not use the async queue flow at all if attrsOpt.isSome. Block building would be faster in that case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that PR is also ready on top of this. Just testing and ironing out few issues on that
( little difficult to test as I don't have much validators, so very less proposals to test )

@advaita-saha advaita-saha requested a review from tersec May 1, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants