fix(identity): do not hold the registry lock across WalletFactory.NewWallet - #2263
Draft
AkramBitar wants to merge 1 commit into
Draft
fix(identity): do not hold the registry lock across WalletFactory.NewWallet#2263AkramBitar wants to merge 1 commit into
AkramBitar wants to merge 1 commit into
Conversation
…Wallet Registry.WalletByID took WalletMu.Lock() and then called WalletFactory.NewWallet while still holding it, contradicting both the method's own inline comment and the concurrency invariant documented on the Registry type. The factory receives the registry itself as IdentitySupport, so a factory that calls back into the registry (e.g. RegisterWallet) deadlocks permanently, since sync.RWMutex is not reentrant. It also serialized every wallet creation for a role behind one write lock, including idemix pseudonym generation and storage access. Wallet construction now runs inside a singleflight group keyed by wallet id, with no lock held: concurrent callers for the same wallet still share a single NewWallet call, while distinct wallets are built in parallel. WalletMu is only taken for the short double-check read and the map write. Fixes #2064 Signed-off-by: AkramBitar <akram@il.ibm.com>
AkramBitar
force-pushed
the
fix/2064-registry-lock-scope
branch
from
August 18, 2026 21:45
b7f8be0 to
3469ce7
Compare
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.
Problem
Registry.WalletByID(token/services/identity/role/registry.go) built the wallet while holding the registry write lock, contradicting its own inline comment ("Create the wallet without holding the registry lock") and the invariant documented on theRegistrytype.Flow before this PR:
WalletByIDtakesr.WalletMu.Lock()(defer Unlock).Walletsmap — still missing.WalletFactory.NewWallet→ idemix pseudonym generation, identity-store reads, identity-store writes.Two consequences:
IdentitySupport, so it may legitimately call back into it (e.g.RegisterWallet, which takesWalletMu.Lock()).sync.RWMutexis not reentrant, so such a factory hangs forever at step 4.Fix
Flow after this PR:
singleflight.Groupkeyed by wallet id (golang.org/x/sync/singleflight, already a dependency and the existing house pattern intoken/services/utils/cache/ristretto.go).RLockfor the double-check, released immediately.WalletFactory.NewWalletis called with no lock held — a factory may safely re-enter the registry.WalletMu.Lock()only for the map write, then unlock.singleflightkeeps the "build once" guarantee that the lock used to provide: concurrent callers for the same wallet id share oneNewWalletcall and get the same instance, while distinct wallet ids are now built in parallel.Tests
Both new tests were verified to fail against the unfixed
registry.go:TestWalletByID_FactoryReentersRegistry— a factory that callsRegisterWalletfrom insideNewWalletmust return; on the old code it hangs (timeout-guarded, a deadlock has no natural termination).TestWalletByID_DistinctWalletsAreCreatedConcurrently— twoNewWalletcalls for different wallet ids must be in flight simultaneously; on the old code they are serialized.TestWalletByID_ConcurrentCreationadditionally asserts exactly one factory call for concurrent same-id requests.go test -race -count=5 ./token/services/identity/role/green;make checksandmake lint-auto-fixclean.Docs
New "Wallet Registry concurrency" section in
docs/services/identity.mdstating the contract:WalletMuguards the map only,NewWalletis never called under a lock, same-id creations are coalesced rather than serialized.Fixes #2064