-
Notifications
You must be signed in to change notification settings - Fork 9.4k
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: refactor gather-runner #8964
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
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.
❤️
* @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 :)
url: options.requestedUrl, | ||
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)
LighthouseRunWarnings: baseArtifacts.LighthouseRunWarnings, | ||
}; | ||
|
||
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
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 :)
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
// In the devtools/extension case, we can't still be on the site while trying to clear state | ||
// 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.js
torun()
(and nowrunPass()
) to get an overview.GatherRunner.beforePass
,pass
, andafterPass
now only call those methods on gatherers and it's a lot easier to see where things like recording a trace starts and stops.Driver
commands and input/output should be exactly the same except:about:blank
twice on the first passlog.time
groups have differentDriver
functions 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:blank
load is like 10ms, and thetiming
changes 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.