Connectors: Stream connection status checks to avoid blocking page render#76546
Connectors: Stream connection status checks to avoid blocking page render#76546jorgefilipecosta wants to merge 2 commits into
Conversation
…nder The connectors page synchronously checks each provider's connection status during PHP page rendering, which blocks the entire page load when validation involves slow HTTP requests. Move the connection checks to a streaming handler hooked into admin_footer. The page is flushed to the browser first, allowing React to mount immediately with a "Checking…" state. Each provider's status is then streamed as an inline <script> tag that updates the UI progressively via CustomEvent + window.__connectorStatuses.
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
| } | ||
|
|
||
| try { | ||
| sleep( 3 ); // Simulate slow validation for demo purposes. |
There was a problem hiding this comment.
This is just for demo to simulate a slow check.
|
Size Change: 0 B Total Size: 8.75 MB ℹ️ View Unchanged
|
|
Flaky tests detected in 31d329b. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/23158785767
|
|
Thanks for tackling the slow page load — the problem is real. I'm wondering if we can take a simpler path until there is a proper way to validate connection status from the client. API keys are already validated server-side when saved — Could we apply the same logic to the connector cards? Remove the The only case this misses is a provider revoking a key externally, which seems like acceptable staleness for a settings page in the initial release given the performance considerations raised. |
|
Added a video demo to the PR description |
| // Flush all output buffers so the browser receives the full page | ||
| // (including React bootstrap scripts) immediately. | ||
| while ( ob_get_level() ) { | ||
| ob_end_flush(); | ||
| } | ||
| flush(); |
There was a problem hiding this comment.
This could conflict with the output buffer used for client-side media processing, which is now slated for 7.1.
Specifically, see WordPress/wordpress-develop#11324 and how wp_start_cross_origin_isolation_output_buffer() starts an output buffer via wp_set_up_cross_origin_isolation(). That said, it currently only added currently on these screens:
add_action( 'load-post.php', 'wp_set_up_cross_origin_isolation' );
add_action( 'load-post-new.php', 'wp_set_up_cross_origin_isolation' );
add_action( 'load-site-editor.php', 'wp_set_up_cross_origin_isolation' );
add_action( 'load-widgets.php', 'wp_set_up_cross_origin_isolation' );So while it doesn't seem it will be a problem in 7.0, and maybe it won't even be a problem in 7.1, I'm concerned it could cause compatibility problems elsewhere for perhaps other extensions that are output buffering the admin screens.
One possibility is to run this at the shutdown action. There is already the wp_ob_end_flush_all() function which runs at shutdown priority 1. So you could run the streaming of these scripts at shutdown priority 2 (or higher).
| printf( | ||
| '<script>' | ||
| . '(window.__connectorStatuses=window.__connectorStatuses||{})[%s]=%s;' | ||
| . 'document.dispatchEvent(new CustomEvent("connector-status",{detail:{id:%s,connected:%s}}));' | ||
| . '</script>', | ||
| wp_json_encode( $connector_id ), | ||
| $is_connected ? 'true' : 'false', | ||
| wp_json_encode( $connector_id ), | ||
| $is_connected ? 'true' : 'false' | ||
| ); |
There was a problem hiding this comment.
A couple important suggestions here:
- This needs to use
wp_print_inline_script_tag()so that CSPnonceattributes can be added. - Additional flags should be provide to ensure safe JSON encoding.
- Encoded params can be once instead of multiple times.
- The JS shouldn't be minified. We can use a NOWDOc to format this to improve maintainability.
- A
sourceURLcomment should be added.
| printf( | |
| '<script>' | |
| . '(window.__connectorStatuses=window.__connectorStatuses||{})[%s]=%s;' | |
| . 'document.dispatchEvent(new CustomEvent("connector-status",{detail:{id:%s,connected:%s}}));' | |
| . '</script>', | |
| wp_json_encode( $connector_id ), | |
| $is_connected ? 'true' : 'false', | |
| wp_json_encode( $connector_id ), | |
| $is_connected ? 'true' : 'false' | |
| ); | |
| $function = <<<'JS' | |
| ( id, connected ) => { | |
| window.__connectorStatuses = window.__connectorStatuses || {}; | |
| window.__connectorStatuses[ id ] = connected; | |
| const event = new CustomEvent( "connector-status", { detail: { id, connected } } ); | |
| document.dispatchEvent( event ); | |
| } | |
| JS; | |
| wp_print_inline_script_tag( | |
| sprintf( '( %s )( %s, %s );', | |
| $function, | |
| wp_json_encode( $connector_id, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), | |
| wp_json_encode( $is_connected, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) | |
| ) . "\n//# sourceURL=" . rawurlencode( __FUNCTION__ ) | |
| ); |
For reference for where this was recently done in core similarly:
In PhpStorm:
Before:
After:
|
This is a really solid UX improvement, would love to get this into "new beta 1" this Friday if you're able to help resolve latest feedback here @jorgefilipecosta 🤞🏼 |
|
Question: instead of attempting to stream the scripts for the providers' status, why not add REST API endpoints or abilities for these? This can be done asynchronously when the page loads. |
|
This PR was mostly done as an experiment we ended up not following. Sorry, I should have closed it sooner. The long-term solution is a REST API as @westonruter referred to, but we did not want to rush proposing a REST API given that there are plans for the AI client to have a REST API too. The AI connectors should just be relying on the AI client API. Instead of adding something permanently in REST, we tried this as a temporary solution, but the complexity it would add did not seem to be worth the enhancement. I think it is better to wait until the AI client has an API and then use it here. |
|
Removing the backport label. |
Summary
Checking if a provider isProviderConfigured involves HTTP calls and the time it takes depends on the provider. Currently this may make it look like the screen is broken because while the checks are in progress nothing is rendered as noticed by @ellatrix. In the future this checks will be done using the WP AI Client REST API, but untill then this PR tries to implement a solution suggested by @mcsf (with adaptations to the way WordPress works).
connectors-screen-streaming.mov
isProviderConfigured()connection checks out of the synchronous page render pathob_end_flush()/flush()in anadmin_footerhook), then each connector's status is streamed as an inline<script>tagwindow.__connectorStatusesand dispatches aCustomEvent, which the React UI picks up to update progressivelyHow it works
_gutenberg_get_connector_script_module_data()no longer callsisProviderConfigured()— the page renders instantly_gutenberg_stream_connector_statuses()hooks intoadmin_footer-{$hook_suffix}, flushes all output buffers, then loops through connectors doing the slow checks<script>tag is output and flushed, delivering the result to the browser incrementallyuseConnectorPlugininitializes fromwindow.__connectorStatuses(for results that arrived before React mounted) and listens forconnector-statusCustomEvent (for results that arrive after mount)Test plan
keySource