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

Use listener API for communication between build/server processes. #10077

Merged
merged 9 commits into from Jul 17, 2018

Conversation

benjamn
Copy link
Contributor

@benjamn benjamn commented Jul 16, 2018

Fixes #10073, per #10073 (comment)

While thinking about this bug, I realized that sending IPC messages to specific packages in the server process was much less flexible than sending messages based on an arbitrary topic string, since the topic string approach allows both autoupdate and dynamic-import to listen for the same message.

The topic string approach calls for a listener interface like onMessage(topic, callback), which elegantly replaces the previous approach of requiring packages to export a single onMessage function.

However, because the meteor package does not have access to the module system, implementing the onMessage listener interface in the meteor package would have required exposing an API like Meteor.onMessage(topic, callback), which has an unpleasant global smell to it. Instead, the onMessage function should be explicitly imported (using the module system) from some less-generically-named package.

Since I knew I was going to have to move the message dispatch logic out of the meteor package, I decided to create a new package called inter-process-messaging to implement the parent/child components of the IPC system.

@benjamn benjamn added this to the Release 1.7.1 milestone Jul 16, 2018
// package, one after the last. The first message waits for the
// package to call Package._define(packageName, exports).
promises[packageName] = (
promises[packageName] || Package._promise(packageName)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since we're no longer using Package._promise in core, and it was only recently introduced for this (now removed) code, I think we should remove it for the sake of keeping the APIs exposed by the core meteor package as small as possible.

});
// If arch is not a string, the receiver of this message should
// assume all clients need to be refreshed.
await appProcess.proc.sendMessage("client-refresh");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If the autoupdate package is not installed, no message will be delivered to that package, so no error needs to be caught here.

// autoupdate package.
Object.keys(cachesByPlatform).forEach(platform => {
delete cachesByPlatform[platform];
});
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the part of the PR that actually fixes #10073.

Fixes #10073, per
#10073 (comment)

While thinking about this bug, I realized that sending IPC messages to
specific packages in the server process was much less flexible than
sending messages based on an arbitrary topic string, since the topic
string approach allows both `autoupdate` and `dynamic-import` to listen
for the same message.

The topic string approach calls for a listener interface like
`onMessage(topic, callback)`, which elegantly replaces the previous
approach of requiring packages to export a single `onMessage` function.

However, because the `meteor` package does not have access to the module
system, implementing the `onMessage` listener interface in the `meteor`
package would have required exposing an API like `Meteor.onMessage(topic,
callback)`, which has an unpleasant global smell to it. Instead, the
`onMessage` function should be explicitly imported (using the module
system) from a less-generically-named package.

Since I knew I was going to have to move the message dispatch logic out of
the `meteor` package, I decided to create a new package called
`inter-process-messaging` to implement the parent/child components of the
IPC system.
const results = [];
const callbacks = callbacksByTopic.get(topic);
if (callbacks && callbacks.size > 0) {
callbacks.forEach(cb => results.push(cb(payload)));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

One really nice benefit of using Set#forEach here is that if you add new elements to the set while you're traversing it, they will be included in the same forEach loop!

Every process is potentially the child of some other process and the
parent of zero or more child processes of its own, so it's confusing to
use terminology that always treats the current global.process as a
"parent" process, or to include PARENT and CHILD in the message types.

Instead, this new implementation uses message types MESSAGE, RESPONSE,
PING, and PONG, and refers to `process` and `otherProcess` objects,
with the caveat that sometimes `process === otherProcess`, because
`process.send` can be used to send messages to the parent process.

Instead of relying on the child to send a special CHILD_READY message to
the parent when it's ready to receive messages, the sending process polls
the receiving process with a preflight PING message, and the receiving
process immediately responds with a PONG when ready.
@benjamn
Copy link
Contributor Author

benjamn commented Jul 17, 2018

I would like to extract this functionality as an npm package one day, but I think I need to draw the line on this endless refactoring somewhere.

@benjamn benjamn merged commit d7e489d into release-1.7.1 Jul 17, 2018
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

2 participants