Skip to content

perf: req/resp handler holds forkchoice exclusive lock during block processing on libp2p thread #708

@zclawz

Description

@zclawz

Problem

The libp2p rust bridge runs on its own thread (rustBridgeThread). onReqRespResponse (handling blocks received from peers) fires on that thread and calls directly into processBlockByRootChunkchain.onBlock()forkChoice.onBlock() (exclusive mutex.lock()) + forkChoice.updateHead() (exclusive lock).

Meanwhile the main libxev event loop calls forkChoice.onInterval() (also exclusive mutex). During block sync when many blocks are incoming these exclusive locks contend and can stall both sync and the main tick.

Serving side (onReqRespRequest) is mostly fine:

  • blocks_by_root: DB-only, no forkchoice lock ✅
  • status: two brief lockShared() calls — OK but blocked if a writer holds the exclusive lock

Fix

  1. Response handling: onReqRespResponse should not call chain.onBlock() directly on the libp2p thread. Received blocks should be queued into a channel/ring buffer and consumed by the main event loop. This is exactly the architecture described in Follow-up: Replace state_mutex with xev.Async lock-free dispatch for Rust→Zig network callbacks #700 (xev.Async dispatch).

  2. Serving status: Cache an atomically-updated Status snapshot (updated in onBlockFollowup whenever head/finalized changes). The status req/resp handler then serves it lock-free.

  3. General: Any read from forkchoice for serving req/resp should use a snapshot — take the lock briefly, copy the needed fields, release, then do the work.

Related

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions