fix(channels): roll back timed-out account startups - #3591
Open
GautamSharma99 wants to merge 1 commit into
Open
Conversation
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
Fixes #3581.
startChannelAccountLive()previously raced registry startup against a timer that had no connection to the operation it timed out. When the timer rejected, persisted state was changed back to disabled, but the registry still owned a registered adapter and itsstart()promise could later resolve into a live integration.This PR makes timeout a real lifecycle cancellation boundary and orders all account start/stop operations with per-account generations. A startup that times out or is superseded is immediately unpublished, stopped best-effort, and stopped again if its original
start()later settles. Cleanup is identity-checked so an old attempt cannot remove or stop a newer retry adapter.What changed
Timeout now reaches the registry
startChannelAccountLive()now creates anAbortControllerfor the existing 10-second deadline and passes its signal toChannelRegistry.startChannelAccount().The registry races the signal at each asynchronous startup boundary:
The original timeout error is carried as the abort reason, preserving the existing actionable user-facing message.
Per-account lifecycle generations
Added a focused
ChannelAccountLifecycleowner for generation and retirement state. Every start receives a generation for its(channelId, accountId)key. The registry verifies that generation after each asynchronous step and before reporting startup success.These operations invalidate older generations:
stopChannelAccount();stopChannel();stopAll().This makes a concurrent stop deterministic even when it arrives before adapter creation or while
adapter.start()is pending.Identity-safe stale cleanup
When startup fails, times out, or becomes stale, cleanup:
stop()immediately as a best-effort cancellation;stop()again after late resolution/rejection;The second stop is required for adapters whose first
stop()is a no-op while startup has not yet marked itself running. Crucially, cleanup retains the old adapter object and never looks up the key and stops whatever happens to be there later, so a successful retry is unaffected.Adapters are also removed from the registry before stop is awaited. Callers and snapshots therefore cannot observe a disabled account as registered/live while slow cleanup completes.
Persisted-state race protection
The service layer now tracks its own account lifecycle generation. A stale start failure only rolls persisted
enabledstate back when it is still the newest service operation.Without this guard, an older start superseded by a retry could reject later and overwrite the retry state back to disabled even though the new adapter succeeded. Stop and remove operations also advance this generation, and startup rechecks it around display-name refresh before returning success.
Existing failure cleanup
The same retirement path now unregisters adapters after ordinary startup failures, not only timeouts. Stop paths call
stop()for registered pending adapters even whenisRunning()is still false, allowing adapters with in-progress initialization to cancel early.Tests
Added real delayed Telegram adapter lifecycle tests covering:
start()resolving after timeout is stopped and cannot re-register;Validation completed:
src/channelssuite: 816 passed, 1 intentional live-network test skipped, 0 failed;bun run check: all 12 repository checks passed;registry.tsremains below the enforced source ceiling at 977 lines after extracting lifecycle ownership.Concurrency semantics
For one account key, the newest lifecycle operation wins. Cleanup from an older operation may only act on the adapter instance created by that operation. Operations for different account keys remain independent and are not globally serialized.
The adapter interface is unchanged. Existing adapters can continue implementing their own startup timeouts/cancellation, while the registry generation guard provides the cross-adapter safety boundary required by the service-level timeout.