wallet: serialize lifecycle transitions - #1328
Draft
yyforyongyu wants to merge 21 commits into
Draft
Conversation
yyforyongyu
commented
Aug 17, 2026
| // This is part of the Controller interface. | ||
| func (w *Wallet) Start(startCtx context.Context) error { | ||
| // 1. Attempt to transition from Stopped to Starting. | ||
| w.lifecycleMu.Lock() |
Collaborator
Author
There was a problem hiding this comment.
This design is ugly and hard to follow - instead, we should take a step back and think about how each component handles its own internal state and boundary. For the wallet, there is no need to support restart, instead, its lifecycle should be managed by a higher component like the manager.
yyforyongyu
force-pushed
the
task-wallet-lifecycle
branch
from
August 18, 2026 15:44
6a97ee0 to
4e99ef1
Compare
yyforyongyu
force-pushed
the
sql-wallet
branch
from
August 18, 2026 16:26
46f7f14 to
a6865a3
Compare
yyforyongyu
force-pushed
the
task-wallet-lifecycle
branch
from
August 18, 2026 17:31
4e99ef1 to
84c7cfc
Compare
Hold the Manager lock from durable Create or the Load cache decision through runtime assembly and cache installation. Keep only fully assembled Wallet pointers in the cache.
Prove concurrent cold Loads install one exact Wallet pointer. Require Create to exclude Load from durable-write completion through runtime cache installation.
Prove failures release Manager admission for retry. Require different wallet names to assemble sequentially through the same Manager-wide lock.
Create durable state with a setup Manager, then contend cold Loads through a fresh Manager. Require every backend to return one exact runtime pointer.
Distinguish a newly created runtime from a terminally stopped one. Allow shutdown admission from every non-terminal lifecycle state so the Manager coordinator can settle each Wallet incarnation exactly once.
Attach terminal completion and lazy coordinator admission to each exact Manager-published runtime.
Provide coordinator-only helpers that publish all runtime workers before returning and join them before terminal Vault locking.
Settle stopped, canceled, and failed runtime startups through one caller-independent terminal path.
Arbitrate accepted Start cancellation, Stop, and setup completion at one coordinator-owned publication point.
Resolve lifecycle requests against the exact current Wallet pointer. Join the recorded terminal result with caller-bounded waits.
Bind StartWallet and StopWallet to the exact Manager-published Wallet pointer and admit one lazy coordinator per runtime. Make Stop teardown caller-independent while preserving caller context as only a bound on each wait.
Join terminal shutdown before deleting the exact stopped cache entry and reuse the per-name publication gate for one fresh Wallet pointer.
Cover pre-accept cancellation, terminal Stop results, exact pointer validation, and failed startup teardown through the Manager API.
Use setup, worker, and Vault barriers to prove Stop and cancellation publish one Start result and every Stop joins the same teardown.
Verify that an admitted Stop or canceled Start caller prevents worker publication at the final Manager-serialized lifecycle cutoff.
Race concurrent Load calls against blocked terminal teardown. Prove the publication gate returns one fresh runtime without reviving the old pointer.
Register each integration runtime with its owning Manager so startup and teardown use StartWallet and StopWallet.
Drive maintained integration lifecycle calls through the exact owning Manager. Cover terminal pointers and same-Manager replacement through Load.
Keep compatibility workers private to Loader, including creation, unload, RPC shutdown, and chain-client reconnect handoff.
Remove public lifecycle entry points from Wallet and Controller. Route maintained modern callers through the exact owning Manager. Keep startup setup and worker operations private to the lifecycle coordinator.
Run worker joining and terminal Vault locking in one helper goroutine. Report completion for the exact runtime.
yyforyongyu
force-pushed
the
task-wallet-lifecycle
branch
from
August 20, 2026 07:44
84c7cfc to
b368ec4
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.
Change Description
Implements roadmap Task 368
Depends on #1336.
Make
Managerthe sole public owner of eachWalletruntime lifecycle. Startupis serialized with shutdown, concurrent callers share one result, and teardown
continues independently of the initiating caller. Once stopped, a runtime stays
terminal and a later
Manager.Loadpublishes a fresh instance.The legacy Loader-owned chain reconnect remains internal to
Loader, so thischange does not restore public
WalletorControllerlifecycle methods.Deterministic tests cover lifecycle state transitions, startup arbitration,
terminal reload, concurrent stop, and legacy chain reconnect.
Steps to Test
make unit-race pkg=wallet case=TestManagerWalletLifecyclego test -race ./wallet -run '^(TestLoaderHandleChainClientDisconnect|TestManagerWalletLifecycle)$' -count=10make unit pkg=walletmake itest chain=btcd db=kvdb icase=controllermake itest chain=btcd db=sqlite icase=controllermake itest chain=btcd db=postgres icase=controllergo test ./...go vet ./...make lint-checkPull Request Checklist
Testing
Code Style and Documentation
📝 Please see our Contribution Guidelines for further guidance.