Skip to content

Commit

Permalink
fix(request): also handle cuncorrent request, always shedule
Browse files Browse the repository at this point in the history
  • Loading branch information
leomp12 committed Mar 3, 2020
1 parent 69bd76f commit ed3a656
Showing 1 changed file with 72 additions and 57 deletions.
129 changes: 72 additions & 57 deletions src/lib/request.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import {
API_STORE,
API_STORE_CACHE,
API_PASSPORT,
API_SEARCH,
API_MODULES,
API_STOREFRONT,
API_GRAPHS,
Expand All @@ -13,12 +11,12 @@ import axios from './axios'
// set delay between requests by API
const delays = {}
delays[API_STORE] = delays[API_MODULES] = delays[API_PLATFORM] = 250
delays[API_STORE_CACHE] = delays[API_SEARCH] = 50
delays[API_STOREFRONT] = 400
delays[API_GRAPHS] = 570
delays[API_PASSPORT] = 1070
// count current scheduled requests by API
const scheduledRequests = {}
let concurrentRequests = 0
// store APIs on idle after 503 response
const waitingApis = []

Expand All @@ -27,41 +25,54 @@ const env = (typeof window === 'object' && window) || (typeof process === 'objec
const checkEnvVar = prop => env && (env[prop] === true || env[prop] === 'true')
const debug = checkEnvVar('ECOMCLIENT_DEBUG')

const request = (config, api, delay = 170) => {
const request = (config, api, delay = 170, scheduleTime) => {
if (debug) {
console.log(`[ecomClient]: (${scheduleTime} ~ ${Date.now()}) send ${config.method} ${config.url}`)
}
if (checkEnvVar('ECOMCLIENT_NOTIMEOUT') && config.timeout) {
// reset request timeout
config.timeout = 0
}

return axios.request(config).catch(err => {
// handle 503 errors here
const { response } = err
if (response && response.status === 503) {
// service unavailable, probably blocked by proxy
if (api) {
// add API to idle
waitingApis.push(api)
}
return new Promise((resolve, reject) => {
concurrentRequests++
axios.request(config)
.then(resolve)

// retry with new promise
return new Promise((resolve, reject) => {
// wait and resend request
setTimeout(() => {
.catch(err => {
if (debug) {
err.message = `[ecomClient]: ${err.message}`
console.error(err)
}
// handle 503 errors here
const { response } = err
if (response && response.status === 503) {
// service unavailable, probably blocked by proxy
if (api) {
// unset API idle
const index = waitingApis.indexOf(api)
if (index > -1) {
waitingApis.splice(index, 1)
}
// add API to idle
waitingApis.push(api)
}
// new axios request without error handler
axios.request(config).then(resolve).catch(reject)
}, delay)

// retry with new promise
// wait and resend request
return setTimeout(() => {
if (api) {
// unset API idle
const index = waitingApis.indexOf(api)
if (index > -1) {
waitingApis.splice(index, 1)
}
}
// new axios request without error handler
axios.request(config).then(resolve).catch(reject)
}, delay >= 170 ? delay : 170)
}
reject(err)
})
}

// chain promise catch
throw err
.finally(() => {
concurrentRequests--
})
})
}

Expand Down Expand Up @@ -94,36 +105,40 @@ export default axiosConfig => {
break
}
}

if (!delay) {
// returns request promise directly
return request(axiosConfig, api)
} else {
// scheduled request
// set request queue position based on current API scheduled requests
// there's no delay when queue is 0 (first request)
const queue = scheduledRequests[api] || 0
scheduledRequests[api] = queue + 1
if (debug) {
console.log(`[ecomClient]: request delay ${delay * queue}ms`)
}
// minimum 50ms delay
delay = 50
}

// returns promise resolved with request after timeout
return new Promise((resolve, reject) => {
const schedule = () => {
setTimeout(() => {
if (waitingApis.indexOf(api) <= -1) {
// send request and reset scheduled requests count
scheduledRequests[api]--
request(axiosConfig, api, delay).then(resolve).catch(reject)
} else {
// API on idle due to 503 response
// schedule request again
schedule()
}
}, delay * queue)
// scheduled request
// set request queue position based on current API scheduled requests
// there's no delay when queue is 0 (first request)
const queue = scheduledRequests[api] || 0
scheduledRequests[api] = queue + 1

// returns promise resolved with request after timeout
return new Promise((resolve, reject) => {
const schedule = () => {
// calculate final delay with API queue and concurrent requests multipliers
const requestDelay = delay * queue + concurrentRequests * 2.5
let scheduleTime
if (debug) {
scheduleTime = Date.now()
console.log(`[ecomClient]: (${scheduleTime}) request delay ${requestDelay}ms`)
}
schedule()
})
}

setTimeout(() => {
if (waitingApis.indexOf(api) <= -1) {
// send request and reset scheduled requests count
scheduledRequests[api]--
request(axiosConfig, api, delay, scheduleTime).then(resolve).catch(reject)
} else {
// API on idle due to 503 response
// schedule request again
schedule()
}
}, requestDelay)
}
schedule()
})
}

0 comments on commit ed3a656

Please sign in to comment.