Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Flight] Split Streaming from Relay Implemenation #18260

Merged
merged 5 commits into from
Mar 10, 2020

Conversation

sebmarkbage
Copy link
Collaborator

@sebmarkbage sebmarkbage commented Mar 10, 2020

This splits the Flight implementation into a general JSON chunk part and a streaming part. The default implementation goes to a binary/string stream API which varies between Node and W3C streams. The Relay implementation by-passes the serialization and instead gets passed JSON-serializable object chunks.

The server configs are layered like this:

  • ReactFlightServer
    • ReactFlightServerConfig
      • ReactFlightServerConfigStream
        • ReactServerStreamConfig
          • ReactServerStreamConfigNode
          • ReactServerStreamConfigBrowser
      • ReactFlightDOMRelayServerHostConfig

The client is a bit simpler. The implementation just imports ReactFlightClient (used by Relay) or ReactFlightClientStream (used by Streams).

Flight Relay Server Bindings

The API for the server is:

function render(model: ReactModel, destination: Destination): void;

The first argument is something that can be returned from a Block. I.e. the root model. Something JSONable. This will change to require a Block. The second argument is whatever Relay wants to the target to be.

This also depends on an external file that we'll have to add to www: ReactFlightDOMRelayServerIntegration.js. This will needs to implement the following API:

 opaque type Destination;
 function emitModel(
    destination: Destination,
    id: number,
    json: JSONValue,
  ): void;
 function emitError(
    destination: Destination,
    id: number,
    message: string,
    stack: string,
  ): void;
function close(destination: Destination): void;

This will be called asynchronously as ReactFlight completes part of the model. Once there's no more to render, it'll call close() on the destination. Relay can then serialize these rows as it wants.

Flight Relay Client Bindings

The client API is:

function createResponse(): Response;
function getModelRoot<T>(response: Response): {model: T};
function resolveModel(
  response: Response,
  id: number,
  json: JSONValue,
): void;
function resolveError(
  response: Response,
  id: number,
  message: string,
  stack: string,
): void;
function close(response: Response): void;

To use this, you first have to create a "response". You can get a model out of the response which will just have placeholders in it. This particular API will change as I get rid of "proxies". It’ll return a Block instead. The principle is that it can return something even before any data has loaded so you immediately get something that can suspend.

The resolveModel and resolveError are called as new rows stream in. close should be called when there's no more rows or the connection dies (even if it dies early).

This just forwards to the stream version of Flight which is itself forked
between Node and W3C streams.

The dom-relay goes directly to the Relay config though which allows it to
avoid the stream part of Flight.
This decouples it so that the Relay implementation doesn't have to encode
the JSON to strings. Instead it can be fed the values as JSON objects and
do its own encoding.
@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Mar 10, 2020
@codesandbox-ci
Copy link

codesandbox-ci bot commented Mar 10, 2020

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit b906235:

Sandbox Source
sad-wing-x276n Configuration

@sizebot
Copy link

sizebot commented Mar 10, 2020

Details of bundled changes.

Comparing: bdc5cc4...b906235

react-flight-dom-relay

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactFlightDOMRelayServer-dev.js +1.0% -7.2% 7.85 KB 7.93 KB 2.56 KB 2.38 KB FB_WWW_DEV
ReactFlightDOMRelayServer-prod.js 🔺+14.6% 🔺+7.8% 4.96 KB 5.68 KB 1.54 KB 1.66 KB FB_WWW_PROD
ReactFlightDOMRelayClient-dev.js -10.1% -11.6% 5.41 KB 4.87 KB 1.75 KB 1.54 KB FB_WWW_DEV
ReactFlightDOMRelayClient-prod.js -26.5% -18.2% 4.02 KB 2.96 KB 1.29 KB 1.05 KB FB_WWW_PROD

react-flight-dom-webpack

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-flight-dom-webpack-server.browser.development.js -5.2% -8.7% 8.31 KB 7.87 KB 2.69 KB 2.45 KB NODE_DEV
react-flight-dom-webpack-server.browser.production.min.js 🔺+0.5% 🔺+0.8% 2.59 KB 2.6 KB 1.22 KB 1.23 KB NODE_PROD
react-flight-dom-webpack.development.js +2.2% +0.9% 8.55 KB 8.74 KB 2.36 KB 2.38 KB UMD_DEV
react-flight-dom-webpack.production.min.js 🔺+1.0% -0.2% 2.85 KB 2.88 KB 1.32 KB 1.32 KB UMD_PROD
react-flight-dom-webpack.development.js +2.3% +0.6% 7.81 KB 7.99 KB 2.26 KB 2.27 KB NODE_DEV
react-flight-dom-webpack.production.min.js 🔺+1.1% 🔺+0.2% 2.67 KB 2.7 KB 1.23 KB 1.24 KB NODE_PROD
react-flight-dom-webpack-server.node.development.js -4.7% -7.9% 9.25 KB 8.81 KB 2.96 KB 2.72 KB NODE_DEV
react-flight-dom-webpack-server.node.production.min.js 🔺+0.4% 🔺+0.4% 2.75 KB 2.76 KB 1.24 KB 1.24 KB NODE_PROD
react-flight-dom-webpack-server.browser.development.js -5.4% -8.7% 9.13 KB 8.63 KB 2.81 KB 2.56 KB UMD_DEV
react-flight-dom-webpack-server.browser.production.min.js 🔺+0.4% 🔺+0.6% 2.81 KB 2.82 KB 1.31 KB 1.32 KB UMD_PROD

