Fix RpcFrontend consensus and history publication - #8127
Open
Amaury Chamayou (achamayou) wants to merge 7 commits into
Open
Fix RpcFrontend consensus and history publication#8127Amaury Chamayou (achamayou) wants to merge 7 commits into
Amaury Chamayou (achamayou) wants to merge 7 commits into
Conversation
Copilot started reviewing on behalf of
Amaury Chamayou (achamayou)
August 7, 2026 16:03
View session
Contributor
There was a problem hiding this comment.
Pull request overview
Publishes consensus/history from NodeState to RPC frontends and synchronizes request-side access.
Changes:
- Adds atomic frontend pointer publication and null-safe redirects.
- Adds concurrency and unpublished-consensus tests.
- Removes the obsolete TSAN suppression.
Custom instructions used:
.github/copilot-instructions.md.github/instructions/reviewing.instructions.md
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
tsan_env_suppressions |
Removes the frontend race suppression. |
src/node/rpc/test/frontend_test.cpp |
Adds publication and redirect regression tests. |
src/node/rpc/frontend.h |
Implements atomic publication and request-local pointer loads. |
src/node/node_state.h |
Publishes consensus and history to frontends. |
src/enclave/rpc_handler.h |
Adds the publication interface. |
Copilot started reviewing on behalf of
Amaury Chamayou (achamayou)
August 7, 2026 20:16
View session
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (1)
include/ccf/endpoint_registry.h:177
EndpointRegistryis a public application API, and changing its protectedconsensus/historymembers to atomics changes how downstream subclasses can use them (the logging sample in this PR already has to switch toget_consensus()). Repository policy requires user-facing API changes to be recorded inCHANGELOG.md; please add a migration note directing subclasses to the new accessors.
std::atomic<ccf::kv::Consensus*> consensus{nullptr};
std::atomic<ccf::kv::TxHistory*> history{nullptr};
Amaury Chamayou (achamayou)
marked this pull request as ready for review
August 10, 2026 10:18
14 tasks
Eddy Ashton (eddyashton)
approved these changes
Aug 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
NodeStateto each RPC frontendRpcFrontendandEndpointRegistryEndpointRegistryconsensus reader through/txduring concurrent publicationRpcFrontendTSAN suppressionIssue #8123 coverage
This PR resolves Still Applicable item 2,
RpcFrontendraces onconsensusandhistory, and the corresponding Phase 3 frontend publication work from #8123:NodeStateexplicitly publishes the initialized consensus and history pointers to every RPC frontend.RpcFrontendandEndpointRegistryuse synchronized atomic pointer publication instead of racing raw-pointer reads and writes.nullptr) consensus without dereferencing it, addressing the related draft-only hazard called out in the issue.node/rpc/frontend.hTSAN race suppression is removed.Pointer flow before and after
Before
NodeStateinstalled owning pointers in the KV store, but did not publish them directly to the frontends. Request and tick paths repeatedly fetched raw pointers from the store and wrote them into frontend and registry state while other threads could read the active fields.flowchart TD NS["NodeState setup_history / setup_consensus"] -->|"Store::set_history / set_consensus<br/>shared_ptr ownership"| Store["KV Store"] Request["RpcFrontend::process / process_forwarded"] --> UpdateConsensus["RpcFrontend::update_consensus"] Tick["RpcFrontend::tick"] --> UpdateConsensus Attempt["Each request attempt"] --> UpdateConsensus Attempt --> UpdateHistory["RpcFrontend::update_history"] Store -->|"get_consensus().get()"| UpdateConsensus Store -->|"get_history().get()"| UpdateHistory UpdateConsensus -->|"unsynchronised raw write"| RFConsensus[("RpcFrontend::consensus")] UpdateHistory -->|"unsynchronised raw write"| RFHistory[("RpcFrontend::history")] UpdateConsensus -->|"EndpointRegistry::set_consensus"| ERConsensus[("EndpointRegistry::consensus")] UpdateHistory -->|"EndpointRegistry::set_history"| ERHistory[("EndpointRegistry::history")] RFConsensus -->|"direct raw reads"| RFLogic["forwarding, redirects,<br/>session checks, backpressure"] RFHistory -->|"direct raw reads"| SignatureLogic["proposal roots and<br/>signature emission"] ERConsensus -->|"direct raw reads"| Endpoints["Base, Common, Node,<br/>JS and app endpoint handlers"] OtherThreads["Concurrent request / tick threads"] -. "could read while another thread wrote" .-> RFConsensus OtherThreads -. "could read while another thread wrote" .-> RFHistory OtherThreads -. "could read while another thread wrote" .-> ERConsensusAfter
NodeStatepublishes the initialized pointers once to every frontend. The setter release-stores them into both frontend and registry atomics before store readiness is published. Request attempts and endpoint operations acquire-load their own stable local snapshot; request and tick paths no longer poll the KV store or rewrite pointer state.flowchart TD SetupHistory["NodeState::setup_history"] -->|"Store::set_history<br/>shared_ptr ownership"| Store["KV Store"] SetupHistory --> History["NodeState history shared_ptr"] SetupConsensus["NodeState::setup_consensus"] -->|"Store::set_consensus<br/>shared_ptr ownership"| Store SetupConsensus -->|"RPCMap::frontends"| Publish["RpcFrontend::set_consensus_and_history"] History -->|"history.get()"| Publish Publish -->|"release store once"| RFConsensus[("atomic RpcFrontend::consensus")] Publish -->|"release store once"| RFHistory[("atomic RpcFrontend::history")] Publish -->|"EndpointRegistry::set_consensus<br/>release store once"| ERConsensus[("atomic EndpointRegistry::consensus")] Publish -->|"EndpointRegistry::set_history<br/>release store once"| ERHistory[("atomic EndpointRegistry::history")] Publish -->|"happens before"| Ready["Store::set_readiness Ready"] Request["RpcFrontend request attempt"] -->|"Store::is_ready acquire"| ReadyCheck["readiness observed"] ReadyCheck --> Snapshot["acquire-load consensus and history once"] RFConsensus --> Snapshot RFHistory --> Snapshot Snapshot --> RFLogic["forwarding, redirects, session checks,<br/>backpressure, proposal roots, signatures"] Handler["Endpoint operation"] --> Getter["EndpointRegistry::get_consensus<br/>acquire-load once"] ERConsensus --> Getter Getter --> Endpoints["Base, Common, Node,<br/>JS and app endpoint handlers"] Tick["RpcFrontend::tick"] --> NoRefresh["endpoint tick only;<br/>no pointer refresh"]Related items already resolved elsewhere
#8123 was written against
mainbefore these focused fixes merged:LedgerSecretslock ordering (item 1) was fixed by Fix LedgerSecrets lock ordering #8125, which moved all three KV dependency reads beforeLedgerSecrets::lockand added a focused TSAN test that runs without the repository-wide deadlock suppressions.sinceparameter to use closed/inclusive semantics #7775.This PR does not resolve the remaining work in #8123:
NodeClientcertificate ownership (item 3)transition_service_to_open()(item 5)backup_snapshot_fetch_task(item 6)Testing
ninja -C buildcd build && ./tests.sh --output-on-failure -R '^(frontend_test|node_frontend_test)$'cd build-tsan && TSAN_OPTIONS="suppressions=$PWD/../tsan_env_suppressions:halt_on_error=1" ./tests.sh --output-on-failure -R '^frontend_test$'scripts/cpp-format-checks.shscripts/includes-checks.shscripts/copyright-checks.shscripts/ascii-checks.shPartially addresses #8123