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

core(image-elements): cap natural size fetch time #7274

Merged
merged 4 commits into from
Feb 26, 2019

Conversation

patrickhulce
Copy link
Collaborator

@patrickhulce patrickhulce commented Feb 19, 2019

Summary
Caps our time spent fetching natural size information in ImageElements gatherer.

Aside, this got me thinking that we really need artifact smoketests. The current logic is covered by existing smoketests, but it'd be a lot more comfortable to make changes to gatherers with lots of evaluateAsync if we could easily add gatherer smoketests.

Related Issues/PRs
closes #7273

if (/^image/.test(record.mimeType) && record.finished) {
// The network record is only valid for size information if it finished with a successful status
// code that indicates a complete resource response.
if (/^image/.test(record.mimeType) && record.finished && record.statusCode === 200) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this is technically the only fix explicitly required for #7273, but seemed like a good idea to address other potential situations too

@@ -130,6 +130,8 @@ class ImageElements extends Gatherer {
async fetchElementWithSizeInformation(driver, element) {
const url = JSON.stringify(element.src);
try {
// We don't want this to take forever, 250ms should be enough for images that are cached
driver.setNextProtocolTimeout(250);
Copy link
Member

Choose a reason for hiding this comment

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

one interesting aspect of our protocol timeouts is the effect of a bunch of orphaned protocol commands still running in the page. In this case there's also the Runtime.evaluate timeout, so they're at least somewhat cut off from further action, but what was being slow here, re-fetching images to get size info?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Correct loading the image to get the size info was taking a long time because it was reissuing the request.

// Additional fetch is expensive; don't bother if we don't have a networkRecord for the image.
if ((element.isPicture || element.isCss) && networkRecord) {
// Additional fetch is expensive; don't bother if we don't have a networkRecord for the image,
// or it's not in the top 50 largest images.
Copy link
Member

Choose a reason for hiding this comment

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

maybe we should include in the docs for the artifact that picture or css images don't have size info if they aren't in the top 50 largest images?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

sure

(element.isPicture || element.isCss) &&
networkRecord &&
top50Images.includes(networkRecord)
) {
Copy link
Member

Choose a reason for hiding this comment

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

for the else case of this conditional, it seems like we need to zero out naturalWidth/naturalHeight for element.isPicture || element.isCss like fetchElementWithSizeInformation does since they won't be good values to trust otherwise?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'll just convert the default to be these since the goal is to set every image to 0 or the real value anyhow.

@@ -163,6 +167,9 @@ class ImageElements extends Gatherer {
const elements = await driver.evaluateAsync(expression);

const imageUsage = [];
const top50Images = Object.values(indexedNetworkRecords)
.sort((a, b) => b.resourceSize - a.resourceSize)
Copy link
Member

Choose a reason for hiding this comment

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

should this be using the transferSize fallback like below?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

IMO, no. That fallback is for making sure we don't miss the super rare case of an image being GZIPped in our final savings estimate but has negligible effect on whether it counts in the top 50.

@connorjclark
Copy link
Collaborator

connorjclark commented Feb 20, 2019

Instead of doing fetchElementWithSizeInformation one element at a time, could we do it all at once? I'm wondering how much overhead there is in the protocol back and forth.

We could also then set an overall timeout for the entire fetchElementWithSizeInformation step, exiting with the info gathered so far. ATM this gatherer could still timeout the run (in LR), if it has enough images. (this may be of little concern though, if we decide to implement gatherer-level timeouts)

@patrickhulce
Copy link
Collaborator Author

ATM this gatherer could still timeout the run (in LR), if it has enough images.

How so? Or is this assuming the worst case imagineable where all 50 images need fetching and the gatherer takes 12.5 seconds.

Instead of doing fetchElementWithSizeInformation one element at a time, could we do it all at once? I'm wondering how much overhead there is in the protocol back and forth.

We certainly could, just somewhat complicates things a bit and the idea of trying to load 50 images all at once if things go poorly means scares me a bit. I'd vote to avoid doing it until we have a compelling example case for it.

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

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

one last test request, but otherwise LGTM!

lighthouse-core/gather/driver.js Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Stuck in ImageElements gatherer
3 participants