@samitouri / QOS-React-2 / commits / f6bc9c8243

[Fizz] Expose maxBoundarySize as an option (#21029)

* Expose maxBoundarySize as an option * Adjust the heuristic * Rename to progressiveChunkSize

Sebastian Markbåge committed Mar 19, 2021 at 12:41 UTC f6bc9c8243a02fc7a3a399845dabac3dc15dd7e7
4 files changed +47 -7
packages/react-dom/src/server/ReactDOMFizzServerBrowser.js
+6 -1
@@ -18,6 +18,7 @@ import {
18
19 type Options = {
20 signal?: AbortSignal,
21 + progressiveChunkSize?: number,
22 };
23
24 function renderToReadableStream(
@@ -35,7 +36,11 @@ function renderToReadableStream(
36 }
37 return new ReadableStream({
38 start(controller) {
38 - request = createRequest(children, controller);
39 + request = createRequest(
40 + children,
41 + controller,
42 + options ? options.progressiveChunkSize : undefined,
43 + );
44 startWork(request);
45 },
46 pull(controller) {
packages/react-dom/src/server/ReactDOMFizzServerNode.js
+10 -1
@@ -21,6 +21,10 @@ function createDrainHandler(destination, request) {
21 return () => startFlowing(request);
22 }
23
24 +type Options = {
25 + progressiveChunkSize?: number,
26 +};
27 +
28 type Controls = {
29 // Cancel any pending I/O and put anything remaining into
30 // client rendered mode.
@@ -30,8 +34,13 @@ type Controls = {
34 function pipeToNodeWritable(
35 children: ReactNodeList,
36 destination: Writable,
37 + options?: Options,
38 ): Controls {
34 - const request = createRequest(children, destination);
39 + const request = createRequest(
40 + children,
41 + destination,
42 + options ? options.progressiveChunkSize : undefined,
43 + );
44 let hasStartedFlowing = false;
45 startWork(request);
46 return {
packages/react-noop-renderer/src/ReactNoopServer.js
+10 -2
@@ -210,7 +210,11 @@ const ReactNoopServer = ReactFizzServer({
210 },
211 });
212
213 -function render(children: React$Element<any>): Destination {
213 +type Options = {
214 + progressiveChunkSize?: number,
215 +};
216 +
217 +function render(children: React$Element<any>, options?: Options): Destination {
218 const destination: Destination = {
219 root: null,
220 placeholders: new Map(),
@@ -220,7 +224,11 @@ function render(children: React$Element<any>): Destination {
224 ReactNoopServer.abort(request);
225 },
226 };
223 - const request = ReactNoopServer.createRequest(children, destination);
227 + const request = ReactNoopServer.createRequest(
228 + children,
229 + destination,
230 + options ? options.progressiveChunkSize : undefined,
231 + );
232 ReactNoopServer.startWork(request);
233 ReactNoopServer.startFlowing(request);
234 return destination;
packages/react-server/src/ReactFizzServer.js
+21 -3
@@ -99,7 +99,7 @@ const CLOSED = 2;
99 type Request = {
100 +destination: Destination,
101 +responseState: ResponseState,
102 - +maxBoundarySize: number,
102 + +progressiveChunkSize: number,
103 status: 0 | 1 | 2,
104 nextSegmentId: number,
105 allPendingWork: number, // when it reaches zero, we can close the connection.
@@ -113,16 +113,34 @@ type Request = {
113 partialBoundaries: Array<SuspenseBoundary>, // Partially completed boundaries that can flush its segments early.
114 };
115
116 +// This is a default heuristic for how to split up the HTML content into progressive
117 +// loading. Our goal is to be able to display additional new content about every 500ms.
118 +// Faster than that is unnecessary and should be throttled on the client. It also
119 +// adds unnecessary overhead to do more splits. We don't know if it's a higher or lower
120 +// end device but higher end suffer less from the overhead than lower end does from
121 +// not getting small enough pieces. We error on the side of low end.
122 +// We base this on low end 3G speeds which is about 500kbits per second. We assume
123 +// that there can be a reasonable drop off from max bandwidth which leaves you with
124 +// as little as 80%. We can receive half of that each 500ms - at best. In practice,
125 +// a little bandwidth is lost to processing and contention - e.g. CSS and images that
126 +// are downloaded along with the main content. So we estimate about half of that to be
127 +// the lower end throughput. In other words, we expect that you can at least show
128 +// about 12.5kb of content per 500ms. Not counting starting latency for the first
129 +// paint.
130 +// 500 * 1024 / 8 * .8 * 0.5 / 2
131 +const DEFAULT_PROGRESSIVE_CHUNK_SIZE = 12800;
132 +
133 export function createRequest(
134 children: ReactNodeList,
135 destination: Destination,
136 + progressiveChunkSize: number = DEFAULT_PROGRESSIVE_CHUNK_SIZE,
137 ): Request {
138 const pingedWork = [];
139 const abortSet: Set<SuspendedWork> = new Set();
140 const request = {
141 destination,
142 responseState: createResponseState(),
125 - maxBoundarySize: 1024,
143 + progressiveChunkSize,
144 status: BUFFERING,
145 nextSegmentId: 0,
146 allPendingWork: 0,
@@ -642,7 +660,7 @@ function flushSegment(
660 flushSubtree(request, destination, segment);
661
662 return writeEndSuspenseBoundary(destination);
645 - } else if (boundary.byteSize > request.maxBoundarySize) {
663 + } else if (boundary.byteSize > request.progressiveChunkSize) {
664 // This boundary is large and will be emitted separately so that we can progressively show
665 // other content. We add it to the queue during the flush because we have to ensure that
666 // the parent flushes first so that there's something to inject it into.