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

[Miniflare 3] Add support for routing to multiple Workers #520

Merged
merged 9 commits into from
Mar 11, 2023

Conversation

mrbbot
Copy link
Contributor

@mrbbot mrbbot commented Feb 23, 2023

This change ports Miniflare 2's mounts router to Miniflare 3. This attempts to replicate the logic of the route(s) field in the Wrangler configuration file: https://developers.cloudflare.com/workers/platform/triggers/routes/#matching-behavior

Internally, this is implemented by binding all routable workers as service bindings in the entry service. The first worker is always bound as a "fallback", in case no routes match. Validation has been added to ensure we a) have a fallback, b) don't have workers with duplicate names that would cause bindings with the same name, and c) all routable/fallback workers have code so they actually get added as workerd services.

I've also split some of the core plugin services into "global services". These need to be constructed from a different set of options to the regular plugin services, and relying on the de-duping behaviour here was a bit silly.

@mrbbot mrbbot requested a review from a team February 23, 2023 11:22
@changeset-bot
Copy link

changeset-bot bot commented Feb 23, 2023

⚠️ No Changeset found

Latest commit: a629360

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@mrbbot mrbbot changed the title Add support for routing to multiple Workers [Miniflare 3] Add support for routing to multiple Workers Feb 23, 2023
This change ports Miniflare 2's `mounts` router to Miniflare 3. This
attempts to replicate the logic of the `route(s)` field in the
Wrangler configuration file:
https://developers.cloudflare.com/workers/platform/triggers/routes/#matching-behavior

Internally, this is implemented by binding all routable workers as
service bindings in the entry service. The first worker is always
bound as a "fallback", in case no routes match. Validation has been
added to ensure we a) have a fallback, b) don't have workers with
duplicate names that would cause bindings with the same name, and c)
all routable/fallback workers have code so they actually get added
as `workerd` services.
@mrbbot mrbbot force-pushed the tre-multi-worker-router branch from 8ecc46f to 9229b30 Compare February 27, 2023 18:18
packages/tre/src/index.ts Show resolved Hide resolved
@@ -119,6 +123,19 @@ function validateOptions(
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The // @ts-expect-errors appear to be because PluginSharedOptions has readonly keys, which can be fixed (and the errors removed) by removing the as const in packages/tre/src/plugins/index.ts. Would it be possible to make that change, or would it break something else? If it's not possible, could the // @ts-expect-error comments be updated?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmmm, I think removing the as const should be ok. It looks like that still keeps the specific Zod types so options inference/completions still work. Will make that change. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

839b02c (unfortunately the change to enforce code means CoreOptionsSchema has required options that aren't satisfied by the other options types, so we still need some // @ts-expect-errors)

const allRoutes = new Map<string, string[]>();
for (const workerOpts of allWorkerOpts) {
if (workerOpts.core.routes !== undefined) {
allRoutes.set(workerOpts.core.name ?? "", workerOpts.core.routes);
Copy link
Contributor

Choose a reason for hiding this comment

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

If there are multiple unnamed workers, will this overwrite some routes?

Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if worker names should be required

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It would, but we've validate names are unique beforehand. Perhaps an assertion that allRoutes doesn't contain the key would be a good idea?

I wonder if worker names should be required

In the single worker case, I don't think they should be. I do agree with you later on though that some of the error messages should be better in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

packages/tre/src/index.ts Show resolved Hide resolved
fallbackWorkerName: string | undefined;
loopbackPort: number;
}
export function getGlobalServices({
Copy link
Contributor

Choose a reason for hiding this comment

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

This feels like it probably shouldn't be in the core plugin?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmmm, maybe. That might help with the moving CORE_PLUGIN_NAME issue you raised later on too. Will move it out into plugins/shared/services.ts or something. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

packages/tre/src/plugins/core/index.ts Show resolved Hide resolved
packages/tre/src/plugins/core/index.ts Show resolved Hide resolved
Comment on lines 381 to 386
throw new MiniflareCoreError(
"ERR_ROUTABLE_NO_SCRIPT",
`Worker [${workerIndex}] ${
options.name === undefined ? "" : `("${options.name}") `
}must have code defined as it's routable or the fallback`
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why can any workers have no code? What does it mean for a worker to not have code defined?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmmm, I suppose that doesn't really make sense, since we don't expose workered's other service types (disk, network, etc) outside of serviceBindings. Will make code required for all Workers. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

packages/tre/src/plugins/core/index.ts Show resolved Hide resolved
const A_MORE_SPECIFIC = -1;
const B_MORE_SPECIFIC = 1;

export function parseRoutes(allRoutes: Map<string, string[]>): WorkerRoute[] {
Copy link
Contributor

Choose a reason for hiding this comment

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

I imagine there's code to do this in EWC somewhere—have you checked the logic against that? (I'm assuming this is copied from Miniflare 2)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I haven't, you're correct this was copied from Miniflare 2, when I didn't have access to this. Will see if I can find it. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(and a629360 to fixup test)

mrbbot added 8 commits March 7, 2023 12:34
It doesn't really make sense to have Workers without code. This
change updates our `zod` schemas to encode this requirement.
Assert names unique when collecting routes
Move `CORE_PLUGIN_NAME` back to `core`
Add specific error message when defining multiple unnamed workers
Use `Map` when de-duping services
Extract out common plugin/namespace/persist Worker into function
Use same specificity calculation for routes as internal service
@mrbbot mrbbot requested a review from penalosa March 7, 2023 13:55
@mrbbot mrbbot merged commit 7d032ee into tre Mar 11, 2023
@mrbbot mrbbot deleted the tre-multi-worker-router branch March 11, 2023 16:52
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.

2 participants