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

Explore opportunities to use Optimization Detective to identify INP problems #1736

Open
westonruter opened this issue Dec 12, 2024 · 9 comments · May be fixed by #1759
Open

Explore opportunities to use Optimization Detective to identify INP problems #1736

westonruter opened this issue Dec 12, 2024 · 9 comments · May be fixed by #1759
Labels
[Plugin] Optimization Detective Issues for the Optimization Detective plugin [Type] Enhancement A suggestion for improvement of an existing feature

Comments

@westonruter
Copy link
Member

westonruter commented Dec 12, 2024

Optimization Detective is currently being used to optimize LCP via Image Prioritizer and CLS via Embed Optimizer. However, for the other Core Web Vital, INP, it is not currently doing anything. This is somewhat expected given that there isn't really an element which can be optimized for INP issues like there can be for LCP and CLS. Nevertheless, Optimization Detective does provide a foundation for at least tracking INP issues:

  1. Initially, Optimization Detective would send the URL Metric as soon as the page has fully loaded and gone idle, but now it sends the URL Metric when the page is being closed. This means that all of the INP/LoAF data collected during the life of the page will be available for adding to a URL Metric for storage.
  2. As of Preload image URLs for LCP elements with external background images #1697 any Optimization Detective extension modules now have direct access to the onINP function as provided by Web Vitals.
  3. Optimization Detective extensions are able to extend the URL Metric schema via the od_url_metric_schema_root_additional_properties filter to add a new root property for something like inpData.
  4. Optimization Detective by default only stores a maximum of 12 URL Metrics per page (3 for each of the 4 viewport groups). This may not be a large enough sample size for collecting enough INP/LoAF data. To this end, there is a od_url_metric_stored action which is fired whenever a new URL Metric is stored. An extension could use this to grab the INP data out of the URL Metric and store it persistently in another location so it is not garbage-collected when a new URL Metric is collected. For example, the OD_URL_Metric_Store_Request_Context object passed to the od_url_metric_stored action includes not only the OD_URL_Metric instance but also the post ID for the od_url_metrics post type that the URL Metric is being stored in. An INP-focused extension could use this to persist the INP data in postmeta for that post, like od_inp_data:
add_action(
	'od_url_metric_stored',
	static function ( OD_URL_Metric_Store_Request_Context $context ) {
		$inp_data = $context->url_metric->get( 'inpData' );
		if ( null === $inp_data ) {
			return;
		}
		// Note: $unique is false to allow for multiple postmeta to be stored with the same key.
		add_post_meta( $context->post_id, 'od_inp_data', $inp_data, false );
	}
);

We can then get all of the INP data with:

$all_data = get_post_meta( $post_id, 'od_inp_data', false );

The question then remains: with this data in hand, what do we do with it?

Assuming we have something on the page that causes a long task:

add_action( 'wp_footer', static function () {
	?>
	<script>
		function slowThing(event) {
			const now = performance.now();
			while ( performance.now() - now < 300 ) {
				continue;
			}
			event.target.textContent = 'Clicked!';
		}
	</script>
	<button type="button" onclick="slowThing(event);">Click Me</button>
	<?php
} );

The INP data provided by Web Vitals includes:

