Skip to content
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

buildId support for Flutter Web #53027

Open
marandaneto opened this issue Jul 24, 2023 · 5 comments
Open

buildId support for Flutter Web #53027

marandaneto opened this issue Jul 24, 2023 · 5 comments
Labels
area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. P2 A bug or feature request we're likely to work on web-dart2js

Comments

@marandaneto
Copy link

marandaneto commented Jul 24, 2023

Hi, thanks for all the hard work.

Relates to #51941

If you're compiling a Flutter Web app, the runtime types (<obj>.runtimeType.toString()) are minified, See caveat.

Flutter Web apps spit out a source maps file if combined with --source-maps, e.g. flutter build web --source-maps

The source maps file contains extension functions that allow the unmangling of such types, https://github.com/dart-lang/sdk/blob/master/pkg/compiler/doc/sourcemap_extensions.md#minified-names-data

For tools such as Sentry.io that have to do the unmangling of such types for debugging reasons, the compiled version of the Flutter web has to be associated with the generated source maps file.

For AOT Apps, you can do this via #51941 using NativeRuntime.buildId at runtime and reading the debug file binary metadata that matches the buildId, so the association can be made thru this buildId of the compiled app and its debug symbols.

For Flutter Web apps, this is not possible, there's no way to read a buildId that is available at runtime and after build time to make the association of the Flutter web version and the generated file.

The goal of this issue would be a similar API to NativeRuntime.buildId for Flutter web, and ideally this buildId would be possible to be read after the App is compiled, or the compiler would inject the buildId into the source maps file, read more about that here https://github.com/getsentry/rfcs/blob/main/text/0081-sourcemap-debugid.md#injecting-the-debugid-into-the-sourcemap

After the error is received, the ingestion pipeline will use this buildId to match the correct source map file.

Let me know your thoughts, thanks.

@mkustermann mkustermann added the area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. label Jul 24, 2023
@mkustermann
Copy link
Member

/cc @sigmundch

@sigmundch
Copy link
Member

cc @rakudrama - I'm curious if you have any reservations here about adding a build-id and what it would entail in our codegen process.

@marandaneto thanks for filing this issue. I'm not so sure about whether the approach taken with AOT would be best for the web. I'd like to brainstorm a bit more with you to see what options are best suited here.

I was reading a bit more about your approach in https://github.com/getsentry/rfcs/blob/main/text/0081-sourcemap-debugid.md, and was wondering if you could share a bit more about your experience using paths to encode the build-id information outside the program. I saw you've tried paths of the form release/dist/path, but it wasn't clear to me if this included any versioning information.

I ask because of our past experience with other customers on this topic. They have approached this as part of their versioning solution, which was necessary for them to address other problems too. In particular, their versioning takes care of:

  • rolling out new versions of the application gradually to a segment of their users
  • testing out experimental features on a small segment of their users
  • ensuring their app integrity. That is, many of our customer apps use deferred loading in Dart and load resources incrementally. This means their apps are very often in a partially loaded state and will potentially request additional code/data while the app is running. It is also often that this partial state happens when a new version of the app is rolled out. The versioning solution ensures the existing apps continue to work as they did before the new release. That means, those existing running apps in a partial state will continue to load their own resources and additional code, and are prevented from loading resources and code that belong to the new release.
  • unmangling symbols and stack traces in production offline (similar to your request here)

Their versioning solution is entirely handled in the serving infrastructure. They release their code and assets under URLs of the form http://<domain>/<app-id>/<build-id>/<resource-path> like:

http://some.host.domain/my/app/name/release_###-###-###/path/within/my/app/main.dart.js

The release_###-###-### is the equivalent of a build-id. It is typically a globally unique name picked at the time the build is created. Then, they use a level of indirection to direct requests, so that when someone attempts to use http://app.domain/index.html, they internally point them at the current release of their app.

