Skip to content

Commit 14fdd0e

Browse files
authored
[Flight] Serialize rate limited objects in console logs as marker an increase limit (#30294)
This marker can then be emitted as a getter. When this object gets accessed we use a special error to let the user know what is going on. <img width="1350" alt="Screenshot 2024-07-08 at 10 13 46 PM" src="https://github.com/facebook/react/assets/63648/e3eb698f-e02d-4394-a051-ba9ac3236480"> When you click the `...`: <img width="1357" alt="Screenshot 2024-07-08 at 10 13 56 PM" src="https://github.com/facebook/react/assets/63648/4b8ce1cf-d762-49a4-97b9-aeeb1aa8722c"> I also increased the object limit in console logs. It was arbitrarily set very low before. These limits are per message. So if you have a loop of many logs it can quickly add up a lot of strain on the server memory and the client. This is trying to find some tradeoff. Unfortunately we don't really do much deduping in these logs so with cyclic objects it ends up maximizing the limit and then siblings aren't logged. Ideally we should be able to lazy load them but that requires a lot of plumbing to wire up so if we can avoid it we should try to. If we want to that though one idea is to use the getter to do a sync XHR to load more data but the server needs to retain the objects in memory for an unknown amount of time. The client could maybe send a signal to retain them until a weakref clean up but even then it kind of needs a heartbeat to let the server know the client is still alive. That's a lot of complexity. There's probably more we can do to optimize deduping and other parts of the protocol to make it possible to have even higher limits.
1 parent 3b2e5f2 commit 14fdd0e

File tree

2 files changed

+30
-3
lines changed

2 files changed

+30
-3
lines changed

Diff for: packages/react-client/src/ReactFlightClient.js

+23
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,29 @@ function parseModelString(
12311231
}
12321232
// Fallthrough
12331233
}
1234+
case 'Y': {
1235+
if (__DEV__) {
1236+
// In DEV mode we encode omitted objects in logs as a getter that throws
1237+
// so that when you try to access it on the client, you know why that
1238+
// happened.
1239+
Object.defineProperty(parentObject, key, {
1240+
get: function () {
1241+
// We intentionally don't throw an error object here because it looks better
1242+
// without the stack in the console which isn't useful anyway.
1243+
// eslint-disable-next-line no-throw-literal
1244+
throw (
1245+
'This object has been omitted by React in the console log ' +
1246+
'to avoid sending too much data from the server. Try logging smaller ' +
1247+
'or more specific objects.'
1248+
);
1249+
},
1250+
enumerable: true,
1251+
configurable: false,
1252+
});
1253+
return null;
1254+
}
1255+
// Fallthrough
1256+
}
12341257
default: {
12351258
// We assume that anything else is a reference ID.
12361259
const ref = value.slice(1);

Diff for: packages/react-server/src/ReactFlightServer.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -1851,6 +1851,10 @@ function serializeSymbolReference(name: string): string {
18511851
return '$S' + name;
18521852
}
18531853

1854+
function serializeLimitedObject(): string {
1855+
return '$Y';
1856+
}
1857+
18541858
function serializeNumber(number: number): string | number {
18551859
if (Number.isFinite(number)) {
18561860
if (number === 0 && 1 / number === -Infinity) {
@@ -3181,10 +3185,10 @@ function renderConsoleValue(
31813185
}
31823186
}
31833187

3184-
if (counter.objectCount > 20) {
3188+
if (counter.objectCount > 500) {
31853189
// We've reached our max number of objects to serialize across the wire so we serialize this
3186-
// object but no properties inside of it, as a place holder.
3187-
return Array.isArray(value) ? [] : {};
3190+
// as a marker so that the client can error when this is accessed by the console.
3191+
return serializeLimitedObject();
31883192
}
31893193

31903194
counter.objectCount++;

0 commit comments

Comments
 (0)