react-server

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-server-flight.production.min.js 🔺+1.4% 🔺+1.7% 2.78 KB 2.82 KB 1.25 KB 1.27 KB NODE_PROD
react-server-flight.development.js -4.2% -8.4% 9.15 KB 8.77 KB 2.79 KB 2.55 KB NODE_DEV
react-server.development.js 0.0% +0.1% 3.68 KB 3.68 KB 1.23 KB 1.23 KB NODE_DEV
react-server.production.min.js 0.0% 🔺+0.3% 1.14 KB 1.14 KB 634 B 636 B NODE_PROD

Size changes (stable)

Generated by 🚫 dangerJS against b906235

@sizebot
Copy link

sizebot commented Mar 10, 2020

Details of bundled changes.

Comparing: bdc5cc4...b906235

react-flight-dom-relay

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactFlightDOMRelayClient-dev.js -10.1% -11.6% 5.41 KB 4.87 KB 1.75 KB 1.54 KB FB_WWW_DEV
ReactFlightDOMRelayClient-prod.js -26.5% -18.2% 4.02 KB 2.96 KB 1.29 KB 1.05 KB FB_WWW_PROD
ReactFlightDOMRelayServer-dev.js +1.0% -7.2% 7.85 KB 7.93 KB 2.56 KB 2.38 KB FB_WWW_DEV
ReactFlightDOMRelayServer-prod.js 🔺+14.6% 🔺+7.8% 4.96 KB 5.68 KB 1.54 KB 1.66 KB FB_WWW_PROD

react-flight-dom-webpack

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-flight-dom-webpack-server.node.development.js -4.7% -7.9% 9.26 KB 8.83 KB 2.96 KB 2.73 KB NODE_DEV
react-flight-dom-webpack-server.node.production.min.js 🔺+0.4% 🔺+0.4% 2.76 KB 2.77 KB 1.25 KB 1.25 KB NODE_PROD
react-flight-dom-webpack-server.browser.development.js -5.4% -8.6% 9.14 KB 8.64 KB 2.81 KB 2.57 KB UMD_DEV
react-flight-dom-webpack-server.browser.production.min.js 🔺+0.4% 🔺+0.6% 2.82 KB 2.83 KB 1.32 KB 1.32 KB UMD_PROD
react-flight-dom-webpack-server.browser.development.js -5.2% -8.7% 8.32 KB 7.88 KB 2.69 KB 2.46 KB NODE_DEV
react-flight-dom-webpack-server.browser.production.min.js 🔺+0.5% 🔺+0.8% 2.6 KB 2.61 KB 1.23 KB 1.24 KB NODE_PROD
react-flight-dom-webpack.development.js +2.2% +0.9% 8.57 KB 8.76 KB 2.37 KB 2.39 KB UMD_DEV
react-flight-dom-webpack.production.min.js 🔺+1.0% -0.2% 2.86 KB 2.89 KB 1.33 KB 1.33 KB UMD_PROD
react-flight-dom-webpack.development.js +2.3% +0.6% 7.82 KB 8 KB 2.27 KB 2.28 KB NODE_DEV
react-flight-dom-webpack.production.min.js 🔺+1.1% 🔺+0.4% 2.68 KB 2.71 KB 1.24 KB 1.25 KB NODE_PROD

react-server

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-server-flight.development.js -4.2% -8.4% 9.17 KB 8.78 KB 2.79 KB 2.56 KB NODE_DEV
react-server-flight.production.min.js 🔺+1.4% 🔺+1.7% 2.8 KB 2.84 KB 1.26 KB 1.28 KB NODE_PROD
react-server.development.js 0.0% +0.1% 3.69 KB 3.69 KB 1.24 KB 1.24 KB NODE_DEV
react-server.production.min.js 0.0% 🔺+0.3% 1.15 KB 1.15 KB 642 B 644 B NODE_PROD

Size changes (experimental)

Generated by 🚫 dangerJS against b906235

@sebmarkbage sebmarkbage force-pushed the relay-flight-refactor branch from 9686ab4 to 5a5fae4 Compare March 10, 2020 06:13
This requires an external helper file that we'll wire up internally.
@sebmarkbage sebmarkbage force-pushed the relay-flight-refactor branch from 5a5fae4 to b906235 Compare March 10, 2020 06:30
let response = createResponse(data);
for (let i = 0; i < data.length; i++) {
processStringChunk(response, data[i], 0);
function parseModel(response, targetObj, key, value) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key seems unused?

@josephsavona
Copy link
Contributor

The API for the server is:
function render(model: ReactModel, destination: Destination): void;
The first argument is something that can be returned from a Block. I.e. the root model

To clarify, this is the result of calling a Block's load function, right? So it's the curried Block Query function plus its arguments.

@sebmarkbage
Copy link
Collaborator Author

To clarify, this is the result of calling a Block's load function, right? So it's the curried Block Query function plus its arguments.

Yea, that's correct. We still need a name for that. 😂

@josephsavona
Copy link
Contributor

We still need a name for that.

Thunk?

@josephsavona
Copy link
Contributor

Sorry for multiple rounds of questions, but who calls the client methods like resolveModel? Is that all orchestrated by Flight?

Copy link
Contributor

@josephsavona josephsavona left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This layering makes sense, thanks for abstracting out the representation.

}
complete(response);
return getModelRoot(response);
return parseModelFromJSON(response, targetObj, key, value);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"key" is used here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦‍♂

@sebmarkbage sebmarkbage merged commit 99d7371 into facebook:master Mar 10, 2020
@sebmarkbage
Copy link
Collaborator Author

sebmarkbage commented Mar 10, 2020

@josephsavona No. Relay calls resolveModel and resolveError when it receives that "chunk" from the server. So you encode model and error however you want on the server and then parse it on the client and feed it back into Flight.

ID = 0 is special. It's the root and is always emitted first. So you could for example inline that in the response and then stream in the rest.

@josephsavona
Copy link
Contributor

Ah got it, so inverse of the server. Cool cool.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants