-
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: add timeout to all protocol commands #6347
Conversation
} | ||
|
||
const value = response.result.value; | ||
async _evaluateInContext(expression, contextId) { |
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.
VSCode suggested this could be async, so I let it :) Thoughts on changing asynchronous syntax? FWIW, V8 just improved the performance of await
to match Promises.
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.
Thoughts on changing asynchronous syntax?
Totally fine. Since we adopted Node 8 as the base requirement we've mostly just followed the rule of only changing from Promises to async/await when we're touching the code for some other reason (see initial discussion in #3742), but to convert (or not) at will if that's the case.
We have some tricky uses of promises (see #6343 :) that need real review, which mass changes make difficult, especially since the indentation level changes and the git diffs end up not always the best, even when ignoring whitespace. We (I?) also end up using git blame a lot, and random changes in unrelated PRs make that annoying :)
It's definitely a fuzzy line, though, and sometimes drive by fixes are great.
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.
(This case is totally different, though, because it's fundamentally changing anyways, so it's a prime candidate for fixing.)
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.
so simple and clean I love it nice work!
I am slightly worried about the commands that are going to come out of the woodwork failing though. I would want to trying running LH on some crappier hardware on some gnarly URLs (@paulirish has a ton of phones :)) and find the longest commands we have right now.
lighthouse-core/gather/driver.js
Outdated
}); | ||
}); | ||
// Encoding issues may lead to hanging getResponseBody calls: https://github.com/GoogleChrome/lighthouse/pull/4718 | ||
// driver.sendCommand will handle timeout after 5s. |
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 keep this at 1s? expanding to 5 could dramatically slow down the failure case when 3 of these go wrong :/
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.
ok, I put it back to 1s
lighthouse-core/gather/driver.js
Outdated
* @param {number} timeout | ||
*/ | ||
setProtocolTimeout(timeout) { | ||
this._protocolTimeout = timeout; |
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 afraid we'll need to make timeout a setting on sendCommand
with a global default :/
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.
We can move to that if it's really needed. It could complicate usage quite a bit, that + the unique typing on the method, I think the best option would be this:
/**
* Call protocol methods.
* @template {keyof LH.CrdpCommands} C
* @param {C|{method: C, timeout: number}} methodOrOpts
* @param {LH.CrdpCommands[C]['paramsType']} params,
* @return {Promise<LH.CrdpCommands[C]['returnType']>}
*/
sendCommand(methodOrOpts, ...params) {
const {method, timeout} = typeof methodOrOpts === 'object' ? methodOrOpts : {
method: methodOrOpts,
timeout: DEFAULT_PROTOCOL_TIMEOUT
}
}
That way, the calling code for most use cases can remain the same.
proto/lighthouse-result.proto
Outdated
// Used when security error prevents page load. | ||
INSECURE_DOCUMENT_REQUEST = 17; | ||
// Used when security error prevents page load. | ||
PROTOCOL_TIMEOUT = 18; |
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 removed REQUEST_CONTENT_TIMEOUT
... instead of making a new enum value for PROTOCOL_TIMEOUT
, could we just reuse REQUEST_CONTENT_TIMEOUT
's enum (rename it)? Would that be OK for proto compat?
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.
nope, can't rename.
But actually looks like we never exposed REQUEST_CONTENT_TIMEOUT in the proto. So I think we're OK. :)
clearTimeout(asyncTimeout); | ||
reject(err); | ||
}); | ||
}); |
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.
maybe drop keep the promise just to the thing that has to be a promise? e.g.
return new Promise(async (resolve, reject) => {
const asyncTimeout = setTimeout((_ => {
const err = new LHError(LHError.errors.PROTOCOL_TIMEOUT);
err.message += ` Method: ${method}`;
reject(err);
}), this._protocolTimeout);
try {
const result = await this._connection.sendCommand(method, ...params);
clearTimeout(asyncTimeout);
resolve(result);
} catch (err) {
clearTimeout(asyncTimeout);
reject(err);
}
});
ok, options:
other options? (1) worries me about exposing complexity for the 5% case that may be with us forever (see: every js API that takes as its first parameter a string, or if it's a number it acts like this, or if it's an object it's an options object, or if it's a stream it...). OTOH it is overhead when writing code, but is fairly self explanatory when reading it, and I take @hoten's point that it's basically equivalent to (2) |
lhrRuntimeError: true, | ||
}, | ||
|
||
// Hey! When adding a new error type, update lighthouse-result.proto too. |
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.
❤️
@patrickhulce I'll test it out on some slower devices today. |
Tested on an old Nexus 4 device. Didn't come across any protocol timeouts. |
Got some timings. https://gist.github.com/Hoten/de38179b2d941b6d195c3c4383d0bd8d
|
Yup looks like it's the font-size gatherer: lighthouse/lighthouse-core/gather/gatherers/seo/font-size.js Lines 216 to 220 in 879141d
I was hoping it was the TBH I think that situation is OK. I don't really want the font-size gatherer taking >5s. |
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.
LGTM!
thanks so much for jumping on that slow device timeout investigation @hoten! sorry I spaced out hearing the first half of your update 😳
@@ -240,23 +248,48 @@ class Driver { | |||
} | |||
} | |||
|
|||
/** | |||
* NOTE: This can eventually be replaced when TypeScript |
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.
Is this still true now? Maybe add a bit more to the comment then because I haven't the faintest clue how we can remove the method and the variadic types header at the top of that issue just makes me dizzy 🤣
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, this is referring to sendCommand
. That might be the confusion...
My understanding is that the linked issue will eventually allow types such as:
type TypedArray = [string, number];
type ExtendedTypedArray = TypedArray + [string];
const value: ExtendedTypedArray = ['', 12, ''];
If so, we'd be able to type that variadic parameter as LH.CrdpCommands[C]['paramsType'] + [?{timeout: number}]
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.
Oooooh, that makes much more sense. Move the comments down there then near that parameter? :)
lighthouse-core/gather/driver.js
Outdated
@@ -860,27 +878,17 @@ class Driver { | |||
* Return the body of the response with the given ID. Rejects if getting the | |||
* body times out. | |||
* @param {string} requestId | |||
* @param {number} [timeout] | |||
* @param {number} timeout |
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.
isn't this param still optional?
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.
yeah. how do you mark optional? Are the brackets "clojure style"?
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.
{number=}
is closure style :)
brackets are jsdoc
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.
This looks great. Nice job handling some really core driver internals. :D
proto/lighthouse-result.proto
Outdated
// Used when security error prevents page load. | ||
INSECURE_DOCUMENT_REQUEST = 17; | ||
// Used when security error prevents page load. | ||
PROTOCOL_TIMEOUT = 18; |
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.
nope, can't rename.
But actually looks like we never exposed REQUEST_CONTENT_TIMEOUT in the proto. So I think we're OK. :)
18e9b9b
to
368f722
Compare
@hoten proto conflict for ya |
@paulirish resolved |
That gatherer sends all those It's also remotely possible we hit some gross style-recalc issue, depending on the page and how the commands are coming in. It's possible it would be more efficient to do all the lookups in the page at once with |
Fixes #6296