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

clients(lightrider): always get transferSize from X-TotalFetchedSize header #7478

Merged
merged 11 commits into from
Mar 14, 2019

Conversation

connorjclark
Copy link
Collaborator

@connorjclark connorjclark commented Mar 12, 2019

After inspecting the devtools log locally with the NS on and off, I'm mostly convinced that Chrome is right, and LH needs to be tweaked

The pattern dataLength=x,encodedDataLength=0 + dataLength=0,encodedDataLength=y only happens for resources that load in one chunk. And x = y only when the content is not compressed. It seems that if all the data for a resource comes in one chunk, the first "dataReceived" event always has 0 for encodedLength, but the second event will have the entire encoded length. If the resource is loaded in multiple chunks, the first dataReceived event has some of the total encoded length, the next won't have much or any, and the remaining unreported encoded length is on the last event. Inspecting the logic in the code linked in #3 seems to support this.

I think the behavior before could be considered incorrect, and the "bugged" version is what should be expected. An encodedDataLength of zero doesn't really make sense, but we were depending on it :) In other words, no issue with Chrome, let's fix LH.

Fixes internal bug b/123996856. Googlers, see internal changes cl/238109521.

@brendankenny
Copy link
Member

brendankenny commented Mar 12, 2019

even the comment in the PR itself is a better PR description than this one :P :P

fixed it

@connorjclark
Copy link
Collaborator Author

Didn't want to repeat myself ;)

@brendankenny brendankenny changed the title lightrider: always get transferSize from X-TotalFetchedSize header clients(lightrider): always get transferSize from X-TotalFetchedSize header Mar 12, 2019
Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

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

Seems fine to me! I'm not surprised that this is now buggy. This entire workaround was done hastily in response to devtools logs that had all 0 transfer size records without being able to reproduce or explained at the time by DT folks 😆

@patrickhulce
Copy link
Collaborator

It seems that if all the data for a resource comes in one chunk, the first "dataReceived" event always has 0 for encodedLength, but the second event will have the entire encoded length. If the resource is loaded in multiple chunks, the first dataReceived event has some of the total encoded length, the next won't have much or any, and the remaining unreported encoded length is on the last event

I had thought that in WRS loaders there was no incremental loading on the Chrome side and it always appeared to showup in one big chunk over the protocol, or at least that's what early devtools logs appeared to show, so if that wasn't true, it certainly explains the troubles we see now.

@connorjclark
Copy link
Collaborator Author

It seems that if all the data for a resource comes in one chunk, the first "dataReceived" event always has 0 for encodedLength, but the second event will have the entire encoded length. If the resource is loaded in multiple chunks, the first dataReceived event has some of the total encoded length, the next won't have much or any, and the remaining unreported encoded length is on the last event

I had thought that in WRS loaders there was no incremental loading on the Chrome side and it always appeared to showup in one big chunk over the protocol, or at least that's what early devtools logs appeared to show, so if that wasn't true, it certainly explains the troubles we see now.

That is true. Orthogonal to how many dataReceived events are seen is - whether or not encodedDataLength is set on them. The weird pattern of dataLength=x,encodedDataLength=0 + dataLength=0,encodedDataLength=y stuck out because of the guaranteed single-chunk loading in WRS, and looked like a weird bug at first, until realizing that a request with 0 encodedDataLength doesn't make much sense.

@paulirish
Copy link
Member

Copying from the internal thread...

Here's how I understand things..

"Correct" pattern (we see in desktop chrome regardless of NS) when data is received in one chunk:

  1. Network.responseReceived: ...
  2. Network.dataReceived: {dataLength:<allbytes>, encodedDataLength:0},
  3. Network.dataReceived: {dataLength:0, encodedDataLength:<gzippedbytes>},

Old WRS:

  1. Network.responseReceived {headers: {X-TotalFetchedSize: <gzippedbytes>}}
  2. Network.dataReceived: {dataLength:<allbytes>, encodedDataLength:0}, (encoded=0 IS A LIE)

ToT WRS:

  1. Network.responseReceived {headers: {X-TotalFetchedSize: <gzippedbytes>}}
  2. Network.dataReceived: {dataLength:<allbytes>, encodedDataLength:0},
  3. Network.dataReceived: {dataLength:0, encodedDataLength:<allbytes>}, (encoded=allbytes IS A LIE)

This seems correct to everyone?

if so...

Due to reasons, we can't have encodedDataLength populated correctly in WRS, so we have to accept a lie. So we have to handle the special situation here on the LH side.

ResponseReceived is received first and already has the full encodedDataLength.

Proposal: If this is Lightrider and we receive that header, then we use that as our transferSize and ignore all the encodedDataLength's.
That's roughly in line with your PR here but I'm wondering if we should set transferSize (via _updateTransferSizeForLightRiderIfNecessary) from inside of onResponseReceived() instead. wdyt?

