-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Implement a pattern for dealing with upstream APIs which are slow on the first hit; affects [endpoint] #9233
Changes from 4 commits
d78e4e4
2865b2d
fb9c636
0ac8fb6
e7d434a
d52b475
b8a184b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,14 +39,14 @@ function coalesceCacheLength({ | |
assert(defaultCacheLengthSeconds !== undefined) | ||
|
||
const cacheLength = coalesce( | ||
serviceOverrideCacheLengthSeconds, | ||
serviceDefaultCacheLengthSeconds, | ||
defaultCacheLengthSeconds | ||
) | ||
|
||
// Overrides can apply _more_ caching, but not less. Query param overriding | ||
// can request more overriding than service override, but not less. | ||
const candidateOverrides = [ | ||
serviceOverrideCacheLengthSeconds, | ||
overrideCacheLengthFromQueryParams(queryParams), | ||
Comment on lines
41
to
50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the history on this, see #2755 Basically, the only thing relying on the logic as it stood here was the endpoint badge (we don't want endpoint badge users to be able to set a lower |
||
].filter(x => x !== undefined) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,6 +42,7 @@ class ShieldsRuntimeError extends Error { | |
if (props.underlyingError) { | ||
this.stack = props.underlyingError.stack | ||
} | ||
this.cacheSeconds = props.cacheSeconds | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've done this here so any |
||
} | ||
} | ||
|
||
|
@@ -206,6 +207,9 @@ class Deprecated extends ShieldsRuntimeError { | |
* @property {string} prettyMessage User-facing error message to override the | ||
* value of `defaultPrettyMessage()`. This is the text that will appear on the | ||
* badge when we catch and render the exception (Optional) | ||
* @property {number} cacheSeconds Length of time to cache this error response | ||
* for. Defaults to the cacheLength of the service class throwing the error | ||
* (Optional) | ||
*/ | ||
|
||
export { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,12 @@ import { | |
|
||
const userAgent = getUserAgent() | ||
|
||
async function sendRequest(gotWrapper, url, options) { | ||
async function sendRequest( | ||
gotWrapper, | ||
url, | ||
options = {}, | ||
customExceptions = {} | ||
) { | ||
const gotOptions = Object.assign({}, options) | ||
gotOptions.throwHttpErrors = false | ||
gotOptions.retry = { limit: 0 } | ||
|
@@ -22,6 +27,12 @@ async function sendRequest(gotWrapper, url, options) { | |
underlyingError: new Error('Maximum response size exceeded'), | ||
}) | ||
} | ||
if (err.code in customExceptions) { | ||
throw new Inaccessible({ | ||
...customExceptions[err.code], | ||
underlyingError: err, | ||
}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Spreading in this order means you can't manually override |
||
} | ||
throw new Inaccessible({ underlyingError: err }) | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -178,7 +178,10 @@ export default class Endpoint extends BaseJsonService { | |
logoWidth, | ||
logoPosition, | ||
style, | ||
cacheSeconds, | ||
// don't allow the user to set cacheSeconds any shorter than this._cacheLength | ||
cacheSeconds: Math.max( | ||
...[this._cacheLength, cacheSeconds].filter(x => x !== undefined) | ||
), | ||
Comment on lines
+181
to
+184
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't leave a line comment on it because I've not changed it in this PR, but there is an existing endpoint service test covering this. |
||
} | ||
} | ||
|
||
|
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.
errorMessages
andcustomExceptions
do somewhat similar but importantly different things. Naming things is hard.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.
ack yeah I see that being tricky down the road since both of these are (ultimately) used in the setting of error messages.
I feel like in a perfect world (one where no refactoring would be required), I'd be inclined to keep a single top level object for errors and maybe have it have two properties, one that would point to an object that's our standard http response status code to error message dictionary we have today, and the other would be the new I feel like a single top level object that had two properties, one being new dictionary that maps nodejs error codes to a desired error message.
Thinking on a bit more, I suppose having those two mappings separate as you do here is fine; I think it's just the first having squatted on the highly generic
errorMessages
name that feels problematic.What if we come up with a more descriptive name for this one for now, e.g. (overly verbosely)
nodeErrorCodeToShieldsExceptions
and then we could potentially consider renamingerrorMessages
to something similarly more descriptive down the road?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 broadly agreed. Just thinking about the ergonomics of consuming this at the service layer...
Mapping HTTP error codes to error messages is really common - we do stuff like
all over the place, so lets keep that easy to do in a convenient way.
I've designed this feature (provisionally lets call it
customExceptions
) to be relatively generic, but I'd expect us to actually use this quite rarely.I think given that, I'd prefer to keep a flatter structure and avoid making
errorMessages
a nested object. In the vast majority of cases we would only care about one of the keys. If we can come up with 2 good names we would need to change the code in loads of places anyway, so lets make those two good names the top-level names rather than introduce another layer of nesting.i.e: If we need to go and change a load of existing service classes, I'd rather do a big find and replace changing
to
than
or whatever.
If we ignore the fact that there is loads of existing code using
errorMessages
and we were naming these two concepts from scratch today, what do you think would be the ideal names/API for these things? I guess the things we currently callerrorMessages
are really HTTP Status Codes. The thing I've calledcustomExceptions
are really System Error Codes. How abouthttpErrors
andsystemErrors
?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.
Yup, love it. Definitely clears up the naming overlap and is much more succinct than the ones I threw out there