-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
Add noListener
option to useDb()
to help memory usage in cases where the app is calling useDb()
on every request
#9961
Comments
Yesterday I've run The modified code (see line with
|
First of all, if you're using Without Regarding models adding to memory usage, you can use the async function handleRequest(id) {
const conn = await database(id)
const res = {
id,
otherDbsLength: conn.otherDbs.length,
relatedDbsLength: Object.keys(conn.relatedDbs).length
}
conn.deleteModel(/.*/) // Delete all models on `conn`
await conn.close()
return res
} We'll add a new method to free up a connection for GC, because that's tricky to do right now due to event emitters. For now, I'd recommend managing connections manually if you expect to have an unlimited number of tenants, or use |
useDb
useDb()
connections that are closed with close({ permanent: true })
@vkarpov15 if we are speaking about new api here, what do you think of adding a Something like this:
where cache internally is a LRU cache which evicts old connections releasing them for GC as new connections come in as soon as limit is reached. I think for consumers it will be easier to specify this option than think of dropping models and connections manually on each request. What do you think? I will be happy to send an MR for implementing both additions, just want to make sure I understand your vision of api correctly. |
The cache approach is possible, but I don't think it would work well because we would have to close connections that are evicted from the cache. The worst case scenario is that Mongoose has to evict and close a connection that is actively being used, which leads to a bad user experience. In general, I'd also say that opening and closing connections on every request is bad practice. If you have 100k+ tenants, then I think you have to open/close connections on every request. In most multi-tenant environments I've seen, you keep connections open on a per-tenant basis because the number of tenants is relatively small and the cost of opening a new connection is high. |
I took a closer look and there's some issues with the example script. First, do not call In addition to that, we've added a const useDbOptions = {
// this causes number of otherDbs to go up, relatedDbs stays the same
useCache: false,
noListener: true
// this causes number of relatedDbs to go up in addition to otherDbs, even worse
//useCache: true
} And: async function handleRequest(id) {
const conn = await database(id)
const res = {
id,
otherDbsLength: conn.otherDbs.length,
relatedDbsLength: Object.keys(conn.relatedDbs).length
}
conn.deleteModel(/.*/) // Delete all models on `conn`
// Note that there's no `await conn.close()` here, that's intentional!
return res
} And that should drastically reduce memory usage and significantly increase the amount of time your server can go without running out of memory 👍 |
…e you're using `useDb()` on every request Fix #9961
useDb()
connections that are closed with close({ permanent: true })
noListener
option to useDb()
to help memory usage in cases where the app is calling useDb()
on every request
We don't, I added that line only to illustrate that even calling
I am trying to understand in which cases adding Thank you very much for all good advices and |
The MongoDB driver handles updating connection states under the hood, but Mongoose connections still need to be aware of them. And, in the case where you're creating new connections with |
Do you want to request a feature or report a bug?
I want to report a bug.
What is the current behavior?
We are using database per tenant approach, thus we make
useDb
call for each http request. After some time nodejs server goes out of memory.If the current behavior is a bug, please provide the steps to reproduce.
My
package.json
file:The whole code to run the server (
server.js
):The code to generate load (
generate-load.sh
):A
docker-compose.yml
to start mongo:Steps to see the issue:
docker-compose up
npm start
./generate.sh 1 30
If you look at stdout output of server you'll see:
otherDbs
collection grows over time. Looking though mongoose code I see that code pushes to that collection inuseDb
, but nowhere there is a code for removing from that collection.MaxListenersExceededWarning
also signals about memory leak. After each useDb mongoose setups listeners on created connection and as connection never gets deleted, event listeners stay for a while.Passing
useCache: true
option touseDb
makes things even worse, asrelatedDbs
collection also starts to grow.What is the expected behavior?
I'd expect that when
useCache: false
, connection objects created by theuseDb
are garbage collected properly.What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.
NodeJS:
14.15.1
Mongoose:
5.11.17
MongoDB:
3.6.4
- comes as a dependency of MongooseThe text was updated successfully, but these errors were encountered: