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

Move networkRecords to a computed artifact #2062

Closed
wants to merge 22 commits into from
Closed

Conversation

paulirish
Copy link
Member

This PR moves networkRecords into a computed artifact. It's a little different than other ones though:

  • A number of gatherers rely on tracingData.networkRecords. As such, we continue to generate these at runtime, so they're available in afterPass. However after the gathering phase is done... we trash this tracingData.networkRecords data. Afterwards when auditing, the networkRecords are recomputed from the artifacts.devtoolsLogs which these days is collected and stored.

The primary motivation here is we want enable splitting up Gathering from Auditing. (So you could Gather on one machine and, later, Audit on another.) However, networkRecords contains circular references, so currently artifacts is not serializable. After this PR is merged, all our artifacts are serializable. \o/


This PR also has a breaking change regarding our ingesting of artifacts.performanceLog. It's now artifacts.devtoolsLogs and that must be an object (typically with a first key of defaultPass).


Reviewers: The w=1 url will help, as there's some decent whitespace changes in here.

There's also more changes we could consider in scope for this PR. (Reducing the double request... calls we have in a few audits now.) However, based on the # of lines this'll touch, I'd recommend I finish that up in a followup.

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.

sweet change, maybe needs a few tweaks for smokehouse though?

image

return artifacts.requestNetworkRecords(devtoolsLogs).then(networkRecords => {
return artifacts.requestNetworkThroughput(networkRecords).then(networkThroughput => {
let totalBytes = 0;
const results = networkRecords.reduce((prev, record) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

forEach ;)?

(tracingData.networkRecords[passName] = passData.networkRecords);
tracingData.devtoolsLogs[passName] = passData.devtoolsLog;

// DD SOMETHING WITH
Copy link
Collaborator

Choose a reason for hiding this comment

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

care to clarify?

readme.md Outdated
@@ -337,7 +337,8 @@ Lighthouse can be used to analyze trace and performance data collected from othe
"traces": {
"defaultPass": "/User/me/lighthouse/lighthouse-core/test/fixtures/traces/trace-user-timings.json"
},
"performanceLog": "/User/me/lighthouse/lighthouse-core/test/fixtures/traces/perflog.json"
"devtoolsLogs":
Copy link
Collaborator

Choose a reason for hiding this comment

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

this seems like invalid json, missing some {}?

@paulirish
Copy link
Member Author

@patrickhulce thanks for taking a look! updated now.

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.

a-ok by me

@@ -83,6 +83,7 @@ function runLighthouse(url, configPath, saveAssetsPath) {
}

runCount++;
console.log(`${log.dim}$ ${command} ${args.join(' ')} ${log.reset}`);
Copy link
Collaborator

Choose a reason for hiding this comment

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

is this in your separate PR now?

Copy link
Member Author

Choose a reason for hiding this comment

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

rebased to master so we're good here.

function walk(node, depth) {
const children = Object.keys(node);
const devtoolsLogs = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
return Promise.resolve().then(_ =>
Copy link
Collaborator

Choose a reason for hiding this comment

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

is there a reason to wrap this in a Promise.resolve? what can fail before we get into the .then of requestNetworkRecords?

Copy link
Member Author

Choose a reason for hiding this comment

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

no reason. nuked it.

});
const devtoolsLogs = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
return artifacts.requestNetworkRecords(devtoolsLogs).then(networkRecords => {
return artifacts.requestNetworkThroughput(networkRecords).then(networkThroughput =>
Copy link
Member

Choose a reason for hiding this comment

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

since computed artifacts can call each other now, do you want to make requestNetworkThroughput take devtoolsLogs directly now and have it create the networkRecords internally?

Copy link
Member Author

Choose a reason for hiding this comment

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

i do, yes! totally makes sense. in fact, i mentioned this at the end of the issue description :) ..

i'd do it in this PR but it'd add a lot more to the diff for no immediate benefit. I am suggesting doing that cleanup in a followup.

Copy link
Member

Choose a reason for hiding this comment

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

i do, yes! totally makes sense. in fact, i mentioned this at the end of the issue description :) ..

got it, didn't realize that's what that was referring to

Copy link
Member

Choose a reason for hiding this comment

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

but it'd add a lot more to the diff

although, would it? It would remove a lot of the lines just due to nesting added to this PR. Is the hard part network-throughput-test.js?

@benschwarz
Copy link
Contributor

Great! I think this will make custom audits that monitor the network easier too, right?

@paulirish
Copy link
Member Author

Great! I think this will make custom audits that monitor the network easier too, right?

Probably! However in an audit you can't really "monitor" as the requests have all finished by then. :)

@benschwarz
Copy link
Contributor

Probably! However in an audit you can't really "monitor" as the requests have all finished by then. :)

Indeed. Do you think it'd be possible to keep the request log in the same format that Network.responseReceived returns it in? Or does it do this already?

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.

I think there's an issue here: devtoolsLog only gets recorded when a trace is happening, so will be empty in all other passes. I think this is working with current default.js because we don't use non-DEFAULT_PASS networkRecords in any audits, only in gatherers, which get the network record whether or not devtoolsLog was created. If an audit comes along that does need the network records from another pass, I'm pretty sure artifacts.devtoolsLogs['other-pass'] will be an empty array.

Rambling ideas:

We could always do a devtoolsLog, but as I note inline, it is a little weird to tie that to begin/endTrace when we rely on it for network recording. I'm not sure how best to rationalize timing between trace and network begin and end. Size overhead of saved artifacts is going to get worse, too, though not by much (just additional saved Page domain stuff).

Maybe NetworkRecorder could keep a log of both devtools messages and the constructed network log, and then if there is no trace that pass, passData.devtoolsLog could be set to NetworkRecorder's version?

That also seems too roundabout, so maybe we should just always take a devtoolsLog, and then only keep it if config.recordTrace or config.recordNetwork was true? Would still need to figure out what starts and ends the logging. Maybe devtoolsLog creation and control should be moved out of driver and into gather-runner, responding to driver events? Makes a certain amount of sense, though we'd need a way to listen to all events based on domain, which node event emitters don't handle well...

@@ -42,7 +42,7 @@ class OffscreenImages extends Audit {
informative: true,
helpText: 'Images that are not above the fold should be lazily loaded after the page is ' +
'interactive. Consider using the [IntersectionObserver](https://developers.google.com/web/updates/2016/04/intersectionobserver) API.',
requiredArtifacts: ['ImageUsage', 'ViewportDimensions', 'traces', 'networkRecords']
requiredArtifacts: ['ImageUsage', 'ViewportDimensions', 'traces', 'devtoolsLogs']
Copy link
Member

Choose a reason for hiding this comment

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

this (and the old networkRecords) doesn't appear to be used?

Copy link
Collaborator

Choose a reason for hiding this comment

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

used by the shared audit they extend

Copy link
Member

Choose a reason for hiding this comment

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

oh, I see. const Audit = require('./byte-efficiency-audit'); makes that difficult to spot. We should probably rename at some point :)

@@ -46,7 +46,7 @@ class UsesOptimizedImages extends Audit {
'The following images could have smaller file sizes when compressed with ' +
'[WebP](https://developers.google.com/speed/webp/) or JPEG at 80 quality. ' +
'[Learn more about image optimization](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/image-optimization).',
requiredArtifacts: ['OptimizedImages', 'networkRecords']
requiredArtifacts: ['OptimizedImages', 'devtoolsLogs']
Copy link
Member

Choose a reason for hiding this comment

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

also unused?

Copy link
Collaborator

Choose a reason for hiding this comment

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

used by the shared audit they extend

@@ -39,7 +39,7 @@ class ResponsesAreCompressed extends Audit {
helpText: 'Text-based responses should be served with compression (gzip, deflate or brotli)' +
' to minimize total network bytes.' +
' [Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer).',
requiredArtifacts: ['ResponseCompression', 'networkRecords']
requiredArtifacts: ['ResponseCompression', 'devtoolsLogs']
Copy link
Member

Choose a reason for hiding this comment

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

unused?

Copy link
Collaborator

Choose a reason for hiding this comment

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

used by the shared audit they extend

@@ -44,7 +44,7 @@ class UsesResponsiveImages extends Audit {
'Image sizes served should be based on the device display size to save network bytes. ' +
'Learn more about [responsive images](https://developers.google.com/web/fundamentals/design-and-ui/media/images) ' +
'and [client hints](https://developers.google.com/web/updates/2015/09/automating-resource-selection-with-client-hints).',
requiredArtifacts: ['ImageUsage', 'ViewportDimensions', 'networkRecords']
requiredArtifacts: ['ImageUsage', 'ViewportDimensions', 'devtoolsLogs']
Copy link
Member

Choose a reason for hiding this comment

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

unused?

Copy link
Collaborator

Choose a reason for hiding this comment

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

used by the shared audit they extend

});
const devtoolsLogs = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
return artifacts.requestNetworkRecords(devtoolsLogs).then(networkRecords => {
return artifacts.requestNetworkThroughput(networkRecords).then(networkThroughput =>
Copy link
Member

Choose a reason for hiding this comment

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

but it'd add a lot more to the diff

although, would it? It would remove a lot of the lines just due to nesting added to this PR. Is the hard part network-throughput-test.js?

package.json Outdated
@@ -30,6 +30,7 @@
"cli-unit": "bash lighthouse-core/scripts/run-mocha.sh --cli",
"viewer-unit": "bash lighthouse-core/scripts/run-mocha.sh --viewer",
"unit": "bash lighthouse-core/scripts/run-mocha.sh --default",
"unit-core": "yarn core-unit",
Copy link
Member

Choose a reason for hiding this comment

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

lol, this is the worst. Just get rid of core-unit. I always forget the order on this as well

@@ -461,7 +474,7 @@ describe('Config', () => {
it('should merge other values', () => {
const artifacts = {
traces: {defaultPass: '../some/long/path'},
performanceLog: 'path/to/performance/log',
devtoolsLogs: {defaultPass: 'path/to/performance/log'},
Copy link
Member

Choose a reason for hiding this comment

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

'path/to/devtools/log'?

@@ -68,6 +68,9 @@ module.exports = {
recordsFromLogs(require('../fixtures/perflog.json'))
);
},
get devtoolsLog() {
return require('../fixtures/perflog.json');
Copy link
Member

Choose a reason for hiding this comment

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

can we move the file to be devtools-log.json or whatever? The two different terms are confusing :)

@@ -462,7 +464,7 @@ describe('Runner', () => {

// Verify a computed artifact. driverMock will include networkRecords
// built from fixtures/perflog.json.
const networkRecords = results.artifacts.networkRecords.firstPass;
const networkRecords = results.artifacts.networkRecords.firstPassDEPRECATED;
Copy link
Member

Choose a reason for hiding this comment

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

at least do this with requestNetworkRecords?


static recordsFromLogs(logs) {
Copy link
Member

Choose a reason for hiding this comment

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

looks like we don't have unit tests for this anywhere?

@patrickhulce
Copy link
Collaborator

this would also be great to land before #2023 so that I can add real trace/devtools log tests instead of hacky network records of today

@patrickhulce
Copy link
Collaborator

I'm pretty sure artifacts.devtoolsLogs['other-pass'] will be an empty array.

It totally will be, but I'll channel my inner Brendan here and say that we should not worry about that until we actually encounter that situation ;)

@patrickhulce
Copy link
Collaborator

I guess it would be nice to just nuke the recordNetwork flag in the pass config though so we don't get confused later 👍

@brendankenny
Copy link
Member

It totally will be, but I'll channel my inner Brendan here and say that we should not worry about that until we actually encounter that situation ;)

I see where you're going with that :)

I think in this case we are encountering it, though, since we already support custom configs, we just don't have any that record network but not trace in our tests (and to be fair, it's not likely anyone has written a config that hits this).

I'm hoping with a section on custom audits at I/O we can get more people writing them, though.

@paulirish
Copy link
Member Author

ptal

@brendankenny Regarding your large comment: Now we always record devtools log and place it on artifacts. There's no more relationship to recordTrace.

simplify devtoolsLog recording, url redirect tracking, and eliminate explicit network recording
@paulirish
Copy link
Member Author

with #2133 merged in here now... 🎉
brb while i resolve the conflicts.

@paulirish
Copy link
Member Author

manually merged to provide best control of the commits


commit 752b2b0
Author: Paul Irish [email protected]

merge fixup.

commit e5bd630
Merge: d7e4d1b af479e9
Author: Paul Irish [email protected]

Merge branch 'master' into netrecordspiecemeal

commit d7e4d1b
Author: Brendan Kenny [email protected]

always construct networkRecords from devtoolsLog (#2133)
simplify devtoolsLog recording, url redirect tracking, and eliminate explicit network recording

commit 75d8f4d
Author: Brendan Kenny [email protected]

fixup types

commit 11a1db3
Author: Paul Irish [email protected]

networkRecords => computed artifact.
generate networkRecords during gather via the networkRecorder dispatcher
breaking change: performanceLog => devtoolsLogs


we done with #2062, folks! holla.

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