softonic / axios-retry

Axios plugin that intercepts failed requests and retries them whenever possible
Other
1.9k stars 167 forks source link

how to auto-retry after waiting pre-defined time for response #164

Open appukuttan-shailesh opened 3 years ago

appukuttan-shailesh commented 3 years ago

I am using axios to make a GET request in my reactjs app. Here is a code snippet of how I am currently making this call:

signal = axios.CancelToken.source();
...
...
...
  getList() {
    const url = baseUrl + "/projects";
    const config = {
      cancelToken: this.signal.token,
      headers: { Authorization: "Bearer " + this.context.auth[0].token },
    };
    axios
      .get(url, config)
      .then((res) => {
        console.log(res);
        this.setState({
          collab_list: res.data,
        });
      })
      .catch((err) => {
        console.log("Error: ", err.message);
      });
  }

What I find is that my GET requests work well most of the time, but around one out of 10 times they get stalled and remain in the pending state for a long time. During this time if I refresh the page, and redo the GET request, it works fine and returns a response immediately. This is an issue that I am facing since some time and could be a problem involving the targer server (over which I have no control).

So what I would like to do is the following (based on what I have found to work when doing manually):

So basically what I wish to do is retry, upto N times, if there is no repsonse within 5 seconds each time.

I know that axios-retry can trigger retry on error codes, but not sure if it can do "wait for X seconds for response, else retry". Is this doable? Thanks for any help.

KevinDanikowski commented 3 years ago

use retryDelay https://stackoverflow.com/questions/56074531/how-to-retry-5xx-requests-using-axios#answer-64076585 and add a timeout to the request (not axios-retry specific)

appukuttan-shailesh commented 3 years ago

If I am not wrong, retryDelay specifically deals with the delay between two successive retries. In my case I am happy to have this delay set to 0 ms (the default value I believe). With the timeout bit, I am finding it doesn't work properly (as seems to be the case with many people apparently - see here).

KevinDanikowski commented 3 years ago

Here is my solution for using a different non-axios library for requests, you can use it here if you're having issues:

  const res = this.requestor(url, {
    ...options,
    timeout: timeout * 1000
  })
  // NOTE: this is done because got doesn't do the timeout correctly, should only be
  // necessary for certain clients as failsafe
  const callTimeout = setTimeout(() => res.cancel(), this.config.callTimeout);

  const response = await res;
  clearTimeout(callTimeout);

  return response;
appukuttan-shailesh commented 3 years ago

Thanks for proposing an alternative solution @KevinDanikowski. I am going to try this out.

KevinDanikowski commented 3 years ago

@appukuttan-shailesh sounds good, I would recommend doing research into canceling axios calls, cancel() may not be the correct command.

Vinayjinugu-gep commented 2 years ago

I am looking for a solution for this problem, @appukuttan-shailesh did you find any good solution for the problem mentioned?

appukuttan-shailesh commented 2 years ago

Not really. I think the target server performance improved and I stopped getting these issues. So didn't really pursue this further. But I don't think I had arrived at a proper axios based solution.

KrayzeeKev commented 2 years ago

I'm just after the very simple behaviour of "retry on timeout". I can't work out how to make this happen. If the network is flaky, some requests just die. Axios eventually times out. This is not a 50x error. It's a timeout. I want to try a few times before giving up. I know that in 99 times out of 100, the second request will succeed. It seems oddly difficult to achieve this. The source SPECIFICALLY excludes network timeouts. Which is different. I'm talking about client-side axios timeouts. eg: const ax = axios.create(); axiosRetry(ax, { retries: 3 } ) await ax.get( { url, timeout: 2000 } )

I want it to start the get, in 2 seconds, fail that, try again, 2 seconds later try again, 2 seconds later fail

KrayzeeKev commented 2 years ago

Found my solution: retryCondition: err => err.code === 'ECONNABORTED' || axiosRetry.isNetworkOrIdempotentRequestError(err)

Waschnick commented 1 year ago

Sadly this does not work currently with axios 1.4.0 or 1.5.0 and axios-retry 3.8.0. The issue is that even if retryCondition is true, for a timeout it does not retry. It works for non-timeout erros.

          AxiosError: timeout of 2000ms exceeded
              at RedirectableRequest.handleRequestTimeout (/Users/sebastian.waschnick/Projekte/ueberseehub/effi-baufred-api/node_modules/axios/lib/adapters/http.js:630:16)
              at RedirectableRequest.emit (node:events:513:28)
              at Timeout.<anonymous> (/Users/sebastian.waschnick/Projekte/ueberseehub/effi-baufred-api/node_modules/follow-redirects/index.js:169:12)
              at listOnTimeout (node:internal/timers:564:17)
              at processTimers (node:internal/timers:507:7)

What did the magic trick for me: shouldResetTimeout: true It looks like otherwise it just does no retry on timeout, as the timeout is still 0 (it needs to be reset).

    axiosRetry(axios, {
      retries: 3,
      shouldResetTimeout: true,
      retryCondition: (e) => {
        return isNetworkOrIdempotentRequestError(e) || e?.code === 'ECONNABORTED';
      },
      onRetry: function onRetry(retryCount, error, requestConfig) {
        console.info(`Retry #${retryCount} to '${requestConfig.url}' because of ${error.message}`);
        return;
      },
    });