Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Axios response memory leak #3457

Closed
Dirk94 opened this issue Dec 5, 2020 · 13 comments · Fixed by follow-redirects/follow-redirects#152
Closed

Axios response memory leak #3457

Dirk94 opened this issue Dec 5, 2020 · 13 comments · Fixed by follow-redirects/follow-redirects#152

Comments

@Dirk94
Copy link

Dirk94 commented Dec 5, 2020

Describe the bug

I think that Axios has a serious memory leak. The Axios responses are never removed from memory.

To Reproduce

Create a new node project.

npm i axios && touch index.js
const axios = require('axios');

// Perform 1 request per second.
setInterval(async () => {
	const response = await axios.get("https://wikipedia.com");
	console.log("Done getting response!");
}, 1000)

Expected behavior

I would expect this program to not keep a reference to every response. But unfortunately, it does.

Environment

  • Axios Version 0.21.0
  • Node.js Version 14.12.0
  • OSX 10.15.6

Additional context/Screenshots

The Chrome memory profiler shows that the Wikipedia responses are stored in memory.

Screen Shot 2020-12-05 at 3 38 31 PM

@Legogris
Copy link

Legogris commented Feb 1, 2021

I have similar indications.

In node.js, after doing an axios.get(...).then(() => {...}), the process hangs indefinitely when it should exit.

If at exit point logging process.getActiveHandles(), the WriteStream, ReadStream and Socket are still hanging around long after the response has returned, the connection should be closed and the scope is out.

Same behavior when setting http(s)Agent with keepalive: false.

Nodejs 12 & 14, axios 0.19,0.20,0.21.1

@splitice
Copy link

I have been investigating a similar issue. I'm currently testing using a more robust script of:

const axios = require('axios'),
      Q = require('q-lite')

let request = 0
async function doRequest(url){
	request++
	try {
		await axios.get(url);
	} catch(ex){
		console.log(`${url}: Failed to get response ${request}`)
		return
	}
	console.log(`${url}: Done getting response ${request}`);
}

async function main(url){
	// Perform requests with a 1 second delay
	while(true){
		await doRequest(url)
		await Q.delay(1000)
	}
}

main("https://wikipedia.com")
main("https://google.com")
main("https://microsoft.com")
main("https://gmail.com")

Ran with: node --max_old_space_size=8 axios-leak.js

Your script is prone to memory build-up due to it not waiting on the response before starting the next. The number of requests in flight can increase if the request time increases.

I wasnt able to replicate any issue with my script, I tested up to 22,422 requests.

@splitice
Copy link

splitice commented Feb 27, 2021

The following however does lead to an eventualy crash.

const axios = require('axios'),
      Q = require('q-lite')

let request = 0
async function doRequest(url){
	request++
	try {
		await axios.get(url, {timeout: 500});
	} catch(ex){
		//console.log(`${url}: Failed to get response ${request}`)
		return
	}
	//console.log(`${url}: Done getting response ${request}`);
}

async function main(url){
	// Perform requests with a 1 second delay
	while(true){
		await doRequest(url)
		await Q.delay(100)
	}
}

main("https://httpstat.us/200?sleep=5000")
main("https://httpstat.us/200?sleep=6000")
main("https://httpstat.us/200?sleep=7000")
main("https://httpstat.us/200?sleep=8000")
main("https://httpstat.us/200?sleep=5000")
main("https://httpstat.us/200?sleep=6000")
main("https://httpstat.us/200?sleep=7000")
main("https://httpstat.us/200?sleep=8000")
main("https://httpstat.us/200?sleep=5000")
main("https://httpstat.us/200?sleep=6000")
main("https://httpstat.us/200?sleep=7000")
main("https://httpstat.us/200?sleep=8000")
main("https://httpstat.us/200?sleep=5000")
main("https://httpstat.us/200?sleep=6000")
main("https://httpstat.us/200?sleep=7000")
main("https://httpstat.us/200?sleep=8000")
main("https://httpstat.us/200?sleep=5000")
main("https://httpstat.us/200?sleep=6000")
main("https://httpstat.us/200?sleep=7000")
main("https://httpstat.us/200?sleep=8000")
main("https://httpstat.us/200?sleep=5000")
main("https://httpstat.us/200?sleep=6000")
main("https://httpstat.us/200?sleep=7000")
main("https://httpstat.us/200?sleep=8000")
main("https://httpstat.us/200?sleep=5000")
main("https://httpstat.us/200?sleep=6000")
main("https://httpstat.us/200?sleep=7000")
main("https://httpstat.us/200?sleep=8000")
main("https://httpstat.us/200?sleep=5000")
main("https://httpstat.us/200?sleep=6000")
main("https://httpstat.us/200?sleep=7000")
main("https://httpstat.us/200?sleep=8000")
main("https://httpstat.us/200?sleep=5000")
main("https://httpstat.us/200?sleep=6000")
main("https://httpstat.us/200?sleep=7000")
main("https://httpstat.us/200?sleep=8000")

