fix(polling): drop stale in-flight check results on cancel/resubscribe - #148
fix(polling): drop stale in-flight check results on cancel/resubscribe#148mem-5514-tahara wants to merge 3 commits into
Conversation
If a listener cancels while a connectivity check is still running, that check's result belonged to the old subscription. Without a guard, it could still get emitted to a brand-new subscriber that started its own fresh check moments later, or restart the polling timer even though nobody is listening anymore. Track a _subscriptionVersion counter, bumped only when a listener cancels. _maybeEmitStatusUpdate snapshots it before the check starts and skips the emit (and the next timer reschedule) if it changed while the check was in-flight.
Removing _generation entirely (previous commit) turned out to be wrong: unlike _cancelGeneration, this guard protects backoff-specific mutable state (_currentBackoffDelay/_backoffNeedsReset), which only exists in this branch. Without it, a setIntervalAndResetTimer call (or a cancel) racing an in-flight failing check silently undoes the reset it just applied — the next failure incorrectly grows the delay instead of starting over at the fresh initial delay. Confirmed via a reproduction: removing the guard makes the added regression test fail (checkCount climbs from 3 to 5, and the delay grows to 100ms instead of staying at the reset 50ms). Renamed from _generation to _timerVersion with a plainer doc comment, per review feedback. _cancelGeneration (renamed _subscriptionVersion) remains split out as OutdatedGuy#148, since that one has no backoff dependency.
Move the hasListener check right after the await so isStale no longer needs to recheck it, and drop comments already covered by the _subscriptionVersion doc comment.
There was a problem hiding this comment.
Pull request overview
Adds subscription-version tracking to discard connectivity results from cancelled subscriptions.
Changes:
- Guards status emissions against stale in-flight checks.
- Adds cancellation and resubscription regression tests.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
lib/src/internet_connection.dart |
Adds stale-result detection during polling. |
test/internet_connection_test.dart |
Tests cancellation during in-flight checks. |
Suppressed comments (1)
lib/src/internet_connection.dart:304
- The version is advanced only after awaiting trigger-stream cleanup. If that subscription has asynchronous cancellation, a replacement listener can attach and the old connectivity check can finish while the version is still unchanged, allowing the stale result to be emitted to the replacement subscriber. Invalidate the generation synchronously before the first
await.
Future<void> _handleStatusChangeCancel() async {
await _triggerSubscription?.cancel();
_triggerSubscription = null;
_subscriptionVersion++;
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| final isStale = _subscriptionVersion != previousSubVersion; | ||
|
|
||
| if (!isStale && _lastStatus != currentStatus) { |
There was a problem hiding this comment.
Fixed — a version mismatch now returns immediately, skipping both emission and rescheduling.
A stale check still reached the unconditional Timer(...) at the end of _maybeEmitStatusUpdate, overwriting _timerHandle with an untracked timer while the fresh subscription's own timer kept running unreferenced. Return immediately on a version mismatch so stale work skips both emission and rescheduling. Also bump _subscriptionVersion synchronously before the first await in _handleStatusChangeCancel, so a resubscribe that races the in-flight trigger-subscription cancellation can't observe the stale version.
|
Hi @OutdatedGuy, just a gentle follow-up on this PR as well! The Copilot review feedback regarding early return on stale checks has been applied, and all tests/checks are green. Whenever you have time to take a look at both #148 and #139, please let me know. Thanks! |
Summary
Split out from #139 per review feedback, with
_cancelGenerationrenamed to_subscriptionVersion.If a listener cancels its subscription while a connectivity check is still running, that check's result belongs to the old subscription. Without a guard:
This adds a
_subscriptionVersioncounter, bumped only when a listener cancels._maybeEmitStatusUpdatesnapshots it before the check starts and skips both the emit and the next timer reschedule if it changed while the check was in-flight.Test plan
dart analyze— no issuesdart format --set-exit-if-changed— cleandart test— all tests pass, including two new regression tests: