Skip to content

Revert to external middleware #656

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

Merged
merged 4 commits into from
May 9, 2025
Merged

Revert to external middleware #656

merged 4 commits into from
May 9, 2025

Conversation

conico974
Copy link
Collaborator

@conico974 conico974 commented May 8, 2025

This is just a proposal for now, but this is the best way that i can think of to really reduce cold start for ISR/SSG (in combination with enableCacheInterception)

By using that we can go from 600-800ms of cpu time for ISR/SSG during cold start to around 30-40ms. Tested with the app-router e2e app with enableCacheInterception and cache purge as well.

This has the drawback of increasing the size of the app.

We should also think if we want to move back the main handler import at the top as well. This could reduce cpu time for SSR route themselves, but could cause issue with thing that are loaded right away like instrumentation (especially if there is I/O)

Copy link

changeset-bot bot commented May 8, 2025

🦋 Changeset detected

Latest commit: 238b72f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@opennextjs/cloudflare Patch

Not sure what this means? Click here to learn what changesets are.

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

Copy link

pkg-pr-new bot commented May 8, 2025

Open in StackBlitz

pnpm add https://pkg.pr.new/@opennextjs/cloudflare@656

commit: 238b72f

if (reqOrResp instanceof Response) {
return reqOrResp;
}

// @ts-expect-error: resolved by wrangler build
const { handler } = await import("./server-functions/default/handler.mjs");
Copy link
Contributor

Choose a reason for hiding this comment

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

Thought: The code is always parsed wether or not we return on line 41. Meaning that the perf difference could be explained by the global init. To have the init done only once we could have a top level serverHandler and have

serverHandler ??= (await import("./server-functions/default/handler.mjs")).handler;

We probably want something similar for the middlewareHandler - we the current top level handler, we would have the same issues as with the server handler: process.env not being initialized at that point and not possible to do IO at top level.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We probably want something similar for the middlewareHandler

That's not an issue for the middleware, we already import it using an awaited import https://github.com/opennextjs/opennextjs-aws/blob/c2da3a8df638b8facdb896e22538a143b3425006/packages/open-next/src/core/routing/middleware.ts#L38

The code is always parsed wether or not we return on line 41. Meaning that the perf difference could be explained by the global init.

I think the problem is not with the parsing itself, but with the v8 compilation. The whole file is parsed, but not all of it is compiled.

I did a simple experiment of returning a silly Response just after the await import and it added barely 100ms of cpu time, compared to the minimum 400 that i would get when returning something from the middleware layer (a redirect or any response from the middleware)

I did an experiment with v8 compile cache in aws and by precompiling the files, you can easily shave 200/300ms, but it did not fully remove the cold start. Things that are imported/required only inside a function doesn't get compiled until the function is actually called.
And i think that when the middleware is bundled, it is actually inside a big function that also contains every other NextServer related things, which probably explains the difference

Copy link
Contributor

@vicb vicb left a comment

Choose a reason for hiding this comment

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

LGTM,

We should update packages/cloudflare/src/cli/build/utils/ensure-cf-config.ts to make sure the mw is external

@conico974
Copy link
Collaborator Author

I'll fix it and rebase on main then.
One thing i think we should think about is relaxing some of the requirement in ensure-cf-config, maybe just show a warning instead of throwing an error. It could be useful for people that would want more customization

@conico974 conico974 force-pushed the feat/external-middleware branch from d6ef564 to 6113481 Compare May 9, 2025 09:05
@conico974 conico974 changed the base branch from feat/cache-purge to main May 9, 2025 09:06
@vicb
Copy link
Contributor

vicb commented May 9, 2025

One thing i think we should think about is relaxing some of the requirement in ensure-cf-config, maybe just show a warning instead of throwing an error. It could be useful for people that would want more customization

+1, We could use cloudflare.dangerousDisableConfigValidation to switch from error to warnings

@conico974 conico974 marked this pull request as ready for review May 9, 2025 09:27
@conico974 conico974 merged commit a20d2df into main May 9, 2025
7 checks passed
@conico974 conico974 deleted the feat/external-middleware branch May 9, 2025 10:06
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