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

Prevent concurrent builds from happening #27

Merged
merged 3 commits into from Mar 20, 2021

Conversation

jridgewell
Copy link
Collaborator

AMP noticed that multiple initial builds can happen if Karma takes a long time between calls to karma-esbuild's preprocessor (in our case, we have hundreds of files having SHA hashes performed). This lead to multiple bundle.write calls happening.

Now, our test suite is so large, esbuild takes a few seconds to run through everything. So when the second bundle.write happens, we're still trying to bundle the first time. This leads to 2 initial builds. Because both builds are incremental, they hold on to esbuild's Go process. And because we would lose our reference to the first incremental build when the second finished, we now had an undying holder on esbuild.

While solving this concurrent initial build problem, I decided that we should never have 2 concurrent builds happening at the same time. Now only 1 build can be happening at a time, and if multiple write come in (either during the initial build or during a rebuild), we'll wait for the in-progress build to finish before starting the new one. A fun challenge was ensuring that we can only have at most 1 in-progress build and 1 pending build.

Async reentrant problems are hard.

AMP noticed that multiple _initial_ builds can happen if Karma takes a long time between calls to karma-esbuild's preprocessor (in our case, we have hundreds of files having SHA hashes performed). This lead to multiple `bundle.write` calls happening.

Now, our test suite is so large, esbuild takes a few seconds to run through everything. So when the second `bundle.write` happens, we're still trying to bundle the first time. This leads to 2 initial builds. Because both builds are `incremental`, they hold on to esbuild's Go process. And because we would lose our reference to the first incremental build when the second finished, we now had an undying holder on esbuild.

While solving this concurrent initial build problem, I decided that we should never have 2 concurrent builds happening at the same time. Now only 1 build can be happening at a time, and if multiple write come in (either during the initial build or during a rebuild), we'll wait for the in-progress build to finish before starting the new one.

Async reentrant problems are hard.
Let the default equality message shine through
Copy link
Owner

@marvinhagemeister marvinhagemeister left a comment

Choose a reason for hiding this comment

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

This is fantastic! It makes so much more sense to disallow parallel builds. Love this 👍

@marvinhagemeister marvinhagemeister merged commit e553c7f into marvinhagemeister:main Mar 20, 2021
Copy link
Contributor

@samouri samouri left a comment

Choose a reason for hiding this comment

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

IDEs really need spellcheck 😄

// case, we resolve the old build with the latest result.
private buildsInProgress = 0;
// buildInProgress tracks the in-progress build. When a build takes too
// long, a new build may have been requsted before the original completed.
Copy link
Contributor

Choose a reason for hiding this comment

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

s/requsted/requested

this.beforeProcess();
} else {
// Wait for the previous build to happen before we continue. This prevents
// any reentrant behavior, and guarnatees we can get an initial bundle to
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: s/guarnatees/guarantees

@@ -100,7 +122,10 @@ export class Bundle {
// Wait for any in-progress builds to finish. At this point, we know no
// new ones will come in, we're just waiting for the current one to
// finish running.
if (this.buildsInProgress > 0 || this._dirty) {
if (this.buildInProgress || this._dirty) {
// Wait on the deffered, not the buildInProgress, because the dirty flag
Copy link
Contributor

Choose a reason for hiding this comment

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

s/deffered/deferred

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.

None yet

3 participants