{
    "name": "INP",
    "value": 312,
    "rating": "needs-improvement",
    "delta": 296,
    "entries": [
        {
            "name": "pointerup",
            "entryType": "event",
            "startTime": 2794.7000000029802,
            "duration": 312,
            "interactionId": 5364,
            "processingStart": 2796.900000002235,
            "processingEnd": 2797,
            "cancelable": true
        },
        {
            "name": "click",
            "entryType": "event",
            "startTime": 2794.7000000029802,
            "duration": 312,
            "interactionId": 5364,
            "processingStart": 2797.10000000149,
            "processingEnd": 3098.300000000745,
            "cancelable": true
        }
    ],
    "id": "v4-1734029682811-7860789630775",
    "navigationType": "reload",
    "attribution": {
        "interactionTarget": "html>body.home.blog.logged-in.admin-bar.no-customize-support.wp-embed-responsive>button",
        "interactionTargetElement": {},
        "interactionType": "pointer",
        "interactionTime": 2794.7000000029802,
        "nextPaintTime": 3106.7000000029802,
        "processedEventEntries": [
            {
                "name": "pointerup",
                "entryType": "event",
                "startTime": 2794.7000000029802,
                "duration": 312,
                "interactionId": 5364,
                "processingStart": 2796.900000002235,
                "processingEnd": 2797,
                "cancelable": true
            },
            {
                "name": "mouseup",
                "entryType": "event",
                "startTime": 2794.7000000029802,
                "duration": 312,
                "interactionId": 0,
                "processingStart": 2797.10000000149,
                "processingEnd": 2797.10000000149,
                "cancelable": true
            },
            {
                "name": "click",
                "entryType": "event",
                "startTime": 2794.7000000029802,
                "duration": 312,
                "interactionId": 5364,
                "processingStart": 2797.10000000149,
                "processingEnd": 3098.300000000745,
                "cancelable": true
            }
        ],
        "longAnimationFrameEntries": [
            {
                "name": "long-animation-frame",
                "entryType": "long-animation-frame",
                "startTime": 2796.5,
                "duration": 302,
                "renderStart": 3098.800000000745,
                "styleAndLayoutStart": 3098.900000002235,
                "firstUIEventTimestamp": 2794.7000000029802,
                "blockingDuration": 252,
                "scripts": [
                    {
                        "name": "script",
                        "entryType": "script",
                        "startTime": 2797.900000002235,
                        "duration": 300,
                        "invoker": "BUTTON.onclick",
                        "invokerType": "event-listener",
                        "windowAttribution": "self",
                        "executionStart": 2797.900000002235,
                        "forcedStyleAndLayoutDuration": 0,
                        "pauseDuration": 0,
                        "sourceURL": "http://localhost:8888/",
                        "sourceFunctionName": "onclick",
                        "sourceCharPosition": 0
                    }
                ]
            }
        ],
        "inputDelay": 2.199999999254942,
        "processingDuration": 301.3999999985099,
        "presentationDelay": 8.400000002235174,
        "loadState": "complete"
    }
}

Do we capture these and just provide a UI to view them? Do we list the counts in the admin bar and show them when clicked? Do we highlight the elements on the page which are involved in slow interactions (if they are even present)? Or do we expose a dashboard for this information as explored in #1324?

Related: #1730

@westonruter westonruter added the [Plugin] Optimization Detective Issues for the Optimization Detective plugin label Dec 12, 2024
@github-project-automation github-project-automation bot moved this to Not Started/Backlog 📆 in WP Performance 2024 Dec 12, 2024
@westonruter westonruter added the [Type] Enhancement A suggestion for improvement of an existing feature label Dec 12, 2024
@swissspidy
Copy link
Member

Thanks for starting the discussion on this!

Thinking out loud a bit:

We probably won't have a dashboard like #1324 anytime soon and even then it is not necessarily the best way to expose INP info, at least not on its own.

Something that works on the frontend would be really cool. The admin bar came to mind as some performance or statistics plugins hook into that (e.g. Site Kit). And elements could be highlighted if they are visible.

This could be done in some sort of "debug mode" if you are logged in and visit a URL where INP info is available.

Assuming we have something on the page that causes a long task: [...]

If the relevant INP elements are present on the page, we could visually highlight it somehow (background color, outline, a glowing dot, etc.). We could even do that with the LCP elements too.

An admin bar menu item can show you the current collected field data, maybe even for all viewports.

It could show the relevant selectors for the affected elements for easier debugging, and then maybe just point to some documentation to help with debugging.

Bonus points for proper attribution of the plugin/theme/block responsible for that element.

Example of the Site Kit admin bar menu item:

Image