@patrickhulce
Copy link
Collaborator

"Correct" pattern (we see in desktop chrome regardless of NS) when data is received in one chunk:

In practice I see, Network.loadingFinished being the actual source of all transfer size information the normal case. We may get those encodedDataLength events, but they always get overridden by loadingFinished. Otherwise that seems correct to me!

That's roughly in line with your PR here but I'm wondering if we should set transferSize (via _updateTransferSizeForLightRiderIfNecessary) from inside of onResponseReceived() instead. wdyt?

seems fine to me 👍

@paulirish
Copy link
Member

In practice I see, Network.loadingFinished being the actual source of all transfer size information the normal case.

Ah I see. loadingFinished's value should override the sum of dataReceived's. I guess we'd use the dataReceived ones if it was loadingFailed or some other weird situation. mkay.

Okay in that case, let's continue to set LR transferSize here in loadingFinished, but let's add a shitton of comments that explain this shit so we don't have to research it so hard next time.

YES? YES.

Copy link
Member

@paulirish paulirish left a comment

Choose a reason for hiding this comment

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

plz add so many more comments on the sequencing and where we expect the data

@connorjclark
Copy link
Collaborator Author

Here's how I understand things..
This seems correct to everyone?

Yupper

I'm wondering if we should set transferSize (via _updateTransferSizeForLightRiderIfNecessary) from inside of onResponseReceived() instead. wdyt?

_updateTransferSizeForLightRiderIfNecessary is currently called for onResponseReceived and onLoadingFinished. The order goes:

responseReceived
dataReceived
dataReceived
loadingFinished / loadingFailed

It seems we could remove the call to _updateTransferSizeForLightRiderIfNecessary in responseReceived and just have it in loadingFinished (and maybe loadingFailed too?). We could just have it in just responseReceived, but we'd also need to not accumulate the encodedDataLength as dataReceived events are processed in LR.

options as I see it;

  1. call _updateTransferSizeForLightRiderIfNecessary in loadingFinished / loadingFailed
  2. call _updateTransferSizeForLightRiderIfNecessary in responseReceived and never accumulate encodedDataLength on dataReceived in LR.

@brendankenny brendankenny self-requested a review March 13, 2019 20:39
@brendankenny
Copy link
Member

Can we keep the changes for lightrider limited to a single place? What happened to

and just have it in loadingFinished (and maybe loadingFailed too?).

Seems like the new behavior is the same as in 8406bc9 (disregard normal way of setting this.transferSize, use the header instead) if we just move the _updateTransferSizeForLightRiderIfNecessary call to finished/failed?

We should also get some tests in for this.

@patrickhulce
Copy link
Collaborator

I'd strongly prefer to just have _updateTransferSizeForLightRiderIfNecessary be called as frequently as necessary (loadingFinished/loadingFailed) then have extra logic for not accumulating on dataReceived :)

@connorjclark
Copy link
Collaborator Author

doneski :)

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.

just to make the testing more work, can we pick a site with fewer requests? :)

Or we can trim down to just the requests we care about? It seems fine to rely on this fixture just for checking this issue

@@ -246,6 +247,18 @@ module.exports = class NetworkRequest {

this.responseReceivedTime = timestamp;

this.responseHeadersText = response.headersText || '';
Copy link
Member

Choose a reason for hiding this comment

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

move these lines back?

// a value via the accumulation.
// In Lightrider, we do not have true value for encodedDataLength, and we get the actual size
// of the encoded data via a special response header. Because the values are totally bogus,
// we do no accumulation.
Copy link
Member

Choose a reason for hiding this comment

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

move comment down to _updateTransferSizeForLightrider?

@brendankenny
Copy link
Member

Or we can trim down to just the requests we care about?

like ...

json.filter(entry => {
  return entry.method.startsWith('Network') &&
    ['B38A026891F997E88601C20D85DC4616', '1000170085.13'].includes(entry.params.requestId);
});

?

:)

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.

it's a beauty

Copy link
Member

@paulirish paulirish left a comment

Choose a reason for hiding this comment

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

can you also leave a comment on L70 that says go read the _updateTransferSizeForLightrider comment?


// The total length of the encoded data is spread out among multiple events. The sum of the
// values in onResponseReceived and all the onDataReceived events will equal the value
// seen on the onLoadingFinished event. As we process onResonseReceived and onDataReceived
Copy link
Member

Choose a reason for hiding this comment

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

The sum of the
// values in onResponseReceived and all the onDataReceived events will equal the value
// seen on the onLoadingFinished event.

I don't know exactly what leads to a situation when the loadingFinished total is different than the sum of the parts, but apparently it does happen.

