Request pool may not be pruned after a reconfig when the application installs the new config asynchronously
Maintainers usually reply within 1 day
A pull request for this has already been merged.
- #729 by @HagarMeir — merged
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 58/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- go
- Domain
- distributed-systems
Research direction
Read pkg/consensus/consensus.go around Consensus.reconfig, internal/bft/controller.go around Controller.Start and MaybePruneRevokedRequests, then trace startComponents to see how the new controller is started. Add a test app hook that switches the verification sequence after the reconfig prune, and check that the next decision removes a request invalid under the new sequence.
Written by the indexing model from the issue text.
Description
Follow-up to #698 / #699.
Summary
#699 prunes the request pool once inside Consensus.reconfig, before the components restart. That relies on the application's Verifier already reflecting the new config when the prune runs. If the application installs the new config asynchronously, the prune can run against the old config. The new controller then cannot catch up later, because Controller.Start records the verification sequence it reads at start, which may already be the new one.
Details
Consensus.reconfig prunes, then starts the components:
https://github.com/hyperledger/SmartBFT/blob/da420f46d186b3cef2562b0171e39055f930ddba/pkg/consensus/consensus.go#L232-L250
Controller.Start stores the sequence the Verifier reports at that moment:
https://github.com/hyperledger/SmartBFT/blob/da420f46d186b3cef2562b0171e39055f930ddba/internal/bft/controller.go#L820
MaybePruneRevokedRequests prunes only if that stored value differs from the current one:
https://github.com/hyperledger/SmartBFT/blob/da420f46d186b3cef2562b0171e39055f930ddba/internal/bft/controller.go#L757-L770
Suppose the application switches its Verifier from sequence s to s+1 on its own goroutine, after Deliver or Synchronizer.Sync has returned the reconfig. Depending on when the switch lands:
| Switch lands | Prune in reconfig uses |
Start stores |
Result |
|---|---|---|---|
| before the prune | s+1 |
s+1 |
correct |
after Controller.Start |
s |
s |
the next MaybePruneRevokedRequests sees s → s+1 and prunes; correct |
during the prune, or between the prune and Controller.Start |
s (all or part of the pool) |
s+1 |
never pruned again |
In the third case, requests that are invalid under s+1 stay in the pool. They go away only when proposed and rejected, which costs a view change if this node leads, or after RequestAutoRemoveTimeout. A view change restarts those timers.
This happens in Fabric-X Orderer on the sync path: the synchronizer's commit callback starts the config switch on a separate goroutine and returns before it completes.
Proposed fix
Make the stored sequence equal the one the prune ran against, so a switch that lands later is always detected:
- In
Consensus.reconfig, readseq := c.Verifier.VerificationSequence()beforec.Pool.Prune(...). - Have the new controller start with that value instead of reading the
Verifieragain inStart. For example, pass it throughstartComponentstoController.Start.
If the switch lands during or after the prune, the controller then holds s, and the next MaybePruneRevokedRequests (on decide, sync or view change) prunes against s+1. If it landed before, seq is already s+1 and nothing changes. The initial Start (not after a reconfig) can keep its current behaviour.
Test
In the test app, apply the new verification sequence after Deliver / Sync returns the reconfig, and after reconfig's prune, for example with a hook in the test Verifier. Check that a request that is invalid under the new sequence is removed from the pool by the next decision.
🤖 Generated with Claude Code
- Dominant language
- Go
- Stars
- 110
- Forks
- 41
- Avg merge
- 1d 4h
- Merged PRs (30d)
- 31
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from hyperledger/SmartBFT
-
Set up a pr projectOpen
Difficulty 4/5 3-5 days Newbie friendliness 25/100
hyperledger/SmartBFT#666 · 4 comments ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
hyperledger/SmartBFT#628 · 12 comments ·
Maintainers usually reply within 1 day
-
Recording library runtime for bug analysis by developersMay be free again @HagarMeir claimed this 1737 days ago, and no pull request is open. Openenhancement
hyperledger/SmartBFT#487 ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 15/100
hyperledger/SmartBFT#439 ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 35/100
hyperledger/SmartBFT#420 ·
Maintainers usually reply within 1 day
All issues in hyperledger/SmartBFT
Similar issues
-
compress: block buffers get re-zeroed on every call, and the zeroing is immediately overwrittenOpen
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
ClickHouse/ch-go#1218 ·
Maintainers usually reply within 13 days
-
automation models
Difficulty 1/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
podman-container-tools/podman#30031 ·
Maintainers usually reply within 1 day