From 0e43943834ac0541db55bc47b60a61f14099b9b2 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Sun, 1 Jun 2025 16:03:45 -0400 Subject: [PATCH 1/2] Include env in ReactAsyncInfo and ReactIOInfo This lets us keep track of which environment this was fetched and awaited. Currently the IO and await is in the same environment. Once we support forwarding information from a Promise fetched from another environment, the await can end up being in a different environment. --- .../react-client/src/ReactFlightClient.js | 4 +-- .../react-server/src/ReactFlightServer.js | 30 ++++++++++++++++++- .../ReactFlightAsyncDebugInfo-test.js | 18 +++++++---- packages/shared/ReactTypes.js | 2 ++ 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 9420cbcf9a8d9..67a98c7cb79ff 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -2758,9 +2758,7 @@ function resolveConsoleEntry( function initializeIOInfo(response: Response, ioInfo: ReactIOInfo): void { const env = - // TODO: Pass env through I/O info. - // ioInfo.env !== undefined ? ioInfo.env : - response._rootEnvironmentName; + ioInfo.env === undefined ? response._rootEnvironmentName : ioInfo.env; if (ioInfo.stack !== undefined) { initializeFakeTask(response, ioInfo, env); initializeFakeStack(response, ioInfo); diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 47ef1d42cc62e..11cb4128f929b 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -1930,10 +1930,14 @@ function visitAsyncNode( } // Outline the IO node. serializeIONode(request, ioNode); + // We log the environment at the time when the last promise pigned ping which may + // be later than what the environment was when we actually started awaiting. + const env = (0, request.environmentName)(); // Then emit a reference to us awaiting it in the current task. request.pendingChunks++; emitDebugChunk(request, task.id, { awaited: ((ioNode: any): ReactIOInfo), // This is deduped by this reference. + env: env, owner: node.owner, stack: stack, }); @@ -1969,8 +1973,12 @@ function emitAsyncSequence( } serializeIONode(request, awaitedNode); request.pendingChunks++; + // We log the environment at the time when we ping which may be later than what the + // environment was when we actually started awaiting. + const env = (0, request.environmentName)(); emitDebugChunk(request, task.id, { awaited: ((awaitedNode: any): ReactIOInfo), // This is deduped by this reference. + env: env, }); } } @@ -3524,6 +3532,7 @@ function emitIOInfoChunk( name: string, start: number, end: number, + env: ?string, owner: ?ReactComponentInfo, stack: ?ReactStackTrace, ): void { @@ -3563,6 +3572,10 @@ function emitIOInfoChunk( start: relativeStartTimestamp, end: relativeEndTimestamp, }; + if (env != null) { + // $FlowFixMe[cannot-write] + debugIOInfo.env = env; + } if (stack != null) { // $FlowFixMe[cannot-write] debugIOInfo.stack = stack; @@ -3597,6 +3610,7 @@ function outlineIOInfo(request: Request, ioInfo: ReactIOInfo): void { ioInfo.name, ioInfo.start, ioInfo.end, + ioInfo.env, owner, ioInfo.stack, ); @@ -3633,9 +3647,22 @@ function serializeIONode( outlineComponentInfo(request, owner); } + // We log the environment at the time when we serialize the I/O node. + // The environment name may have changed from when the I/O was actually started. + const env = (0, request.environmentName)(); + request.pendingChunks++; const id = request.nextChunkId++; - emitIOInfoChunk(request, id, name, ioNode.start, ioNode.end, owner, stack); + emitIOInfoChunk( + request, + id, + name, + ioNode.start, + ioNode.end, + env, + owner, + stack, + ); const ref = serializeByValueID(id); request.writtenObjects.set(ioNode, ref); return ref; @@ -4161,6 +4188,7 @@ function forwardDebugInfo( const debugAsyncInfo: Omit = { awaited: ioInfo, + env: debugInfo[i].env, owner: debugInfo[i].owner, stack: debugInfo[i].stack, }; diff --git a/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js b/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js index 72857556b4c10..68a9cdb3b1558 100644 --- a/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js +++ b/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js @@ -173,6 +173,7 @@ describe('ReactFlightAsyncDebugInfo', () => { { "awaited": { "end": 0, + "env": "Server", "name": "delay", "owner": { "env": "Server", @@ -219,6 +220,7 @@ describe('ReactFlightAsyncDebugInfo', () => { ], "start": 0, }, + "env": "Server", "owner": { "env": "Server", "key": null, @@ -258,6 +260,7 @@ describe('ReactFlightAsyncDebugInfo', () => { { "awaited": { "end": 0, + "env": "Server", "name": "delay", "owner": { "env": "Server", @@ -304,6 +307,7 @@ describe('ReactFlightAsyncDebugInfo', () => { ], "start": 0, }, + "env": "Server", "owner": { "env": "Server", "key": null, @@ -394,9 +398,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 364, + 368, 109, - 351, + 355, 67, ], ], @@ -404,6 +408,7 @@ describe('ReactFlightAsyncDebugInfo', () => { { "awaited": { "end": 0, + "env": "Server", "name": "setTimeout", "owner": { "env": "Server", @@ -415,9 +420,9 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Object.", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 364, + 368, 109, - 351, + 355, 67, ], ], @@ -426,14 +431,15 @@ describe('ReactFlightAsyncDebugInfo', () => { [ "Component", "/packages/react-server/src/__tests__/ReactFlightAsyncDebugInfo-test.js", - 354, + 358, 7, - 352, + 356, 5, ], ], "start": 0, }, + "env": "Server", }, { "time": 0, diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 6d4d2104022bc..b2b2f25fbaa93 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -234,6 +234,7 @@ export type ReactIOInfo = { +name: string, // the name of the async function being called (e.g. "fetch") +start: number, // the start time +end: number, // the end time (this might be different from the time the await was unblocked) + +env?: string, // the environment where this I/O was spawned. +owner?: null | ReactComponentInfo, +stack?: null | ReactStackTrace, // Stashed Data for the Specific Execution Environment. Not part of the transport protocol @@ -243,6 +244,7 @@ export type ReactIOInfo = { export type ReactAsyncInfo = { +awaited: ReactIOInfo, + +env?: string, // the environment where this was awaited. This might not be the same as where it was spawned. +owner?: null | ReactComponentInfo, +stack?: null | ReactStackTrace, // Stashed Data for the Specific Execution Environment. Not part of the transport protocol From 725448912d17de5d1329bae6ce47e848ab45a781 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Sun, 1 Jun 2025 16:27:36 -0400 Subject: [PATCH 2/2] Include env in performance track if different --- packages/react-client/src/ReactFlightClient.js | 2 +- .../react-client/src/ReactFlightPerformanceTrack.js | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 67a98c7cb79ff..38849f65b55b4 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -2769,7 +2769,7 @@ function initializeIOInfo(response: Response, ioInfo: ReactIOInfo): void { // $FlowFixMe[cannot-write] ioInfo.end += response._timeOrigin; - logIOInfo(ioInfo); + logIOInfo(ioInfo, response._rootEnvironmentName); } function resolveIOInfo( diff --git a/packages/react-client/src/ReactFlightPerformanceTrack.js b/packages/react-client/src/ReactFlightPerformanceTrack.js index 8284ac8b6e49a..7ee214ac22a21 100644 --- a/packages/react-client/src/ReactFlightPerformanceTrack.js +++ b/packages/react-client/src/ReactFlightPerformanceTrack.js @@ -224,11 +224,15 @@ function getIOColor( } } -export function logIOInfo(ioInfo: ReactIOInfo): void { +export function logIOInfo(ioInfo: ReactIOInfo, rootEnv: string): void { const startTime = ioInfo.start; const endTime = ioInfo.end; if (supportsUserTiming && endTime >= 0) { const name = ioInfo.name; + const env = ioInfo.env; + const isPrimaryEnv = env === rootEnv; + const entryName = + isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']'; const debugTask = ioInfo.debugTask; const color = getIOColor(name); if (__DEV__ && debugTask) { @@ -236,7 +240,7 @@ export function logIOInfo(ioInfo: ReactIOInfo): void { // $FlowFixMe[method-unbinding] console.timeStamp.bind( console, - name, + entryName, startTime < 0 ? 0 : startTime, endTime, IO_TRACK, @@ -246,7 +250,7 @@ export function logIOInfo(ioInfo: ReactIOInfo): void { ); } else { console.timeStamp( - name, + entryName, startTime < 0 ? 0 : startTime, endTime, IO_TRACK,