Ran with: node --max_old_space_size=5 --trace_gc --gc_interval=5000 axios-leak.js

So it appears the leak is tied to timeouts or failed requests.

@splitice
Copy link

splitice commented Feb 27, 2021

Further troubleshooting narrows it down to follow-redirects as memory leak slower / non existant with maxRedirects:0. It only occurs on failed requests, so my current feeling is that a reference is being kept alive due to incorrect setTimeout / abort handling.

@RubenVerborgh
Copy link

follow-redirects v1.13.3 fixes this

@jasonsaayman
Copy link
Member

This has been fixed with #3694

@DjovDev
Copy link

DjovDev commented Jun 10, 2021

Hey you guys sure this is fixed?

I thought I'd give Axios a try instead of the actual HTTP client of Nodejs, yet I see my ram jump skyhigh when retrieving 5mb responses from a backend server using a post command:

I call these functions sequentially. After one is finished the next starts. This always retrieves the same response with binary data of abount 5 megabytes.

My axios function:

const axios = require('axios').default;
const parseString = require('xml2js').parseString;

// post xml function
function xmlPost(body,host,port,callback) {   
    axios({
        method:'post',
        url: 'http://' + host + ':' + port,
        data: body,
        headers:  {
            'Content-Type': 'application/xml',
            'Content-Length': body.length
        }
    }).then(response => {
        parseString(response.data, function (err, result) {
            if (err) {
                callback('Tried to parse the following XML: ' + buffer + ' - ' + err)
            } else {
                callback(null,result)
                result = null
            }
        })
    })
}

When doing something 'similar' in the http library, my ram stays levelled.

const http = require('http')
const parseString = require('xml2js').parseString;

// post xml function
function xmlPost(body,host,port,callback) {   

    let postRequest = {
        host: host,
        port: port,
        method: "POST",
        headers: {
            'Content-Type': 'application/xml;charset=UTF-8',
        }
    }

    let req = http.request( postRequest, function( res )    {
        let requestError
        let buffer = "";
        res.on( "data", function( data ) { buffer = buffer + data; } );
        res.on( "end", function( data ) { 
            if (requestError) {
                callback(requestError)
            } else {
                parseString(buffer, function (err, result) {
                    if (err) {
                        callback('Tried to parse the following XML: ' + buffer + ' - ' + err)
                    } else {
                        callback(null,result)
                    }
                })
            }

        } );
    
    });
    
    req.on('error', function(e) {
        requestError = 'Got error while posting XML: ' + e.message
        console.log('Got error while posting XML: ' + e.message)
    });
    
    req.write( body );
    req.end(); 
}

@Samrat-Saha-Sammy
Copy link

Seems like the bugFix #3694 is merged to master on March 23, but still we got no version release after that.

@shaoxp
Copy link

shaoxp commented Aug 11, 2022

i used 0.27.2 version to test @Dirk94's original test, it still have memory leakage.

@shaoxp
Copy link

shaoxp commented Aug 12, 2022

I test all versions following image.

the results show that memory usage will always increase, so there seems still memory leakage. can someone reopen this issue? do we have any quick workaround?

and I used the chrome inspect to investigate the reason. and find the main leakage resource is a string referenced by root.
image

@lptai
Copy link

lptai commented Aug 15, 2022

I just upgraded to axios@0.27.2 still have memory leak. My use case is retrying on 429 status - rate limit exceeded error. The application is keeping restarted with memory leak error with axios retry

@vzkhrv
Copy link

vzkhrv commented Oct 4, 2022

I test all versions following

Can't reproduce on my own. Do u still have this leakage?

@yuvalbl
Copy link

yuvalbl commented May 10, 2023

Reproduced on v1.4.0
A new related issue opened - see #5641

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.