Got: Memory leak when using `cache`

Created on 22 Mar 2020  Â·  10Comments  Â·  Source: sindresorhus/got

Describe the bug

  • Node.js version: v12.16.1
  • OS & version: macOS 10.15.3

Actual behavior

Getting the following error message when making multiple requests with got:

(node:60669) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 error listeners added to [KeyvMemcache]. Use emitter.setMaxListeners() to increase limit

Expected behavior

For got/keyv to:

  • not keep adding the event listener for each call, leading to the memory leak
  • clean up the event listener (can add a teardown feature req in Keyv)

I expect this behavior especially when .extending:

const extendedGot = got.extend({ cache });

Code to reproduce

  • I traced it back to keyv:L43
  • The cache storage is passed into got, and got passes that into Keyv, which adds the event listener
  • In the code below, I shim .on, but in practice I'm using keyv-memcache, which extends the EventEmitter which is what makes the warning about the memory leak
const got = require('got');

const cache = new Map();
cache.on = function(eventName) {
    console.trace(eventName);
};

// Individual calls
for (let i = 0; i < 10; i++) {
    got('http://google.com', { cache });
}

// Extended calls
const extendedGot = got.extend({ cache });
for (let i = 0; i < 10; i++) {
    extendedGot('http://google.com');
}

Checklist

  • [x] I have read the documentation.
  • [x] I have tried my code with the latest version of Node.js and Got.

For what it's worth, I also read https://github.com/sindresorhus/got/issues/792, https://github.com/lukechilds/cacheable-request/issues/30, and https://github.com/sindresorhus/got/pull/921 (the "click for solution" link is broken for me) but I believe the problem is different.

bug enhancement external ✭ help wanted ✭

All 10 comments

You're creating a new Keyv instance every time (using the cacheable-request package unknowingly).

@sindresorhus I think we should disallow passing raw storage instances and force users to pass a Keyv instance.

I think we should disallow passing raw storage instances and force users to pass a Keyv instance.

That's inconvenient for users just wanting a simple memory cache. Can we not just handle it somehow?

Can we not just handle it somehow?

It can be solved on the Keyv side via a WeakMap.

A destructor/destroy method can also be added in Keyv that takes care of tear-downs.

Actually I can solve this in Got, then this issue would be fixed as well as #1098

Fixed in 2abacfffef9e84afd111827bb7848815e28b0cbf

Interestingly if you put await before got(...) it works as expected...

The script now gives (I replaced console.trace with console.log and ran node --trace-warnings bug.js):

error
(node:24387) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 error listeners added to [Keyv]. Use emitter.setMaxListeners() to increase limit
    at _addListener (events.js:390:17)
    at Keyv.addListener (events.js:406:10)
    at Keyv.once (events.js:437:8)
    at /home/szm/Desktop/got/node_modules/cacheable-request/src/index.js:190:16
    at /home/szm/Desktop/got/node_modules/cacheable-request/src/index.js:202:6
    at /home/szm/Desktop/got/dist/source/core/index.js:62:59
    at new Promise (<anonymous>)
    at cacheFn (/home/szm/Desktop/got/dist/source/core/index.js:56:41)
    at PromisableRequest._makeRequest (/home/szm/Desktop/got/dist/source/core/index.js:881:39)
    at /home/szm/Desktop/got/dist/source/core/index.js:291:28

It just attaches keyv.once('error', ...) but it's removed when the cache has been read.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

carvallegro picture carvallegro  Â·  4Comments

erfanium picture erfanium  Â·  3Comments

f-mer picture f-mer  Â·  4Comments

dAnjou picture dAnjou  Â·  3Comments

astoilkov picture astoilkov  Â·  3Comments