I feel like such a small admin bar integration would already be very helpful. Even for us working on Optimization Detective it could be useful to see the numbers right there on the page.

@swissspidy
Copy link
Member

Debug helpers could also surface data added in #1742

@westonruter
Copy link
Member Author

If the relevant INP elements are present on the page, we could visually highlight it somehow (background color, outline, a glowing dot, etc.). We could even do that with the LCP elements too.

I love this. Perhaps this would also be a great chance to use the anchor positioning API to add annotations with each element that is being highlighted, e.g. "LCP on mobile" or "Slow interaction target".

For highlighting elements involved in slow interactions, I suppose this could be implemented by injecting a STYLE into the page which simply has all of the interactionTarget selectors combined together in one rule with the desired style properties (e.g. outline). Maybe we should not show them by default but only when you opt-in to showing the INP issues when you click on the admin bar? There could be a button to show those annotations.

One complication for LCP would be the fact that the URL Metric stores the XPath of the elements and these can easily become invalid. For example, when the admin bar is shown it inserts the wpadminbar DIV at the beginning of the BODY. There is also a Customizer script that gets injected there too. The presence of these elements causes the indices of the XPaths to be shifted by 2. (Aside: I wonder if when we compute XPaths we could find a way to ignore these variations that happen at the beginning of the BODY.) Another complication is that as soon as the page is loaded, the XPath may no longer apply if JS moves elements around. So we may need to attach designated data attributes via tag visitors server-side.

But for INP since CSS selectors are what web-vitals.js exposes, then the issue should be minimized. Also, currently Optimization Detective isn't using the attribution build of web-vitals.js, since they haven't been needed. Maybe we should have a way to opt-in to the attribution build if an extension needs it.

@swissspidy
Copy link
Member

I started looking into that a bit today and try to come up with a prototype this week for further discussion.

For highlighting elements involved in slow interactions, I suppose this could be implemented by injecting a STYLE into the page

Yeah for simple outline or so that would be enough. However...

Perhaps this would also be a great chance to use the anchor positioning API to add annotations with each element that is being highlighted

...that means we'll need to add some extra element for a tooltip that we can position & anchor. For LCP elements I can do that server-side with a tag visitor, but for INP elements we only have a selector, so this would need to be done client-side I presume.

Maybe we should not show them by default but only when you opt-in to showing the INP issues when you click on the admin bar?

Yeah that was my thinking, or only if you enabled debug mode or so. We can experiment with that.

So we may need to attach designated data attributes via tag visitors server-side.

Annotating LCP elements with a tag visitor seems to be relatively trivial so far.

Currently Optimization Detective isn't using the attribution build of web-vitals.js, since they haven't been needed. Maybe we should have a way to opt-in to the attribution build if an extension needs it.

👍

@westonruter
Copy link
Member Author

I started looking into that a bit today and try to come up with a prototype this week for further discussion.

Do you have a repo for this?

@swissspidy
Copy link
Member

I was working directly in the optimization-detective plugin and will open a PR when it‘s more shareable. Then we can discuss where it should live :)

@westonruter
Copy link
Member Author

I do have three extension plugins which I have repos for under my account:

Originally I had thrown these up in gists but I moved them over to proper repos for collaboration. These don't seem like they would make sense as part of the Performance repo. But for an extension that helps detect INP issues, maybe it would?

@swissspidy
Copy link
Member

Oh I didn't know, thanks for sharing!

That could be an option for such a debug helper as well, though one could argue whether it should be part of OD itself (behind a flag or so).

@swissspidy swissspidy linked a pull request Dec 18, 2024 that will close this issue
@swissspidy
Copy link
Member

Haven't actually added the INP part yet, but I just opened #1759 with what I had in mind so far.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Plugin] Optimization Detective Issues for the Optimization Detective plugin [Type] Enhancement A suggestion for improvement of an existing feature
Projects
Status: Not Started/Backlog 📆
Development

Successfully merging a pull request may close this issue.

2 participants