Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(ngcc): do not spawn unlocker processes on cluster workers #36569

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

Closed
wants to merge 2 commits into from

Conversation

gkalpak
Copy link
Member

@gkalpak gkalpak commented Apr 10, 2020

The current ngcc lock-file strategy spawns a new process in order to capture a potential SIGINT and remove the lock-file. For more information see #35861.

Previously, this unlocker process was spawned as soon as the LockFile was instantiated in order to have it available as soon as possible (given that spawning a process is an asynchronous operation). Since the LockFile was instantiated and passed to the Executor, this meant that an unlocker process was spawned for each cluster worker, when running ngcc in parallel mode. These processes were not needed, since the LockFile was not used in cluster workers, but we still had to pay the overhead of each process' own memory and V8 instance.
(NOTE: This overhead was small compared to the memory consumed by ngcc's normal operations, but still unnecessary.)

This commit avoids the extra processes by only spawning an unlocker process when first creating a lock-file (which only happens on the cluster master process). Since instantiating the LockFile and attempting to lock happen on the same tick, this change is unlikely to cause any difference in actual behavior.

@pullapprove pullapprove bot requested a review from JoostK April 10, 2020 18:59
@gkalpak gkalpak added comp: ngcc action: merge The PR is ready for merge by the caretaker target: patch This PR is targeted for the next patch release type: bug/fix labels Apr 10, 2020
@ngbot ngbot bot added this to the needsTriage milestone Apr 10, 2020
@mary-poppins
Copy link

You can preview 038c341 at https://pr36569-038c341.ngbuilds.io/.

@atscott atscott added the action: review The PR is still awaiting reviews from at least one requested reviewer label Apr 10, 2020
Copy link
Contributor

@petebacondarwin petebacondarwin left a comment

Choose a reason for hiding this comment

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

The later the unlocker child process is created the more chance there is of it not being in-place in case the main process dies (or is killed). When I was testing different positions for the creation of the child process, it was quite possible to Ctrl-C before the unlocker had been created. Can you test this? Especially for the sync version of ngcc.

Copy link
Contributor

@petebacondarwin petebacondarwin left a comment

Choose a reason for hiding this comment

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

Instead of changing when the unlocker process is created, could we not just guard it with a check for cluster.isMaster?

@gkalpak
Copy link
Member Author

gkalpak commented Apr 12, 2020

The code between instantiating the LockFile (which is where the unlocker process was spawn before this PR) and calling LockFile#write() (which where the unlocker process will be spawned after this PR) is all synchronous. So, I assumed there would be a tiny delay between the two (~10ms).

I tested it (which always beats "assuming" 😛) and I am seeing a delay between 40ms and 90ms (which is significantly higher than I thought. I'll reword the PR.

Thx for the feedback ❤️

Instead of changing when the unlocker process is created, could we not just guard it with a check for cluster.isMaster?

We could, but I don't want to sprinkle the code with cluster-specific APIs as it will make it more difficult to migrate to a different parallelization mechanism. So, I try to limit the use of cluster APIs inside ngcc/src/execution/cluster/.

Sounds like we might need a cluster-specific LockFile 😁

@petebacondarwin
Copy link
Contributor

We could, but I don't want to sprinkle the code with cluster-specific APIs as it will make it more difficult to migrate to a different parallelization mechanism. So, I try to limit the use of cluster APIs inside ngcc/src/execution/cluster/.

This is specifically why I did not implement that previously. (I did know about the extra unlockers but I kept quiet about it 🙀). Are there any cases where cluster.isMaster would be false when we are not using clustering?

gkalpak added 2 commits April 12, 2020 19:17
The current ngcc lock-file strategy spawns a new process in order to
capture a potential `SIGINT` and remove the lock-file. For more
information see angular#35861.

Previously, this unlocker process was spawned as soon as the `LockFile`
was instantiated in order to have it available as soon as possible
(given that spawning a process is an asynchronous operation). Since the
`LockFile` was instantiated and passed to the `Executor`, this meant
that an unlocker process was spawned for each cluster worker, when
running ngcc in parallel mode. These processes were not needed, since
the `LockFile` was not used in cluster workers, but we still had to pay
the overhead of each process' own memory and V8 instance.
(NOTE: This overhead was small compared to the memory consumed by ngcc's
normal operations, but still unnecessary.)

This commit avoids the extra processes by only spawning an unlocker
process when running on the cluster master process and not on worker
processes.
On Windows, the output of a detached process (such as the unlocker
process used by `LockFileWithChildProcess`) is not shown in the parent
process' stdout.

This commit addresses this by piping the spawned process' stdin/stdout
and manually writing to the parent process' stdout.
@gkalpak gkalpak force-pushed the fix-ngcc-unlocker branch from 038c341 to f6304ea Compare April 12, 2020 16:18
@gkalpak gkalpak requested a review from petebacondarwin April 12, 2020 16:18
@gkalpak gkalpak removed the action: merge The PR is ready for merge by the caretaker label Apr 12, 2020
@gkalpak
Copy link
Member Author

gkalpak commented Apr 12, 2020

I think cluster.isMaster will always be true when not running on a cluster worker afaict, but I would still prefer to refrain from using cluster.XYZ outside the ngcc/src/execution/cluster/ directory. If we move to a different parallelization mechanism in the future, it will be easy to miss a cluster-specific API and continue to operate under the wrong assumption.

I changed the first commit to use a ClusterLockFileWithChildProcess \o/
PTAL

@mary-poppins
Copy link

You can preview f6304ea at https://pr36569-f6304ea.ngbuilds.io/.

* A `LockFileWithChildProcess` that is `cluster`-aware and does not spawn unlocker processes from
* worker processes (only from the master process, which does the locking).
*/
export class ClusterLockFileWithChildProcess extends LockFileWithChildProcess {
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than extending LockFileWithChildProcess, how about using composition instead...

export class ClusterLockFile implements LockFile {
  constructor(private delegate: LockFileWithChildProcess) {
    if (cluster.isMaster) {
      delegate.createUnlocker();
    }
  }
  ... etc
}

Obviously we would need to make createUnlocker() public, but this would make testing a lot cleaner, avoiding all those spies, since you could just pass a mock delegate in for testing.

Copy link
Contributor

Choose a reason for hiding this comment

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

It might need a bit of tweaking.... to work properly but you get the idea...

Copy link
Contributor

Choose a reason for hiding this comment

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

Or perhaps we could move this logic into the Locker classes themselves? Saving the middleman?

Copy link
Member Author

Choose a reason for hiding this comment

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

Rather than extending LockFileWithChildProcess, how about using composition instead...

That would be my preference too, but the delegate would have spawned the process in its constructor. There are ways around that but they either make things more complicated than they already are or end up delaying the creation of the unlocker (which we are trying to avoid here).

Or perhaps we could move this logic into the Locker classes themselves? Saving the middleman?

Not sure what you mean by that 😅

Copy link
Contributor

@petebacondarwin petebacondarwin left a comment

Choose a reason for hiding this comment

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

On further thought, over the weekend, I am happy to approve this.
My big gripe, outside of this PR, is that we are kicking off the worker processes with the same entry-point as the master process, which requires us to jump through these hoops.
I would like to propose that we land this but then refactor the whole clustering approach to avoid having to do this.

@gkalpak gkalpak removed the request for review from JoostK April 15, 2020 09:44
@gkalpak gkalpak added action: merge The PR is ready for merge by the caretaker and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Apr 15, 2020
@atscott atscott closed this in 66effde Apr 15, 2020
atscott pushed a commit that referenced this pull request Apr 15, 2020
On Windows, the output of a detached process (such as the unlocker
process used by `LockFileWithChildProcess`) is not shown in the parent
process' stdout.

This commit addresses this by piping the spawned process' stdin/stdout
and manually writing to the parent process' stdout.

PR Close #36569
atscott pushed a commit that referenced this pull request Apr 15, 2020
The current ngcc lock-file strategy spawns a new process in order to
capture a potential `SIGINT` and remove the lock-file. For more
information see #35861.

Previously, this unlocker process was spawned as soon as the `LockFile`
was instantiated in order to have it available as soon as possible
(given that spawning a process is an asynchronous operation). Since the
`LockFile` was instantiated and passed to the `Executor`, this meant
that an unlocker process was spawned for each cluster worker, when
running ngcc in parallel mode. These processes were not needed, since
the `LockFile` was not used in cluster workers, but we still had to pay
the overhead of each process' own memory and V8 instance.
(NOTE: This overhead was small compared to the memory consumed by ngcc's
normal operations, but still unnecessary.)

This commit avoids the extra processes by only spawning an unlocker
process when running on the cluster master process and not on worker
processes.

PR Close #36569
atscott pushed a commit that referenced this pull request Apr 15, 2020
On Windows, the output of a detached process (such as the unlocker
process used by `LockFileWithChildProcess`) is not shown in the parent
process' stdout.

This commit addresses this by piping the spawned process' stdin/stdout
and manually writing to the parent process' stdout.

PR Close #36569
@gkalpak gkalpak deleted the fix-ngcc-unlocker branch April 15, 2020 16:28
petebacondarwin added a commit to petebacondarwin/angular that referenced this pull request Apr 16, 2020
matsko pushed a commit that referenced this pull request Apr 16, 2020
matsko pushed a commit that referenced this pull request Apr 16, 2020
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators May 16, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes target: patch This PR is targeted for the next patch release type: bug/fix
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants