module.exports = wrapRequest const noop = () => Promise.resolve() function wrapRequest (state, request, options) { return state.retryLimiter.schedule(doRequest, state, request, options) } async function doRequest (state, request, options) { const isWrite = options.method !== 'GET' && options.method !== 'HEAD' const isSearch = options.method === 'GET' && options.url.startsWith('/search/') const isGraphQL = options.url.startsWith('/graphql') const retryCount = ~~options.request.retryCount const jobOptions = retryCount > 0 ? { priority: 0, weight: 0 } : {} if (state.clustering) { // Remove a job from Redis if it has not completed or failed within 60s // Examples: Node process terminated, client disconnected, etc. jobOptions.expiration = 1000 * 60 } // Guarantee at least 1000ms between writes // GraphQL can also trigger writes if (isWrite || isGraphQL) { await state.write.key(state.id).schedule(jobOptions, noop) } // Guarantee at least 3000ms between requests that trigger notifications if (isWrite && state.triggersNotification(options.url)) { await state.notifications.key(state.id).schedule(jobOptions, noop) } // Guarantee at least 2000ms between search requests if (isSearch) { await state.search.key(state.id).schedule(jobOptions, noop) } const req = state.global.key(state.id).schedule(jobOptions, request, options) if (isGraphQL) { const res = await req if (res.data.errors != null && res.data.errors.some((err) => err.type === 'RATE_LIMITED')) { const err = new Error('GraphQL Rate Limit Exceeded') err.headers = res.headers err.data = res.data throw err } } return req }