-
Notifications
You must be signed in to change notification settings - Fork 273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix hang that occurs when streaming calls are ongoing and a hot-restart occurs in Flutter web #718
Fix hang that occurs when streaming calls are ongoing and a hot-restart occurs in Flutter web #718
Conversation
|
e288236
to
bf28648
Compare
Thanks for the PR! The enum switch not being exhaustive is really worrying behavior in my opinion. I also don't see where the |
@galenwarren, could you possibly provide a reproducible example or add a test? Just wanting to make sure the exhaustiveness is the issue. |
@nshahan @biggs0125 can you shed some light on how hot restart is wired up in Flutter Web through DDC (or dart2js if supported)? It seems extremely problematic if hot restart keeps old code running but changes identity of constants which breaks things like switches. @galenwarren This seems like a serious bug in the hot restart mechanism. Trying to work around it in individual packages is like trying to carry water in a sieve. |
@mosuem @mraleph This behavior only happens during hot restarts, so I'm not sure how to add a test. But this is absolutely what's happening -- I can catch it in the debugger when I do a hot restart with an active streaming call and observe it not hitting any of the case statements, just looping forever and blocking the hot restart, forcing a (time-consuming!) stop and restart. With a default case added, it exits and there is no hang. I've encountered this problem with hanging during hot restarts for months -- several times per day -- but couldn't figure out what was going on. Since I've made this change locally, I haven't had a single issue! Already saved me a ton of time. :) I agree that this is not the root problem. The root problem seems to be that there is no reliable way to detect a pending isolate shutdown in order to run cleanup code. If that existed, we could hook that and abort the in-progress calls. Some discussion of this here for the FFI case. More discussion here. There was even a PR to add a hook (which I can't find now, but will look for more if you want) that was postponed. However, it's a tremendous time-saver, I can't imagine I've been the only person affected by this. Outside of the multiple-isolate case that occurs during hot restart, I don't think the default case would ever be hit so it should be harmless. EDIT: Also, I'm working exclusively in Flutter web, so maybe this issue and/or the weirdness with enum values across isolates only occurs there, I can't say for sure. EDIT #2: I could probably figure out how to repro it and do a screen capture of the debugger both with and without the default case added, if that would be helpful to establish that this is what is happening? Just let me know. |
@galenwarren note that there are no isolates on the Web, so discussions around isolates shutdown on hot-restart don't apply here. |
@mraleph Yeah, I'm sure you're more familiar with Dart internals than I am. I was speculating it was related to isolates because of the similar discussions for FFI. Nonetheless, at a high level, this is what is happening on Flutter web. There must be some other mechanism that is causing the enum values not to be recognized in the switch statement, causing an infinite loop. Would you like me to do a screen capture so you can see the debugger behavior? |
Hot restart is only supported in DDC. The model we aim to support is to:
In practice this means that values from one run of the app should not exist in later restarts of the application and it should be considered a bug if they do. It sounds like this might be an interaction between old code from a callback that is still running and the global constants table that contains the new versions of the enum values. Historically callbacks have been a source of problems like this and we have added some cleanup for callbacks from previous versions of the application (dom listeners, timers, etc). @galenwarren
@ditman |
@nshahan Regarding:
In this app, there are long-running grpc server-streaming calls, i.e. one request and then a stream of responses over long-ish periods of time. These calls would be active during hot restarts and could be the source of the callbacks. Here's the stacktrace, it's taken from a breakpoint at the
_StackTrace (http://localhost:8080/dart-sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart 843:28 get current
|
cc @sigmundch |
@nshahan This is how open-source Flutter web handles restart signals: These are some of the different points where the engine adds listeners: AFAIK this is not exposed to end users (flutter apps/plugins); if an end-user attempts to hook to that |
I wonder if any of those points is observable from the app, and could be used to trigger cleanup in the app and stop any open grpc calls? For example, I noticed one of the |
@sigmundch If we want to expose this to apps, I think we should design the way to do it "properly" (I imagine a If we want this to be a web-only API, it could be tucked in the |
Completely agree 😄 - I just suddenly got curious if it was possible as a way to test things out |
Ahh, gotcha, I thought we needed this as a permanent feature (got excited for a second :P) You can potentially listen to the browser History with the |
cc @biggs0125 |
How should we proceed on this PR? |
I am inclined to just close this. It has to be treated as a bug in DDC - we can't expect every package to manually work around this. |
Yep, I don't disagree that, fundamentally, the issue is something else and
that this PR is more of a workaround. It's just a quite effective
workaround for anyone using this library with Flutter web and streaming
grpc calls, because it does fix hot restarting.
Is a more proper solution likely here? I'm not clear what that looks like,
yet.
…On Mon, Jul 1, 2024, 3:51 AM Slava Egorov ***@***.***> wrote:
I am inclined to just close this. It has to be treated as a bug in DDC -
we can't expect every package to manually work around this.
—
Reply to this email directly, view it on GitHub
<#718 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAF7RBPU5URDYHIIZVN2S5LZKEC75AVCNFSM6AAAAABJYSQ6TOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCOJZGQ3TIOJXG4>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
I agree that a small change like this does not really hurt, especially if well documented, even if it is not a solution to the bug in general. |
PR HealthBreaking changes ✔️Details
Changelog Entry ✔️Details
Changes to files need to be accounted for in their respective changelogs. Coverage ✔️Details
This check for test coverage is informational (issues shown here will not fail the PR). API leaks
|
Package | Leaked API symbols |
---|---|
grpc | $1.Duration ServerHandler Any |
This check can be disabled by tagging the PR with skip-leaking-check
.
Package publish validation ✔️
Details
Package | Version | Status |
---|---|---|
package:grpc | 4.0.0 | ready to publish |
Documentation at https://github.com/dart-lang/ecosystem/wiki/Publishing-automation.
@mosuem I see you kicked off some checks and that one is failing (looks like it wants a changelog entry). Would you like me to look into that? |
Generally agree, and below I link to issues tracking this in our SDK. There are two issues at play:
|
bf28648
to
c297c53
Compare
@mosuem I added an entry to the changelog file if you want to try rerunning the failing check. |
Just checking in on this, anything you need me to do? I see this branch is out of date, I can rebase if people would like. Thanks. |
c297c53
to
1a99c03
Compare
@mosuem Thanks! |
Thank you! |
I know this is closed, but I could not sleep without understanding why that switch was skipping the value, so I added a log and.... I was not expecting this 100%. I mean, the switch is completely ignoring a legit value? Speaking with @AngeloAvv he said something that triggered my interest, "what if the issue is the private enum?" So I tried by changing Basically what was happening is that for some reason Changing to |
Thanks for investigating further - as noted in #718 (comment), the underlying issue needs to be fixed in the framework. |
commit 471a8b3 Author: Tsavo Knott <tsavo@pieces.app> Date: Fri Jan 3 13:02:16 2025 -0500 Clean Up CI commit 1632820 Author: Tsavo Knott <tsavo@pieces.app> Date: Fri Jan 3 13:00:20 2025 -0500 True Upstream Master commit 1dddfe0 Merge: 774bf81 c9d6286 Author: Tsavo Knott <102485237+tsavo-at-pieces@users.noreply.github.com> Date: Fri Jan 3 12:32:24 2025 -0500 Merge pull request #2 from open-runtime/update-from-upstream Update from Upstream Master commit c9d6286 Merge: 774bf81 9a9c017 Author: Tsavo Knott <tsavo@pieces.app> Date: Fri Jan 3 12:31:33 2025 -0500 Merge branch 'upstream-master' into update-from-upstream commit 774bf81 Merge: 4a043fa 00eabed Author: Tsavo Knott <102485237+tsavo-at-pieces@users.noreply.github.com> Date: Fri Jan 3 12:13:44 2025 -0500 Merge pull request #1 from open-runtime/aot_monorepo_compat Merging AOT Monorepo Compat into our Main Branch commit 9a9c017 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue Dec 17 09:58:22 2024 +0100 Bump vm_service from 14.3.1 to 15.0.0 (grpc#751) Bumps [vm_service](https://github.com/dart-lang/sdk/tree/main/pkg) from 14.3.1 to 15.0.0. - [Changelog](https://github.com/dart-lang/sdk/blob/main/CHANGELOG.md) - [Commits](https://github.com/dart-lang/sdk/commits/HEAD/pkg) --- updated-dependencies: - dependency-name: vm_service dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Moritz <mosum@google.com> commit 3e94fec Author: Moritz <mosum@google.com> Date: Tue Dec 17 09:53:02 2024 +0100 Update health.yaml (grpc#753) * Update health.yaml * Upgrade example * Fixes * try different syntax * without endings * test new wf * new version * Works, use main now * Add changelog commit 6676c20 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon Dec 16 13:37:35 2024 +0000 Bump dart-lang/setup-dart from 1.6.5 to 1.7.0 (grpc#746) commit f61b9a3 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon Nov 4 10:45:05 2024 +0000 Bump actions/checkout from 4.2.0 to 4.2.2 (grpc#744) commit c063010 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue Oct 1 08:51:16 2024 +0000 Bump actions/checkout from 4.1.7 to 4.2.0 (grpc#741) commit 04ba68e Author: Moritz <mosum@google.com> Date: Tue Oct 1 04:46:38 2024 -0400 Rev package:lints (grpc#740) * Rev package:lints * Add changelog * Run CI on 3.5.0 * Test with 3.2.0 * Update .github/workflows/dart.yml Co-authored-by: Kevin Moore <kevmoo@users.noreply.github.com> * Update .github/workflows/dart.yml Co-authored-by: Kevin Moore <kevmoo@users.noreply.github.com> --------- Co-authored-by: Kevin Moore <kevmoo@users.noreply.github.com> commit f8bbdce Author: Kevin Moore <kevmoo@users.noreply.github.com> Date: Tue Sep 24 12:07:42 2024 -0700 ignore unreachable_switch_default in weird switch case (grpc#737) commit 071ebc5 Author: steffenhaak <haak@aucentiq.com> Date: Fri Sep 6 17:13:11 2024 +0200 fix: keep alive timeout finishes transport instead of connection shutdown (grpc#722) * fix: keep alive timeout finishes transport instead of shutting down channel * Update keepalive_test.dart * Update CHANGELOG.md --------- Co-authored-by: Moritz <mosum@google.com> commit 8177633 Author: Moritz <mosum@google.com> Date: Fri Sep 6 15:09:54 2024 +0200 Small fixes (grpc#732) * Small fixes * Revert changes on file * Add changelog * Small fixes in keepalive test * Add delay * Fix symbol visibilty * Add try catch for debugging * Fail * fail * Use for loop commit 38ca626 Author: Lasse R.H. Nielsen <lrn@google.com> Date: Mon Sep 2 16:58:43 2024 +0200 Use `Map.of` instead of `Map.from` in grpc client. (grpc#724) * Use `Map.of` instead of `Map.from` in grpc client. `Map.of` creates a new map with the same keys, values and *type* as the original map, when used without type arguments or context type, where `Map.from` creates a `Map<dynamic, dynamic>`. (This code failed on an attempt to make `Map.unmodifiable` be more strictly typed, like `Map.of` instead of `Map.from`, showing that an intermediate map had type `Map<dynamic, dynamic>` unnecessarily). Same for using `List.of` instead of `List.from`. The new code should be (microscopically) more efficient and type safe, and is forwards-compatible with a stronger type on `Map.unmodifiable`. (The code can be optimized more. For example `List.of(list1)..addAll(list2)` can be just `list1 + list2` or `[...list1, ...list2]`, both of which may know the total number of elements when doing the initial list allocation. This is a minimal change to allow the type changes for `.unmodifiable` to get past this very initial blocker in internal tests.) * Add changelog and minor version increment. And my save removes trailing spaces. commit 4f6fe9b Author: c-lucera-pvotal <91328643+c-lucera-pvotal@users.noreply.github.com> Date: Wed Aug 28 08:18:15 2024 +0200 fix: fix headers not completing when call is terminated (grpc#728) Fixes grpc#727 commit c18e185 Author: Kevin Moore <kevmoo@users.noreply.github.com> Date: Wed Jul 24 14:24:57 2024 -0700 Fix status badge (grpc#726) commit b999b64 Author: Galen Warren <galen@cvillewarrens.com> Date: Wed Jul 17 08:11:29 2024 -0400 feat: fix hang that occurs when hot restarting (grpc#718) commit bf8bbde Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon Jul 1 11:56:47 2024 +0000 Bump dart-lang/setup-dart from 1.6.4 to 1.6.5 (grpc#720) commit 4aa4c8c Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon Jul 1 11:52:08 2024 +0000 Bump actions/checkout from 4.1.6 to 4.1.7 (grpc#719) commit dee1b2b Author: Kevin Moore <kevmoo@users.noreply.github.com> Date: Wed May 29 17:23:53 2024 -0700 Update pubspec.yaml commit 52023d4 Author: Kevin Moore <kevmoo@google.com> Date: Tue May 28 14:47:30 2024 -0700 code fixes commit ebb7368 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed May 22 06:56:00 2024 +0000 Bump lints from 3.0.0 to 4.0.0 Bumps [lints](https://github.com/dart-lang/lints) from 3.0.0 to 4.0.0. - [Release notes](https://github.com/dart-lang/lints/releases) - [Changelog](https://github.com/dart-lang/lints/blob/main/CHANGELOG.md) - [Commits](dart-lang/lints@v3.0.0...v4.0.0) --- updated-dependencies: - dependency-name: lints dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> commit 4e65d4b Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue May 21 11:05:38 2024 +0000 --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> commit 1495453 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue May 21 11:01:10 2024 +0000 Bump dart-lang/setup-dart from 1.6.2 to 1.6.4 Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.6.2 to 1.6.4. - [Release notes](https://github.com/dart-lang/setup-dart/releases) - [Changelog](https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md) - [Commits](dart-lang/setup-dart@fedb126...f0ead98) --- updated-dependencies: - dependency-name: dart-lang/setup-dart dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> commit 6586b74 Author: Sarah Zakarias <zarah@google.com> Date: Tue May 21 12:30:20 2024 +0200 Add `topics` to `pubspec.yaml` (grpc#712) commit 9f65399 Author: Moritz <mosum@google.com> Date: Fri May 17 14:53:33 2024 +0200 Move `codec.dart` to former place (grpc#713) commit 0d02e43 Author: Moritz <mosum@google.com> Date: Mon May 6 06:25:06 2024 -0700 Remove dependency on `package:archive` (grpc#707) * Remove dependency on package:archive * Test compression on vm only * Add licenses * Fix analyze issues * Fix codec web * Fix licenses * Add changelog commit 078fd23 Author: Moritz <mosum@google.com> Date: Thu Apr 25 04:45:40 2024 -0700 Remove generated `StatusCode` (grpc#703) * Remove generated `StatusCode` * Rev version for breaking change * Upgrade min sdk version * Fix issues commit bdbe5f5 Author: Ruben Garcia <RubenGarcia@users.noreply.github.com> Date: Mon Apr 22 16:09:18 2024 +0200 Fix issue 669 (grpc#693) * Fix issue 669 * Update CHANGELOG.md * Update CHANGELOG.md * Fix dart format issue. Fix prefer single quote issue. * Update pubspec and changelog to avoid merge check publish / validate validate packages * Add test for GRPC Compression Flag * Fix dart analyze issues. * Fix latest dart analyze issue (uninizialized variable) commit bb8b6e5 Author: Moritz <mosum@google.com> Date: Fri Apr 19 02:05:59 2024 -0700 Make protobuf generated imports absolute (grpc#696) * Make protobuf generated imports absolute * Stop test for now commit b05fafe Author: Moritz <mosum@google.com> Date: Mon Apr 15 04:43:26 2024 -0700 Add Health workflow (grpc#699) * Add Health workflow * Remove license check commit aece2a4 Author: Abdul Momin <98901875+Curious-x@users.noreply.github.com> Date: Mon Apr 15 12:53:00 2024 +0500 Typo Correction in README.md (grpc#695) Corrected typo "RPs" to "RPCs". To avoid confusion.
When hot-restarting in Flutter Web, streaming GRPC calls can get stuck in this method of
_GrpcWebConversionSink
:The
while
loop runs, but theswitch
statement doesn't match any cases so it just keeps looping. This seems to have something to with some issues around how Dart handles enums across isolates (some discussion here and elsewhere) and the fact that hot-restarting involves the creation of a new isolate.In any case, adding a
default
handler causes the loop to break when a call is orphaned in this manner, which lets the isolate shut down and the hot-restart succeed.There is also some discussion in the Dart/Flutter community about creating hooks for all platforms that would allow one to reliably detect when an isolate were shutting down and take action. If that were in place, that would allow for a cleaner solution, but it doesn't seem to be available yet.