Skip to content

Regression: since 7.2.1, middleware rebuilds on _every_ hotUpdate #17933

Description

@mhsnook

Astro Info

Astro                    v7.3.1
Node                     v22.22.2
System                   Linux (x64)
Package Manager          pnpm
Output                   server
Adapter                  @astrojs/cloudflare
Integrations             none

If this issue only occurs in one browser, which browser is a problem?

No response

Describe the Bug

Since 7.2.1, middleware invalidations no longer filter out unrelated hotUpdate triggers, causing extra invalidations, extra SSR, extra load times.

PR #17605, which fixed #17590, replaced server.watcher.on("change", ...) with a hotUpdate handler that takes no arguments and invalidates middleware without regard for the nature of the update. The old code had some quick checks and early returns:

// v 7.2.0
server.watcher.on("change", (path) => {
  const normalizedPath = viteNormalizePath(path);
  if (!normalizedPath.startsWith(normalizedSrcDir)) return;   // not under srcDir
  const relativePath = normalizedPath.slice(normalizedSrcDir.length);
  if (!isMiddlewarePath(relativePath)) return;                // not the middleware
  // ...invalidate
});

You can see the "if (x) early return" happening twice. The new code takes no arguments so the handler is unable to inspect the change for relevance:

// v 7.2.1 and later
hotUpdate: {
  handler() {
    if (!isAstroServerEnvironment(this.environment)) return;
    const middlewareVirtualMod = this.environment.moduleGraph
      .getModuleById(MIDDLEWARE_RESOLVED_MODULE_ID);
    if (!middlewareVirtualMod) return;
    this.environment.moduleGraph.invalidateModule(middlewareVirtualMod);
    this.environment.hot.send("astro:middleware-updated", {});
  }
}

A partial fix for some of the more provably useless updates is to check for an empty ctx.modules array, and return early. That fix also resolves #17922, and may improve the situation for some other recent bug-filings involving cloudflare/workerd, middleware and SSR over-rebuilding.

I suspect this is starting to bite more often because of changes to the @cloudflare/vite-plugin, which by default now writes to .wrangler/state on every pageload (writing the trace to SQLite), which Vite's watcher sees as a file change, firing the hotUpdate hook every time any of Miniflare's different services write to their local state. (That is filed as cloudflare/workers-sdk#15550 -- careful, it only reproduces on Windows or Linux, in case you get curious.) On a fresh, new EmDash site, the combination of this Miniflare-state-mutation -> Vite hotUpdate -> Astro middleware rebuild -> SSR rebuild, means that simply loading localhost:4321 was taking 30 second on my Linux machine.

On @astrojs/node the invalidation fires too — it's just rare and cheap (12 handler calls, 6 invalidations for three file touches; requests stay at 0.03s). On @astrojs/cloudflare, five plain requests with nothing edited produced 222 handler calls and 111 invalidations — about 22 invalidations per request, because Miniflare rewrites its trace-store SQLite repeatedly while serving a single page. Only one or two distinct files change per request, out of 21 in the directory — it's the same file being written over and over, and every write fires the hook.

What's the expected result?

hotUpdate events that list no changed modules should be discarded rather than invalidating middleware.

draft fix: https://github.com/mhsnook/astro/pull/1/changes

Link to Minimal Reproducible Example

https://github.com/mhsnook/bug-repro-astro-hmr

Participation

  • I am willing to submit a pull request for this issue.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

- P4: importantViolate documented behavior or significantly impacts performance (priority)pkg: astroRelated to the core `astro` package (scope)triage: fix pendingReporter needs to verify the triage bot fix works

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions