-
Notifications
You must be signed in to change notification settings - Fork 9.6k
core: refactor gather-runner #8964
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
Conversation
| if (loadData.trace) baseArtifacts.traces[passConfig.passName] = loadData.trace; | ||
|
|
||
| // If there were any load errors, treat all gatherers as if they errored. | ||
| const pageLoadError = GatherRunner.getPageLoadError(passContext, loadData); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there are a few ways #8340 could be incorporated here. One would be have GatherRunner.loadPage pass back non-fatal errors as it does in that PR and pass that error into GatherRunner.getPageLoadError here, and have the code in getPageLoadError decide if the error is severe enough, which error wins if there are multiple ones, etc
(where exactly this call should go is another question that we need to figure out in terms of what's reasonable to pass back in an errored run...e.g. it doesn't make much sense to save a trace of a security interstitial)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems reasonable to me
| await GatherRunner.beforePass(passContext, gathererResults); | ||
| await GatherRunner.pass(passContext, gathererResults); | ||
| const passResults = await GatherRunner.runPass(passContext); | ||
| Object.assign(artifacts, passResults.artifacts); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#8866 could be done here. GatherRunner.runPass returns the artifacts from the pass but also any errors encountered loading the page in that pass. If we want run() to break at this point (or after the isFirstPass check below), it's easy to do
patrickhulce
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh whoops I thought I flushed these already my b
|
|
||
| /** | ||
| * Class that drives browser to load the page and runs gatherer lifecycle hooks. | ||
| * Execution sequence when GatherRunner.run() is called: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❤️
| /** | ||
| * Reset browser state where needed and release the connection. | ||
| * @param {Driver} driver | ||
| * @param {{requestedUrl: string, settings: LH.Config.Settings}} options |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we actually need requestedUrl?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we actually need requestedUrl?
I was going to say no but apparently yes, for the driver.clearDataForOrigin call :)
| * @param {LH.Gatherer.PassContext} passContext | ||
| * @param {LH.Gatherer.LoadData} loadData | ||
| */ | ||
| static getPageLoadError(passContext, loadData) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 I like this distinction
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 I like this distinction
Yeah, same.
pageLoadError is also a natural name for a future error passed back from GatherRunner.loadPage and this pageLoadError could be something like passError, but I feel like we've all gotten kind of used to calling the pass-level error pageLoadError, so I'm not sure what we should do there (if anything)
| // TODO(bckenny): correct Partial<LH.GathererArtifacts> at this point to drop cast. | ||
| return /** @type {LH.Artifacts} */ ({...baseArtifacts, ...gathererArtifacts}); | ||
| return { | ||
| artifacts: gathererArtifacts, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
feels a bit weird that this isnt just returing the partial artifacts then...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh I see it's so later on it matches the signature of the other method :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
feels a bit weird that this isnt just returing the partial artifacts then...
oh I see it's so later on it matches the signature of the other method :)
(I know you're already well aware of all this, @patrickhulce, but for anyone else reading :)
it's not my favorite thing ever, but we really have three finished-pass states
- regular artifacts returned
- fatal exception thrown (e.g. javascript syntax error, LH logic error)
pageLoadError, when the page load failed but LH is doing fine (bad URL, bad cert, etc)
The first one is handled by the existing returned artifacts interface, the second is handled by normal exceptions, but the third we don't want to handle with exceptions because it's not really an exceptional state for Lighthouse.
That's been fine before because we can just handle the pageLoadError within a pass by erroring out all the artifacts from that pass, but now we want the error information higher up, so we need to pass back artifacts and the pageLoadError. There are other ways of doing this, but really we're just talking about a multi-value return, so let's just do a multi-value return :)
| settings: options.settings, | ||
| passConfig, | ||
| baseArtifacts, | ||
| // *pass() functions and gatherers can push to this warnings array. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can they not anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can they not anymore?
no change, they can, I just put the comments on the PassContext type rather than inline here since it's a little distracting from the run() logic and it's more helpful to gatherer authors than folks in this file (it's kind of helpful in this file, so I also added the /** @type {LH.Gatherer.PassContext} */ to the object literal so anyone using intellisense will get the comment on hover as well)
| }; | ||
|
|
||
| await driver.setThrottling(options.settings, passConfig); | ||
| if (!isFirstPass) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we just keep this on pass context so we don't lose this? it's such a small concession to keep our 300ms savings :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we just keep this on pass context so we don't lose this? it's such a small concession to keep our 300ms savings :)
so we caaaaan, and I admit I deleted it only because it was ugly :) but because we only wait for Page.frameNavigated after #6446, I consistently get ~10ms for this second load of about:blank in the Timing artifact.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I forgot I fixed that already lol, yeah that's fine then carryon :)
| if (loadData.trace) baseArtifacts.traces[passConfig.passName] = loadData.trace; | ||
|
|
||
| // If there were any load errors, treat all gatherers as if they errored. | ||
| const pageLoadError = GatherRunner.getPageLoadError(passContext, loadData); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems reasonable to me
patrickhulce
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems like an improvement to me!
I've got 2 gather-runner PRs open, so let's make like a lane closure and merge this fool 🚥 🚗
LGTM :)
| const baseArtifacts = await GatherRunner.getBaseArtifacts(options); | ||
| baseArtifacts.BenchmarkIndex = await options.driver.getBenchmarkIndex(); | ||
|
|
||
| await GatherRunner.setupDriver(driver, options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's now a good amount of time that isn't instrumented
Yes, though to be fair, if a timing is dropped and no one is observing it anywhere, does it really matter? :P
I'll try to get it covered and avoid that in this PR, though
| // So we first navigate to about:blank, then apply our emulation & setup | ||
| await GatherRunner.loadBlank(driver); | ||
|
|
||
| const baseArtifacts = await GatherRunner.getBaseArtifacts(options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename method now that we have populateBaseArtifacts?
initializeBaseArtifacts?

This is a refactor of
gather-runner.jstorun()(and nowrunPass()) to get an overview.GatherRunner.beforePass,pass, andafterPassnow only call those methods on gatherers and it's a lot easier to see where things like recording a trace starts and stops.Drivercommands and input/output should be exactly the same except:about:blanktwice on the first passlog.timegroups have differentDriverfunctions in them so the timing might change slightlyThe first is not a big deal since after core(driver): wait for Page.frameNavigated for about:blank #6446 the second
about:blankload is like 10ms, and thetimingchanges seem unavoidable since it's an explicit externalizing of internal implementation details.This is a big PR touching a lot of stuff, so if reviewers prefer this PR can just show the motivating goal and be closed and I can split into a couple of PRs.