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
- Perform GET /health API request
- The response is 200 and is cached
- Perform GET /health API request
- The response is an AxiosError and is handled as expected
- Perform GET /health API request
- The response is an AxiosError
- 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!!!