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

Fix lazy compilation #9093

Merged
merged 6 commits into from
Jul 31, 2023

Conversation

marcins
Copy link
Contributor

@marcins marcins commented Jun 14, 2023

↪️ Pull Request

This fixes the reproduction provided in #8816 (Thanks @rayinaway), as well as a separate reproduction I created at https://github.com/marcins/lazy-testbed.

There were a couple of issues I found when trying to create a basic lazy compilation example:

  • The decision as to whether a bundle is a placeholder isn't revisited after the bundle is created. A change has been made to set the placeholder value to false when a bundle is requested.
  • Whether a bundle is a placeholder has been added to it's hash so that the bundle graph hash is invalidated when a bundle is no longer a placeholder.
  • When a previously deferred dependency was no longer deferred, the asset graph was updated correctly, but this didn't re-trigger bundling with incremental bundling. A change has been made to skip incremental bundling when a dependency is "un-deferred".

A new integration test based on my testbed reproduction has been added that I first verified was failing on current v2 branch, and verified that it now passes with these changes. In addition I tested the reproduction from #8816 and verified that Parcel with these fixes now loads those bundles correctly.

Note that due to my lack of deep knowledge in the areas touched the testing is possibly not exhaustive, I possibly haven't tested some scenarios that may still fail with these changes. I did manually test adding / removing dependencies and confirming that things re-compiled correctly.

Given the scope of the changes, they shouldn't affect anything outside of --lazy mode, so at least they're unlikely to break other functionality.

🚨 Test instructions

See repro repos above - these fail with current v2, but succeed when using Parcel linked with these changes.

✔️ PR Todo

  • Added/updated unit tests for this change
  • Filled out test instructions (In case there aren't any unit tests)
  • Included links to related issues/PRs

@@ -353,10 +353,12 @@ export default class AssetGraph extends ContentGraph<AssetGraphNode> {
nodeId !== traversedNodeId
) {
if (!ctx?.hasDeferred) {
this.safeToIncrementallyBundle = false;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do any of the other cases here need this added?

The symptoms I saw was that while the asset graph was correctly updated after a previously lazy bundle was requested, the bundle graph was not - so I've focused on cases where dependency nodes have changed from deferred to not deferred, rather than asset nodes.

@@ -321,6 +321,10 @@ export default class Parcel {
};
}

// If this bundle was previously a placeholder, it now no longer is as it has
// been requested.
bundleNode.value.isPlaceholder = false;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is the right place or way to do this - is it "safe" to mutate a bundleNode here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm kinda weird but probably ok since we only store the bundle graph in the cache when parcel exits.

Copy link
Contributor Author

@marcins marcins Jul 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I just had another look and I have a feeling this might no longer be required with the this.safeToIncrementallyBundle = false; fixes, as they'll cause the bundle graph to be recreated and the placeholder flag will be appropriately set.

My tests seem to pass without this line, I'll just validate it's the safeToIncrementallyBundle change that's fixing the issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, I've removed this change as the other change (which I made later) in AssetGraph fixes the issue this was fixing.

@marcins marcins force-pushed the mszczepanski/fix-lazy-compilation branch from 2c22a39 to 92ee5eb Compare June 14, 2023 07:41
@marcins marcins marked this pull request as draft June 19, 2023 00:41
@marcins marcins force-pushed the mszczepanski/fix-lazy-compilation branch from 2eb85ac to 43d98d0 Compare July 24, 2023 06:34
assert(await distDirIncludes(['index.js', /^lazy-1\./, /^lazy-2\./]));
});