As you can imagine, that architecture allows pushing out a new version of an app without interfering with existing ones. This benefits the unmangling work as well. Obfuscated error stack frames contain the resource URIs, so they embed the build-id in that way. Their storage of source-map files mimics the same structure, so they can lookup the source-map using the same build-id. Again, because of the structured organization of their storage, the source-map file doesn't need to store the build-id within it.

Separately, though, they do have a mapping from build-id to a commit in the history of their repository. This allows them to fetch the correct sources without having to embed the .js sources inlined in the source-map file directly.

@marandaneto
Copy link
Author

@sigmundch thanks for your message.
Here @mitsuhiko writes a bit more about the need of Debug Ids for source maps.
I'll come back to you later this week with more clarification.

@marandaneto
Copy link
Author

I saw you've tried paths of the form release/dist/path, but it wasn't clear to me if this included any versioning information.

The release actually is the versioning info, gotten from pubspec.yaml, it follows the pattern name@version+buildNumber.
The dist is the buildNumber.
There's more info about that here.

The path depends on how the source maps were uploaded, but most of the time is as simple as ~/lib/main.dart, ~/main.dart.js, ~/main.dart.js.map but one could configure that as well using e.g. --url-prefix param.

ensuring their app integrity. That is, many of our customer apps use deferred loading in Dart and load resources incrementally. This means their apps are very often in a partially loaded state and will potentially request additional code/data while the app is running. It is also often that this partial state happens when a new version of the app is rolled out. The versioning solution ensures the existing apps continue to work as they did before the new release. That means, those existing running apps in a partial state will continue to load their own resources and additional code, and are prevented from loading resources and code that belong to the new release.

By that you mean, for example, https://docs.sentry.io/A and https://docs.sentry.io/B, look at A and B, they were rolled out incrementally, and A is v1 still, and B is v2 so the debugId of both of them should be different for correct unmangling? in this case, my suggested approach would likely not work well in this cases and would be a mismatch as well.

I'm not sure if we've ever considered this use case, maybe @Swatinem can help us out here since he was part of the new spec.

@sigmundch
Copy link
Member

There's more info about that here.

It appears as if this is just a recommendation, but it is not enforced by the system. I think that's an important detail - I'd claim that versioning should be required for correctness and for correct error attribution.

By that you mean, for example, https://docs.sentry.io/A and https://docs.sentry.io/B, look at A and B, they were rolled out incrementally, and A is v1 still, and B is v2 so the debugId of both of them should be different for correct unmangling? in this case, my suggested approach would likely not work well in this cases and would be a mismatch as well.

Not sure I understood your example. Also, I'm not sure if those URIs are just examples or if they meant to have a running app on the other side (they currently show a PageNotFound error on my end).

To illustrate my point about integrity. Imagine 2 versions of the same app, which uses deferred loading. The compiled code consists of two files: main.dart.js and main.dart.js_1.part.js. When the user clicks on one part of the UI, it requests a load for main.dart.js_1.part.js.

Supposed we release v1 and v2 versions of this app. Without versioning paths you have an architecture like:

http://app.com/index.html
http://app.com/main.dart.js             // could be v1 or v2
http://app.com/main.dart.js_1.part.js   // could be v2, even if a client loaded the other main.dart.js at v1.

This causes issues when you deploy a new version if anyone had loaded main.dart.js, but haven't loaded the other part file. They can easily end up with v1 of main.dart.js, but v2 of main.dart.js_1.part.js, which is problemantic.

Using versioning paths you have an architecture like:

http://app.com/v1/index.html
http://app.com/v1/main.dart.js
http://app.com/v1/main.dart.js_1.part.js
http://app.com/v2/index.html
http://app.com/v2/main.dart.js
http://app.com/v2/main.dart.js_1.part.js

This ensures that you can't mix v1 and v2 versions of the app accidentally anymore.

@sigmundch sigmundch added the P2 A bug or feature request we're likely to work on label Aug 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. P2 A bug or feature request we're likely to work on web-dart2js
Projects
None yet
Development

No branches or pull requests

3 participants