perf: accelerate production request hot paths
This commit is contained in:
@@ -148,6 +148,10 @@ export interface ProdOptions {
|
||||
port?: number;
|
||||
hostname?: string;
|
||||
maxBodyBytes?: number;
|
||||
/** Keep-alive idle timeout in seconds. Defaults to 30. */
|
||||
idleTimeout?: number;
|
||||
/** Allow multiple Bun workers to share the listening port. */
|
||||
reusePort?: boolean;
|
||||
/** Enable only for the CLI's supervised exact-production development mode. */
|
||||
developmentRuntime?: boolean;
|
||||
}
|
||||
@@ -205,6 +209,19 @@ function buildProdRouter(manifest: ProdManifest): {
|
||||
const api = toRoutes(manifest.api);
|
||||
const realtime = toRoutes(manifest.realtime);
|
||||
|
||||
const optimizedMatcher = (routes: Route[]) => {
|
||||
const exact = new Map<string, Route>();
|
||||
const dynamic: Route[] = [];
|
||||
for (const route of routes) {
|
||||
if (route.paramNames.length === 0) exact.set(route.raw, route);
|
||||
else dynamic.push(route);
|
||||
}
|
||||
return (pathname: string) => {
|
||||
const route = exact.get(pathname);
|
||||
return route ? { route, params: {} } : matchRoute(dynamic, pathname);
|
||||
};
|
||||
};
|
||||
|
||||
// Components are keyed by name; the runtime resolves them via loadModule(name).
|
||||
for (const c of manifest.components) modules.set(c.name, c.mod);
|
||||
// Layouts share the module map under a `layout:` prefix (no name collisions).
|
||||
@@ -219,19 +236,30 @@ function buildProdRouter(manifest: ProdManifest): {
|
||||
layouts: manifest.layouts.map((l) => ({ name: l.name, file: `layout:${l.name}` })),
|
||||
stores: [],
|
||||
schemas: [], // descriptors are pre-baked into schemasJs; not needed at runtime
|
||||
matchPage: (p) => matchRoute(pages, p),
|
||||
matchApi: (p) => matchRoute(api, p),
|
||||
matchRealtime: (p) => matchRoute(realtime, p),
|
||||
matchPage: optimizedMatcher(pages),
|
||||
matchApi: optimizedMatcher(api),
|
||||
matchRealtime: optimizedMatcher(realtime),
|
||||
};
|
||||
|
||||
return { router, modules };
|
||||
}
|
||||
|
||||
/** Serve a pre-built asset file from disk, or 404 if it is absent. */
|
||||
const productionFileCache = new Map<string, ReturnType<typeof Bun.file>>();
|
||||
const missingProductionFiles = new Set<string>();
|
||||
|
||||
/** Serve a pre-built asset file from disk, caching stable build file handles. */
|
||||
async function serveFile(path: string | undefined, headers: Record<string, string>) {
|
||||
if (!path) return new Response("Not Found", { status: 404 });
|
||||
const file = Bun.file(path);
|
||||
if (!(await file.exists())) return new Response("Not Found", { status: 404 });
|
||||
if (missingProductionFiles.has(path)) return new Response("Not Found", { status: 404 });
|
||||
let file = productionFileCache.get(path);
|
||||
if (!file) {
|
||||
file = Bun.file(path);
|
||||
if (!(await file.exists())) {
|
||||
missingProductionFiles.add(path);
|
||||
return new Response("Not Found", { status: 404 });
|
||||
}
|
||||
productionFileCache.set(path, file);
|
||||
}
|
||||
return new Response(file, { headers });
|
||||
}
|
||||
|
||||
@@ -410,6 +438,8 @@ export async function createProductionServer(manifest: ProdManifest, opts: ProdO
|
||||
hostname: resolveProductionHostname(opts.hostname),
|
||||
development: false,
|
||||
maxRequestBodySize: opts.maxBodyBytes ?? 10 * 1024 * 1024,
|
||||
idleTimeout: opts.idleTimeout ?? 30,
|
||||
reusePort: opts.reusePort ?? false,
|
||||
fetch: handlers.fetch,
|
||||
websocket: handlers.websocket,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user