it('should lazy compile properly when same module is used sync/async', async () => {
Copy link
Contributor Author

@marcins marcins Jul 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test still fails on this branch, it has been pulled in as an intentional broken test (hence the draft PR).

The "fix" is to remove the conditions for shouldBuildLazily in JSRuntime (e.g.:

// Also do this when building lazily or the runtime itself could get deduplicated and only
// exist in the parent. This causes errors if an old version of the parent without the runtime
// is already loaded.
if (
bundle.env.outputFormat === 'commonjs' ||
bundle.env.isLibrary ||
options.shouldBuildLazily
) {
externalBundles = [mainBundle];
} else {
)

With the code as it is, the whole bundle group doesn't get loaded - so when the uses-static-component-async bundle loads, it doesn't trigger a load of the static-component bundle, so it can't resolve the module.

I know that the"fix" (seen here: marcins@a36a37d) means that there is a potential issue when a bundle group grows after lazy compilation - this means Parcel can get into a state where it requires a browser refresh in order to recover. However, with the current state, a refresh won't heal as the load order is just incorrect.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed in the Core meeting, the "fix" for this has now been cherry-picked onto this branch. I've updated the comments to leave behind some trace that there was lazy build specific code previously there.

@marcins marcins marked this pull request as ready for review July 26, 2023 03:46
@marcins marcins requested a review from devongovett July 26, 2023 03:47
@marcins marcins self-assigned this Jul 26, 2023
@marcins marcins force-pushed the mszczepanski/fix-lazy-compilation branch from b58f23e to a031a0f Compare July 31, 2023 06:35
@marcins marcins merged commit 16deba5 into parcel-bundler:v2 Jul 31, 2023
16 checks passed
@marcins marcins deleted the mszczepanski/fix-lazy-compilation branch July 31, 2023 10:03
@jondlm
Copy link
Contributor

jondlm commented Aug 1, 2023

Hey @marcins I tried out this patch but I'm still seeing some more issues with lazy mode. Here's another minimal repro that's exhibiting similar issues to what this PR was meant to fix. Maybe it'll be helpful to shake out more bugs! I can also file this as a separate issue if that's helpful.

I'm still trying to get comfortable with parcel internals to propose fixes but I'm not quite there yet.

Thank you for all your hard work on --lazy, I'm also quite interested in seeing it work properly on a large codebase we're (Stripe) piloting Parcel out on.

@devongovett
Copy link
Member

@jondlm did you build Parcel locally? This hasn't even been released in a nightly yet.

@marcins
Copy link
Contributor Author

marcins commented Aug 1, 2023

Hey @marcins I tried out this patch but I'm still seeing some more issues with lazy mode. Here's another minimal repro that's exhibiting similar issues to what this PR was meant to fix. Maybe it'll be helpful to shake out more bugs! I can also file this as a separate issue if that's helpful.

Thanks for that repro @jondlm - I haven't tried it, but looking at the description in the README that a refresh fixes it, I have a feeling you might've run into the issue which the conditions I removed in JSRuntime were trying to address.. with those conditions present, lazy compilation wouldn't work at all for our large application (even after refreshing), but without them it does work however I did intermittently see a "module not found" error that a refresh would fix. Presumably that's the issue the logic there was trying to avoid.

As I couldn't find a reliable way to minimally repro this, we decided to merge the fixes for now as they definitely make things better than before, albeit requiring an occasional refresh to unstick things. Having a repro that fails with these fixes in place will definitely be handy in understanding the issue and working on a proper fix.

But just to confirm as Devon asked - you still see this issue by running a locally built Parcel with this patch applied? If that's the case, then yes please - do file a separate issue.

@jondlm
Copy link
Contributor

jondlm commented Aug 1, 2023

@devongovett I pulled commit 824ddbe, compiled the JS runtime, copied it into my project's node_modules, and then ran Parcel via bin.js. I think that should pull in all the relevant changes from this PR.

Thanks for the context @marcins. I'll file a separate issue with the case I found 👍

@marcins
Copy link
Contributor Author

marcins commented Aug 2, 2023

I pulled commit 824ddbe, compiled the JS runtime, copied it into my project's node_modules, and then ran Parcel via bin.js. I think that should pull in all the relevant changes from this PR.

You'll also need the changes in AssetGraph and BundleGraph to ensure bundles are rebuilt when they should be - though the symptoms of your issue (fixed by a refresh) definitely point to the JSRuntime part.

In any case, the latest nightly should now contain these fixes.

@natan-stripe
Copy link

👋 I created a fork of your original lazy-testbed repo with the steps to reproduce the additional issue that @jondlm and I are bumping into which he mentioned above. We've spent some time trying to figure out how to fix it but haven't really been able to understand the expected behavior and usage of deferred modules properly.

It seems like it's very close to working but there's just like one connecting piece that should be tying lazy async bundles to the relevant shared bundles that is maybe missing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants