chore: respect retry-after header for rate-limiting (#35957)

This commit is contained in:
Alaister Young authored and GitHub committed 2025-05-27 19:37:05 +08:00
1 parent f8b3bfc4d6
commit 4ea088d93d
3 files changed
+22 -4

No files matched your search

+8 -2
View File
@@ -63,10 +63,12 @@ function pgMetaGuard(request: Request) {
// If there is no valid `x-connection-encrypted`, pg-meta will necesseraly fail to connect to the target database
// in such case, we save the hops and throw a 421 response instead
if (!isValidConnString(request.headers.get('x-connection-encrypted'))) {
const retryAfterHeader = request.headers.get('Retry-After')
throw new ResponseError(
'API Error: happened while trying to acquire connection to the database',
400,
request.headers.get('X-Request-Id') ?? undefined
request.headers.get('X-Request-Id') ?? undefined,
retryAfterHeader ? parseInt(retryAfterHeader) : undefined
)
}
}
@@ -98,6 +100,8 @@ client.use(
// add code field to body
body.code = response.status
body.requestId = request.headers.get('X-Request-Id')
const retryAfterHeader = response.headers.get('Retry-After')
body.retryAfter = retryAfterHeader ? parseInt(retryAfterHeader) : undefined
return new Response(JSON.stringify(body), {
headers: response.headers,
@@ -136,9 +140,11 @@ export const handleError = (error: unknown): never => {
const errorCode = 'code' in error && typeof error.code === 'number' ? error.code : undefined
const requestId =
'requestId' in error && typeof error.requestId === 'string' ? error.requestId : undefined
const retryAfter =
'retryAfter' in error && typeof error.retryAfter === 'number' ? error.retryAfter : undefined
if (errorMessage) {
throw new ResponseError(errorMessage, errorCode, requestId)
throw new ResponseError(errorMessage, errorCode, requestId, retryAfter)
}
}
+11 -1
View File
@@ -24,7 +24,9 @@ export function getQueryClient() {
error instanceof ResponseError &&
error.code !== undefined &&
error.code >= 400 &&
error.code < 500
error.code < 500 &&
// Still retry on 429s (rate limit)
error.code !== 429
) {
return false
}
@@ -35,6 +37,14 @@ export function getQueryClient() {
return false
},
retryDelay(failureCount, error) {
if (error instanceof ResponseError && error.retryAfter) {
return error.retryAfter * 1000
}
// react-query default: doubles, starting at 1000ms, with each attempt, but will not exceed 30 seconds
return Math.min(1000 * 2 ** failureCount, 30000)
},
},
},
})
+3 -1
View File
@@ -99,11 +99,13 @@ export type SupaResponse<T> = T | ResponseFailure
export class ResponseError extends Error {
code?: number
requestId?: string
retryAfter?: number
constructor(message: string | undefined, code?: number, requestId?: string) {
constructor(message: string | undefined, code?: number, requestId?: string, retryAfter?: number) {
super(message || 'API error happened while trying to communicate with the server.')
this.code = code
this.requestId = requestId
this.retryAfter = retryAfter
}
}