[2.0 rc.15] frames: late-mounted consumers report a false ASYNC_WATERFALL for an already-streaming response
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 42/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- javascript, playwright, typescript, vite
Hướng nghiên cứu
Read the frame client’s landing() implementation and frame handler, then trace how request origin reaches root, nested, staged, and single-flight deliveries. Start with the inline Vite reproduction and compare the late-mounted case with the immediate control; add regression coverage for the other delivery paths and retries. Done means an already-streaming frame no longer reports ASYNC_WATERFALL, while genuinely new sequential work and retries remain attributed correctly.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Describe the bug
With server components enabled, a consumer of a server-function component reference that mounts late records ASYNC_WATERFALL for a frame response that was already streaming before the delay. There is only one request, and the content renders correctly. The finding is false: the frame delivery started before the preceding client wait resolved, not after it.
The frames client wraps the already-running delivery in a new landing promise, and that promise carries no flight origin, so attribution sees it as work that started when the consumer mounted. The repro below isolates this by acquiring the reference outside the reactive async graph, then mounting it through dynamic behind a 200 ms gate. The same page without the gate records no waterfall.
This only affects development diagnostics. The finding has info severity, so the default console doesn't print it; the repro subscribes to diagnostics to show it. It matters for tooling and tests that treat every diagnostic as a defect, where it shows up under slow mounting or CPU contention. Production rendering is unaffected.
Your Example Website or App
Minimal code is inline below (Vite + @solidjs/vite-plugin start mode, no other dependencies).
Steps to Reproduce the Bug or Issue
Create a Vite app with these three files and solid-js, @solidjs/web and @solidjs/vite-plugin installed (versions under Platform). Start mode needs no other entry files.
vite.config.ts:
import { defineConfig } from 'vite';
import solid from '@solidjs/vite-plugin';
export default defineConfig({
plugins: [solid({ start: true, ssr: true, serverFunctions: { components: true } })],
});
src/panel.tsx:
import { createMemo } from 'solid-js';
export async function serverPanel() {
'use server';
return function Panel() {
const text = createMemo(async () => {
await new Promise(resolve => setTimeout(resolve, 1000));
return 'Frame finished';
});
return <p>{text()}</p>;
};
}
src/App.tsx:
import { createSignal, createMemo, Show, Loading, OBSERVE } from 'solid-js';
import type { Component } from 'solid-js';
import { dynamic } from '@solidjs/web';
import { attribution } from 'solid-js/attribution';
import { serverPanel } from './panel';
if (typeof window !== 'undefined') {
attribution.enable({ log: false });
// Depth-two findings are informational and silent in the default console.
OBSERVE?.diagnostics.subscribe(event => {
if (event.code === 'ASYNC_WATERFALL') console.info(event.message);
});
}
export default function App() {
const [panel, setPanel] = createSignal<Component>();
async function start() {
// Acquire the reference outside a reactive async node, so request latency
// doesn't become an extra upstream flight in the immediate control.
const reference = await serverPanel();
setPanel(() => reference);
}
function Demo() {
// The server is already rendering its 1 s async body before this gate.
const ready = createMemo(() => new URLSearchParams(location.search).get('late') === '0'
? true : new Promise<true>(resolve => setTimeout(() => resolve(true), 200)));
const Panel = dynamic(() => panel());
return <Loading fallback={<p>Loading frame</p>}><Show when={ready()}><Mount /></Show></Loading>;
function Mount() {
return <Panel />;
}
}
return <main><button onClick={start}>Start</button><Show when={panel()}><Demo /></Show></main>;
}
- Run
vite, open/?late=1and open the browser console. - Click Start. The server begins its 1,000 ms async render immediately; the client mounts the consumer after its 200 ms gate, while the response is still streaming.
- Frame finished renders, and the console shows the
ASYNC_WATERFALLfinding below. The network panel shows a single frame request. - Control: open
/?late=0and click Start. The consumer mounts immediately and there is no finding.
Reproduced in 5 of 5 late runs, with 5 of 5 controls clean.
Expected behavior
A frame delivery that is already running should keep its request's flight origin when a new landing wrapper consumes it. Mounting the consumer later should not classify that delivery as new sequential async work, so no ASYNC_WATERFALL should be reported.
Actual (durations vary):
[ASYNC_WATERFALL] 2 sequential async flights — "ready" (201ms) → "computed" (789ms) — 989ms serialized: each began only after the previous resolved (as far as this graph can see). If a later request doesn't need the earlier response, derive both from the same inputs so they start together; if the dependency is intrinsic, preload the dependent data or join the requests server-side. If this work WAS already started elsewhere (a preloader or request cache), have that layer stamp its promises with attribution.markFlight() from "@solidjs/signals/attribution".
The frame is attributed 789 ms, although the server render takes 1,000 ms from the click: the request was in flight during the 200 ms gate.
Possible fix. Use the mechanism from #4108: capture the origin before the fetch, carry it through frame delivery, and call OBSERVE.attribution.markFlight(pending, startedAt) on the promise the landing memo returns. The origin should survive direct-root, nested, staged and single-flight deliveries, including commits delayed until later; retries should stay new work. The bookkeeping can be confined to dev/observe builds. This repro covers the ordinary root-delivery path; the other paths need their own regression tests. On next (d2ad8b2), landing() still returns an unstamped .then(() => value) wrapper and the frame handler doesn't propagate the request origin. I checked that by reading the source; I haven't run the repro against next.
Screenshots or Videos
(leave empty)
Platform
- OS: Linux x64
- Browser: Chromium 149 (headless, Playwright 1.61.1)
- Version:
solid-js,@solidjs/weband@solidjs/signals2.0.0-rc.15;@solidjs/vite-plugin3.0.0-next.50; Vite 8.3.3; Node 24.21.0
- Ngôn ngữ chính
- TypeScript
- Star
- 36.1k
- Fork
- 1.1k
- Merge trung bình
- 11 giờ 17 phút
- Pull request đã merge (30 ngày)
- 371
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của solidjs/solid
-
`lazy()`: remounting an already-loaded component renders empty (or the Suspense fallback) since 1.9.14Có thể đã có người làm @itisrmk đã nhận 5 ngày trước. Đang mở1.x
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
solidjs/solid#4182 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
enhancement
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
Maintainer thường phản hồi trong vòng 1 ngày
-
`createRoot`'s dispose function keeps `fn` alive, so `<For>`/`<Index>` rows retain removed list itemsCó thể đã có người làm @boombang đã nhận 7 ngày trước. Đang mở1.x
Độ khó 4/5 1-2 ngày Mức phù hợp với người mới 45/100
Maintainer thường phản hồi trong vòng 1 ngày
-
signals: a landing reveals a held write its stale reader re-derives onto a new flight (U-5, D-9)Đang mở
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 12/100
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của solidjs/solid
Issue tương tự
-
native
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
prime-radiant-inc/evener#4839 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug needs-triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
processone/fluux-messenger#1790 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
🛣️ Infrastructure
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
opencrvs/opencrvs-core#14232 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
kunchenguid/gh-axi#220 ·
Maintainer thường phản hồi trong vòng 1 ngày