Skip to content

Commit 32b4302

Browse files
ematipicosarah11918yanthomasdevArmandPhilippot
authored
feat(runtime): use queued based rendering (#15471)
Co-authored-by: Sarah Rainsberger <5098874+sarah11918@users.noreply.github.com> Co-authored-by: Yan <61414485+yanthomasdev@users.noreply.github.com> Co-authored-by: Armand Philippot <git@armand.philippot.eu>
1 parent be1c87e commit 32b4302

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+3276
-20
lines changed

.changeset/hot-eyes-sink.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
---
2+
'astro': minor
3+
---
4+
5+
Adds a new experimental flag `queuedRendering` to enable a queue-based rendering engine
6+
7+
The new engine is based on a two-pass process, where the first pass
8+
traverses the tree of components, emits an ordered queue, and then the queue is rendered.
9+
10+
The new engine does not use recursion, and comes with two customizable options.
11+
12+
Early benchmarks showed significant speed improvements and memory efficiency in big projects.
13+
14+
#### Queue-rendered based
15+
16+
The new engine can be enabled in your Astro config with `experimental.queuedRendering.enabled` set to `true`, and can be further customized with additional sub-features.
17+
18+
```js
19+
// astro.config.mjs
20+
export default defineConfig({
21+
experimental: {
22+
queuedRendering: {
23+
enabled: true
24+
}
25+
}
26+
})
27+
```
28+
29+
#### Pooling
30+
31+
With the new engine enabled, you now have the option to have a pool of nodes that can be saved and reused across page rendering. Node pooling has no effect when rendering pages on demand (SSR) because these rendering requests don't share memory. However, it can be very useful for performance when building static pages.
32+
33+
```js
34+
// astro.config.mjs
35+
export default defineConfig({
36+
experimental: {
37+
queuedRendering: {
38+
enabled: true,
39+
poolSize: 2000 // store up to 2k nodes to be reused across renderers
40+
}
41+
}
42+
});
43+
```
44+
45+
#### Content caching
46+
47+
The new engine additionally unlocks a new `contentCache` option. This allows you to cache values of nodes during the rendering phase. This is currently a boolean feature with no further customization (e.g. size of cache) that uses sensible defaults for most large content collections:
48+
49+
When disabled, the pool engine won't cache strings, but only types.
50+
51+
```js
52+
// astro.config.mjs
53+
export default defineConfig({
54+
experimental: {
55+
queuedRendering: {
56+
enabled: true,
57+
contentCache: true // enable re-use of node values
58+
}
59+
}
60+
});
61+
```
62+
63+
For more information on enabling and using this feature in your project, see the [experimental queued rendering docs](https://v6.docs.astro.build/en/reference/experimental-flags/queued-rendering/) for more details.

.github/workflows/continuous_benchmark.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
runs-on: ubuntu-latest
2828
strategy:
2929
matrix:
30-
shard: [ "1/4","2/4", "3/4", "4/4" ]
30+
shard: [ "1/4", "2/4", "3/4", "4/4" ]
3131
permissions:
3232
contents: read
3333
pull-requests: write

benchmark/packages/adapter/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export default function createIntegration(): AstroIntegration {
1717
setAdapter({
1818
name: '@benchmark/adapter',
1919
serverEntrypoint: '@benchmark/adapter/server.js',
20-
exports: ['manifest', 'createApp'],
20+
exports: ['manifest', 'createApp', 'App'],
2121
supportedAstroFeatures: {
2222
serverOutput: 'stable',
2323
envGetSecret: 'experimental',

benchmark/packages/adapter/src/server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,7 @@ export function createExports(manifest: SSRManifest) {
3838
return {
3939
manifest,
4040
createApp: (streaming: boolean) => new MyApp(manifest, streaming),
41+
// Export App class directly for benchmarks that need to pass custom manifests
42+
App: MyApp,
4143
};
4244
}

benchmark/static-projects/build-hybrid/astro.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ import node from '@astrojs/node';
44
export default defineConfig({
55
adapter: node({
66
mode: 'standalone',
7-
}),
7+
})
88
});

packages/astro/src/assets/vite-plugin-assets.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,10 @@ export default function assets({ fs, settings, sync, logger }: Options): vite.Pl
168168
export { default as Picture } from "astro/components/${imageComponentPrefix}Picture.astro";
169169
import { inferRemoteSize as inferRemoteSizeInternal } from "astro/assets/utils/inferRemoteSize.js";
170170
171-
export { default as Font } from "astro/components/Font.astro";
172-
export * from "${RUNTIME_VIRTUAL_MODULE_ID}";
173-
174-
export const getConfiguredImageService = _getConfiguredImageService;
171+
export { default as Font } from "astro/components/Font.astro";
172+
export * from "${RUNTIME_VIRTUAL_MODULE_ID}";
173+
174+
export const getConfiguredImageService = _getConfiguredImageService;
175175
176176
export const viteFSConfig = ${JSON.stringify(resolvedConfig.server.fs ?? {})};
177177

packages/astro/src/container/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ function createManifest(
176176
placement: undefined,
177177
},
178178
logLevel: 'silent',
179+
experimentalQueuedRendering: manifest?.experimentalQueuedRendering ?? {
180+
enabled: false,
181+
},
179182
};
180183
}
181184

@@ -266,6 +269,7 @@ type AstroContainerManifest = Pick<
266269
| 'serverLike'
267270
| 'assetsDir'
268271
| 'image'
272+
| 'experimentalQueuedRendering'
269273
>;
270274

271275
type AstroContainerConstructor = {

packages/astro/src/core/app/dev/pipeline.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import { type HeadElements, Pipeline, type TryRewriteResult } from '../../base-p
99
import { ASTRO_VERSION } from '../../constants.js';
1010
import { createModuleScriptElement, createStylesheetElementSet } from '../../render/ssr-element.js';
1111
import { findRouteToRewrite } from '../../routing/rewrite.js';
12+
import { newNodePool } from '../../../runtime/server/render/queue/pool.js';
13+
import { HTMLStringCache } from '../../../runtime/server/html-string-cache.js';
14+
import { queueRenderingEnabled } from '../manifest.js';
1215

1316
type DevPipelineCreate = Pick<NonRunnablePipeline, 'logger' | 'manifest' | 'streaming'>;
1417

@@ -45,6 +48,10 @@ export class NonRunnablePipeline extends Pipeline {
4548
undefined,
4649
undefined,
4750
);
51+
if (queueRenderingEnabled(manifest.experimentalQueuedRendering)) {
52+
pipeline.nodePool = newNodePool(manifest.experimentalQueuedRendering!);
53+
pipeline.htmlStringCache = new HTMLStringCache(1000); // Use default size
54+
}
4855
return pipeline;
4956
}
5057

packages/astro/src/core/app/manifest.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,18 @@ export function deserializeRouteInfo(rawRouteInfo: SerializedRouteInfo): RouteIn
129129
routeData: deserializeRouteData(rawRouteInfo.routeData),
130130
};
131131
}
132+
133+
export function queuePoolSize(
134+
config: NonNullable<SSRManifest['experimentalQueuedRendering']>,
135+
): number {
136+
return config?.poolSize ?? 1000;
137+
}
138+
export function queueContentCache(
139+
config: NonNullable<SSRManifest['experimentalQueuedRendering']>,
140+
): boolean {
141+
return config?.contentCache ?? false;
142+
}
143+
144+
export function queueRenderingEnabled(config: SSRManifest['experimentalQueuedRendering']): boolean {
145+
return config?.enabled ?? false;
146+
}

packages/astro/src/core/app/pipeline.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
import { getFallbackRoute, routeIsFallback, routeIsRedirect } from '../routing/helpers.js';
1313
import { findRouteToRewrite } from '../routing/rewrite.js';
1414
import { createConsoleLogger } from './logging.js';
15-
1615
export class AppPipeline extends Pipeline {
1716
getName(): string {
1817
return 'AppPipeline';

0 commit comments

Comments
 (0)