arthurfiorette / axios-cache-interceptor

πŸ“¬ Small and efficient cache interceptor for axios. Etag, Cache-Control, TTL, HTTP headers and more!

Home Page:https://axios-cache-interceptor.js.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bug: Deadlock when request fail for a stale cache entry

rayvincent2 opened this issue Β· comments

What happened?

I've run into an issue when making requests to a health status API. Our http client naturally uses axios with the axios-cache-interceptor and it works great. While testing the use cases we have, I found a situation where the http client gets locked up.

Axios cache interceptor configuration:

{
  "staleIfError": false,
  "ttl": -1,
}

Steps to reproduce

  1. Perform GET /health API request
  2. The response is 200 and is cached
  3. Perform GET /health API request
  4. The response is an AxiosError and is handled as expected
  5. Perform GET /health API request
  6. The response is an AxiosError
  7. The axios.get promise never rejects and the application is locked

When I turn on the debugger, I see this message when it locks up:

Detected concurrent request, waiting for it to finish

Code to reproduce the issue

Here's a quick code snippet that showcases the issue. It will stand up an express server on a random open port of localhost and run the steps above. You'll notice that if you set staleIfError to false, then the application will safely exit.

const express = require('express');
const Axios = require('axios');
const http = require('http');
const { setupCache, buildMemoryStorage } = require('axios-cache-interceptor/dev');
const app = express();

// Global variable to allow switching this api from passing or failing
let pass = true;

app.set('etag', 'strong');
// Define the /api route
app.get('/health', (req, res) => {
  if (pass) {
    res.json({ healthy: true });
  } else {
    res.status(404).send();
  }
});

const server = http.createServer(app);

// Start the server on any available port on localhost
server.listen(undefined, 'localhost', async() => {
  const { port, address } = server.address();
  const url = `http://${address}:${port}`;
  console.log(`Server is running at ${url}`);

  const axios = setupCache(Axios, {
    storage: buildMemoryStorage(),
    debug: console.log,
    staleIfError: false,
    ttl: -1
  }); 

  // Successful request
  console.log(`GET ${url}/health`)
  const response = await axios.get(`${url}/health`);
  console.log(`Request succeeded with ${response.status}`)

  // Set the /health api to always fail
  pass = false;

  // Failed request 1
  console.log(`GET ${url}/health`)
  try {
    const response = await axios.get(`${url}/health`);
    console.log(`Request succeeded with ${response.status}`)
  } catch(error) {
    console.log(`${error.message}`)
  }

  // Failed request 2
  console.log(`GET ${url}/health`)
  try {
    const response = await axios.get(`${url}/health`);
    console.log(`Request succeeded with ${response.status}`)
  } catch(error) {
    console.log(`${error.message}`)
  }
  server.close();
});

When the debugger is turned off, this is the output of the application:

Server is running at http://127.0.0.1:40385
GET http://127.0.0.1:40385/health
Request succeeded with 200
GET http://127.0.0.1:40385/health
Request failed with status code 404
GET http://127.0.0.1:40385/health

axios-cache-interceptor version

v0.10.7, v1.3.0

Node / Browser Version

Node v18.17.0, 16.14.2

Axios Version

v0.27.2, v1.5.0

What storage is being used

Memory Storage

Relevant debugging log output

You are using a development build. Make sure to use the correct build in production
https://axios-cache-interceptor.js.org/guide/getting-started


Server is running at http://127.0.0.1:34351
GET http://127.0.0.1:34351/health
{
  id: '484133867',
  msg: 'Sending request, waiting for response',
  data: { overrideCache: false, state: 'empty' }
}
{
  id: '484133867',
  msg: 'Useful response configuration found',
  data: {
    cacheConfig: {
      update: {},
      ttl: -1,
      methods: [Array],
      cachePredicate: [Object],
      etag: true,
      modifiedSince: true,
      interpretHeader: true,
      cacheTakeover: true,
      staleIfError: false,
      override: false,
      hydrate: undefined
    },
    cacheResponse: {
      data: [Object],
      status: 200,
      statusText: 'OK',
      headers: [Object [AxiosHeaders]]
    }
  }
}
{ id: '484133867', msg: 'Found waiting deferred(s) and resolved them' }
{
  id: '484133867',
  msg: 'Response cached',
  data: {
    cache: {
      state: 'cached',
      ttl: -1,
      staleTtl: undefined,
      createdAt: 1696055740300,
      data: [Object]
    },
    response: {
      status: 200,
      statusText: 'OK',
      headers: [Object [AxiosHeaders]],
      config: [Object],
      request: [ClientRequest],
      data: [Object],
      id: '484133867',
      cached: false
    }
  }
}
Request succeeded with 200
GET http://127.0.0.1:34351/health
{ id: '484133867', msg: 'Updated stale request' }
{
  id: '484133867',
  msg: 'Sending request, waiting for response',
  data: { overrideCache: false, state: 'stale' }
}
{
  id: '484133867',
  msg: 'Received an unknown error that could not be handled',
  data: {
    error: AxiosError: Request failed with status code 404
        at settle (/home/rvincent/Documents/open-source/axios-cache-interceptor-test/node_modules/axios/dist/node/axios.cjs:1913:12)
        at IncomingMessage.handleStreamEnd (/home/rvincent/Documents/open-source/axios-cache-interceptor-test/node_modules/axios/dist/node/axios.cjs:2995:11)
        at IncomingMessage.emit (node:events:526:35)
        at endReadableNT (node:internal/streams/readable:1359:12)
        at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
      code: 'ERR_BAD_REQUEST',
      config: [Object],
      request: [ClientRequest],
      response: [Object]
    },
    config: {
      transitional: [Object],
      adapter: [Array],
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 0,
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      maxBodyLength: -1,
      env: [Object],
      validateStatus: [Function (anonymous)],
      headers: [Object [AxiosHeaders]],
      cache: [Object],
      method: 'get',
      url: 'http://127.0.0.1:34351/health',
      id: '484133867',
      data: undefined
    }
  }
}
Request failed with status code 404
GET http://127.0.0.1:34351/health
{
  id: '484133867',
  msg: 'Detected concurrent request, waiting for it to finish'
}