I just tried against 6 saved devtoolslogs i have. (Disclaimer: the artifacts were collected months ago definitely without networkservice, though I wouldn't expect it to matter)... There's, 320 network requests in total and nearly all of them have a sum that matches (or the sum was 0).. But I found 3 requests where this wasn't the case.

encodedSum encodedTotalInLoadingFinished url
108 12640 http://localhost:10200/dobetterweb/dbw_tester.html
139 144 http://localhost:10200/dobetterweb/empty_module.js?delay=500
650 714 https://s.amazon-adsystem.com/x/da2e6c890e6e3636

Again.. don't know what leads to this situation, but it happens. I'm happy with admitting that in the comment or we can ask caseq/network people about the circumstances..

Copy link
Collaborator Author

@connorjclark connorjclark Mar 13, 2019

Choose a reason for hiding this comment

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

ooh that is funky. With NS off, it's good. But on it was off by 5 for "empty_module?delay=500". And there was no dataReceived event!

// we accumulate the total encodedDataLength. When we process onLoadingFinished, we override
// the accumulated total. We do this so that if the request is aborted or fails, we still get
// a value via the accumulation.
// In Lightrider, we do not have true value for encodedDataLength, and we get the actual size
Copy link
Member

Choose a reason for hiding this comment

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

we do no accumulation.

Looks like we do, as of now.. We just override the transferSize in loadingFinished

Thank you VERY MUCH for writing all this shit out. These comments are so Good.

here's a very small rewrite of this second para for clarity:

In Lightrider, due to instrumentation limitations, our values for encodedDataLength are bogus and not valid. However the resource's true encodedDataLength/transferSize is shared via a special response header, X-TotalFetchedSize. In this situation, we read this value from responseReceived, use it for the transferSize and ignore the encodedDataLength values in both dataReceived and loadingFinished.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

oops thanks for the revisions ❤️

Copy link
Member

@paulirish paulirish left a comment

Choose a reason for hiding this comment

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

two nits

thanks for digging in on this!

* 'X-TotalFetchedSize' is the canonical transfer size in LR. Nothing should supersede it.
*
* The total length of the encoded data is spread out among multiple events. The sum of the
* values in onResponseReceived and all the onDataReceived events will equal the value
Copy link
Member

Choose a reason for hiding this comment

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

will equal => typically equals

and add

In <1% of cases we see the values differ.

_updateTransferSizeForLightRiderIfNecessary() {
// Bail if we're not in LightRider, this only applies there.
if (!global.isLightRider) return;
_updateTransferSizeForLightrider() {
Copy link
Member

Choose a reason for hiding this comment

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

can you also leave a comment on L70 that says go read the _updateTransferSizeForLightrider comment?

@paulirish paulirish merged commit b548452 into master Mar 14, 2019
@paulirish paulirish deleted the lr-transfersize branch March 14, 2019 00:26
Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

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

sorrrrrryy I'm late to the party

* The total length of the encoded data is spread out among multiple events. The sum of the
* values in onResponseReceived and all the onDataReceived events typically equals the value
* seen on the onLoadingFinished event. In <1% of cases we see the values differ. As we process
* onResonseReceived and onDataReceived we accumulate the total encodedDataLength. When we
Copy link
Collaborator

Choose a reason for hiding this comment

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

typo onResponseReceived

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

sent a pr

*
* The total length of the encoded data is spread out among multiple events. The sum of the
* values in onResponseReceived and all the onDataReceived events typically equals the value
* seen on the onLoadingFinished event. In <1% of cases we see the values differ. As we process
Copy link
Collaborator

Choose a reason for hiding this comment

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

fwiw in ~70% of cases in my local devtoolslog of a run I just did the final encodedDataLength differs from the sum of the events, not sure if I'm broken or the something else has changed :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

interesting. could you or paul share your test scripts / LH code diff / devtool logs? We should control for what logs we are using + NS + Chrome version so we know what's actually happening.

Copy link
Collaborator

Choose a reason for hiding this comment

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

oh I just looked at the last latest-run/defaultPass.devtoolslog.json I had, so it's probably fairly old

* seen on the onLoadingFinished event. In <1% of cases we see the values differ. As we process
* onResonseReceived and onDataReceived we accumulate the total encodedDataLength. When we
* process onLoadingFinished, we override the accumulated total. We do this so that if the
* request is aborted or fails, we still get a value via the accumulation.
Copy link
Collaborator

Choose a reason for hiding this comment

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

or fails

did we decide not to override for onLoadingFailed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

_updateTransferSizeForLightrider is being called on failed now, or did you mean something else?

Copy link
Collaborator

Choose a reason for hiding this comment

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

oh I just missed it and comment didn't mention it so I got confused, we're all good :)

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.

4 participants