Thanks for this detailed bug report :) Open to a PR?

Yes! I'd be happy to contribute. I probably would have except I ran into an issue building the project last night and figured I'd start with the bug ticket. Here's the issue I ran into when I tried to build:

rvincent@office-desktop:~/Documents/open-source/axios-cache-interceptor$ nvm use
Found '/home/rvincent/Documents/open-source/axios-cache-interceptor/.nvmrc' with version <v20.5.0>
Now using node v20.5.0 (npm v9.8.0)
rvincent@office-desktop:~/Documents/open-source/axios-cache-interceptor$ npm install
npm WARN deprecated @types/prettier@3.0.0: This is a stub types definition. prettier provides its own type definitions, so you do not need this installed.

added 479 packages, and audited 480 packages in 2s

116 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
rvincent@office-desktop:~/Documents/open-source/axios-cache-interceptor$ npm run build

> axios-cache-interceptor@1.3.0 build
> sh build/build.sh


Starting build...

Target cleared...

error TS2688: Cannot find type definition file for 'prettier'.
  The file is in the program because:
    Entry point for implicit type library 'prettier'


Found 1 error.

assets by status 10.3 KiB [cached] 1 asset
orphan modules 47.3 KiB [orphan] 21 modules
runtime modules 396 bytes 2 modules
./src/index.ts + 15 modules 48 KiB [not cacheable] [built] [code generated]

ERROR in /home/rvincent/Documents/open-source/axios-cache-interceptor/build/tsconfig.build.json
/home/rvincent/Documents/open-source/axios-cache-interceptor/build/tsconfig.build.json
[tsl] ERROR
      TS2688: Cannot find type definition file for 'prettier'.
  The file is in the program because:
    Entry point for implicit type library 'prettier'
ts-loader-default_788ac1d04dfcd11f

webpack 5.88.2 compiled with 1 error in 5447 ms

assets by status 13.4 KiB [cached] 1 asset
orphan modules 47.3 KiB [orphan] 21 modules
runtime modules 396 bytes 2 modules
./src/index.ts + 15 modules 48 KiB [not cacheable] [built] [code generated]

ERROR in /home/rvincent/Documents/open-source/axios-cache-interceptor/build/tsconfig.build.json
/home/rvincent/Documents/open-source/axios-cache-interceptor/build/tsconfig.build.json
[tsl] ERROR
      TS2688: Cannot find type definition file for 'prettier'.
  The file is in the program because:
    Entry point for implicit type library 'prettier'
ts-loader-default_788ac1d04dfcd11f

webpack 5.88.2 compiled with 1 error in 5440 ms

assets by status 9.98 KiB [cached] 1 asset
orphan modules 47.3 KiB [orphan] 21 modules
runtime modules 670 bytes 3 modules
./src/index.ts + 15 modules 48 KiB [not cacheable] [built] [code generated]

ERROR in /home/rvincent/Documents/open-source/axios-cache-interceptor/build/tsconfig.build.json
/home/rvincent/Documents/open-source/axios-cache-interceptor/build/tsconfig.build.json
[tsl] ERROR
      TS2688: Cannot find type definition file for 'prettier'.
  The file is in the program because:
    Entry point for implicit type library 'prettier'
ts-loader-default_788ac1d04dfcd11f

webpack 5.88.2 compiled with 1 error in 5466 ms

assets by status 13.2 KiB [cached] 1 asset
orphan modules 47.3 KiB [orphan] 21 modules
runtime modules 670 bytes 3 modules
./src/index.ts + 15 modules 48 KiB [not cacheable] [built] [code generated]

ERROR in /home/rvincent/Documents/open-source/axios-cache-interceptor/build/tsconfig.build.json
/home/rvincent/Documents/open-source/axios-cache-interceptor/build/tsconfig.build.json
[tsl] ERROR
      TS2688: Cannot find type definition file for 'prettier'.
  The file is in the program because:
    Entry point for implicit type library 'prettier'
ts-loader-default_788ac1d04dfcd11f

webpack 5.88.2 compiled with 1 error in 5397 ms

assets by status 16.3 KiB [cached] 1 asset
orphan modules 78.4 KiB [orphan] 22 modules
runtime modules 695 bytes 3 modules
./src/index.ts + 16 modules 79.2 KiB [built] [code generated]

ERROR in /home/rvincent/Documents/open-source/axios-cache-interceptor/build/tsconfig.build.json
/home/rvincent/Documents/open-source/axios-cache-interceptor/build/tsconfig.build.json
[tsl] ERROR
      TS2688: Cannot find type definition file for 'prettier'.
  The file is in the program because:
    Entry point for implicit type library 'prettier'
ts-loader-default_a5eefc6b8f4b789c

webpack 5.88.2 compiled with 1 error in 5681 ms

assets by status 15 KiB [cached] 1 asset
orphan modules 49.9 KiB [orphan] 21 modules
runtime modules 670 bytes 3 modules
./src/index.ts + 15 modules 50.7 KiB [built] [code generated]

ERROR in /home/rvincent/Documents/open-source/axios-cache-interceptor/build/tsconfig.build.json
/home/rvincent/Documents/open-source/axios-cache-interceptor/build/tsconfig.build.json
[tsl] ERROR
      TS2688: Cannot find type definition file for 'prettier'.
  The file is in the program because:
    Entry point for implicit type library 'prettier'
ts-loader-default_788ac1d04dfcd11f

webpack 5.88.2 compiled with 1 error in 5572 ms

Build done!
rvincent@office-desktop:~/Documents/open-source/axios-cache-interceptor$ 

It doesn't seem to generate any js files when building which I'm assuming is attributed to the errors.

rvincent@office-desktop:~/Documents/open-source/axios-cache-interceptor$ tree dist
dist
β”œβ”€β”€ cache
β”‚   β”œβ”€β”€ axios.d.ts
β”‚   β”œβ”€β”€ axios.d.ts.map
β”‚   β”œβ”€β”€ cache.d.ts
β”‚   β”œβ”€β”€ cache.d.ts.map
β”‚   β”œβ”€β”€ create.d.ts
β”‚   └── create.d.ts.map
β”œβ”€β”€ header
β”‚   β”œβ”€β”€ headers.d.ts
β”‚   β”œβ”€β”€ headers.d.ts.map
β”‚   β”œβ”€β”€ interpreter.d.ts
β”‚   β”œβ”€β”€ interpreter.d.ts.map
β”‚   β”œβ”€β”€ types.d.ts
β”‚   └── types.d.ts.map
β”œβ”€β”€ index.bundle.d.ts
β”œβ”€β”€ index.d.ts
β”œβ”€β”€ index.d.ts.map
β”œβ”€β”€ interceptors
β”‚   β”œβ”€β”€ build.d.ts
β”‚   β”œβ”€β”€ build.d.ts.map
β”‚   β”œβ”€β”€ request.d.ts
β”‚   β”œβ”€β”€ request.d.ts.map
β”‚   β”œβ”€β”€ response.d.ts
β”‚   β”œβ”€β”€ response.d.ts.map
β”‚   β”œβ”€β”€ util.d.ts
β”‚   └── util.d.ts.map
β”œβ”€β”€ storage
β”‚   β”œβ”€β”€ build.d.ts
β”‚   β”œβ”€β”€ build.d.ts.map
β”‚   β”œβ”€β”€ memory.d.ts
β”‚   β”œβ”€β”€ memory.d.ts.map
β”‚   β”œβ”€β”€ types.d.ts
β”‚   β”œβ”€β”€ types.d.ts.map
β”‚   β”œβ”€β”€ web-api.d.ts
β”‚   └── web-api.d.ts.map
β”œβ”€β”€ tsconfig.types.tsbuildinfo
└── util
    β”œβ”€β”€ cache-predicate.d.ts
    β”œβ”€β”€ cache-predicate.d.ts.map
    β”œβ”€β”€ key-generator.d.ts
    β”œβ”€β”€ key-generator.d.ts.map
    β”œβ”€β”€ types.d.ts
    β”œβ”€β”€ types.d.ts.map
    β”œβ”€β”€ update-cache.d.ts
    └── update-cache.d.ts.map

5 directories, 40 files

There seems to be an issue with the types for prettier? I only saw what's on the surface and haven't dug any deeper. I hope to have time tonight to play. If you have any insight to save me any time getting my development environment up and running, then I'll take it!

You need to use pnpm to build this project, it will correctly resolve all dependencies :)

Anyways, I got time and could fix this bug. Thanks for reporting it :)

Incredible! Thanks for the quick turn around! I've confirmed that the new version is working great in my project. Thank you!!!