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

Jest detect open handles in tests after upgrading from 8.9.6 to 8.10.0 #15241

Closed
2 tasks done
Freezystem opened this issue Feb 7, 2025 · 8 comments · Fixed by #15245 or #15251
Closed
2 tasks done

Jest detect open handles in tests after upgrading from 8.9.6 to 8.10.0 #15241

Freezystem opened this issue Feb 7, 2025 · 8 comments · Fixed by #15245 or #15251

Comments

@Freezystem
Copy link
Contributor

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Mongoose version

8.10.0

Node.js version

20

MongoDB server version

6.0.20

Typescript version (if applicable)

No response

Description

Jest detects open handles after upgrading from 8.9.6 to 8.10.0.
Here are some logs:

Jest has detected the following 14 open handles potentially keeping Jest from exiting:

...

at node_modules/mongoose/lib/connection.js:838:19
at NativeConnection._waitForConnect (node_modules/mongoose/lib/connection.js:837:7)
at _createCollection (node_modules/mongoose/lib/model.js:1106:16)
at Function.init (node_modules/mongoose/lib/model.js:1119:16)
at Mongoose._model (node_modules/mongoose/lib/mongoose.js:667:9)
at Mongoose.model (node_modules/mongoose/lib/mongoose.js:622:27)
...

My code hasn't changed it was just a mongoose dependency upgrade. When I rollback the upgrade everything works fine again.

Steps to Reproduce

upgrade from 8.9.6 to 8.10.0. Run tests that use jest and mongoose connections.

Expected Behavior

No response

@hasezoey
Copy link
Collaborator

hasezoey commented Feb 8, 2025

I had also run into this, a fix is in #15245.

@vkarpov15
Copy link
Collaborator

vkarpov15 commented Feb 8, 2025

The only way I've been able to repro this is with creating a model but never connecting to MongoDB:

const mongoose = require('mongoose');

// Define a User schema and model
const userSchema = new mongoose.Schema({
  name: String,
  email: String,
  age: Number
});

const User = mongoose.model('User', userSchema);

describe('Database connection', () => {
  it('test', function () {

  });
});

Output:

$ ./node_modules/.bin/jest --detectOpenHandles
 PASS  ./index.test.js
  Database connection
    ✓ test (1 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.393 s
Ran all test suites.

Jest has detected the following 1 open handle potentially keeping Jest from exiting:

  ●  Timeout

       8 | });
       9 |
    > 10 | const User = mongoose.model('User', userSchema);
         |                       ^
      11 |
      12 | describe('Database connection', () => {
      13 |   it('test', function () {

      at node_modules/mongoose/lib/connection.js:838:19
      at NativeConnection._waitForConnect (node_modules/mongoose/lib/connection.js:837:7)
      at _createCollection (node_modules/mongoose/lib/model.js:1106:16)
      at Function.init (node_modules/mongoose/lib/model.js:1119:16)
      at Mongoose._model (node_modules/mongoose/lib/mongoose.js:667:9)
      at Mongoose.model (node_modules/mongoose/lib/mongoose.js:622:27)
      at Object.model (index.test.js:10:23)

Is there some other way that you've been able to repro this @hasezoey @Freezystem ? This just means there's some connection helpers that are buffered when tests are done, which likely means registering models or executing operations on a connection that was never opened.

@hasezoey
Copy link
Collaborator

hasezoey commented Feb 9, 2025

In my use-case for typegoose, the only issues happen when i try to set existingConnection to use a different connection to register models on than the mongoose default, which is literally a connection that is created but never connected.

Jest detected output for those lines
  ●  Timeout

      101 |     mongoose.model.bind(mongoose);
      102 |
    > 103 |   const compiledModel: mongoose.Model<any> = modelFn(name, buildSchema(cl, mergedOptions));
          |                                              ^
      104 |
      105 |   return addModelToTypegoose<U, QueryHelpers>(compiledModel, cl, {
      106 |     existingMongoose: mergedOptions?.existingMongoose,

      at node_modules/mongoose/lib/connection.js:838:19
      at NativeConnection._waitForConnect (node_modules/mongoose/lib/connection.js:837:7)
      at _createCollection (node_modules/mongoose/lib/model.js:1106:16)
      at Function.init (node_modules/mongoose/lib/model.js:1119:16)
      at Mongoose._model (node_modules/mongoose/lib/mongoose.js:667:9)
      at NativeConnection.model (node_modules/mongoose/lib/connection.js:1454:23)
      at getModelForClass (src/typegoose.ts:103:46)
      at Object.<anonymous> (test/tests/disableCache.test.ts:288:30)

This was working (no errors / open handles) before #15229.

For my use-case some alternatives could be:

  • allow disabling the error / timeout for automatic operations like the model init call in a .model call (because i wouldnt expect the timeout to actually happen until the connect, as model registration can happen way before connect)
  • disabling buffering on the connection (i havent tried, but wouldnt this mean init immediately errors?)

For now the unref would just restore the old behavior of not keeping the node process around at least, but still error if something else keeps the event loop busy (like awaiting a operation, but would this example really just exit in that case instead of erroring?)

Also kinda related but not exactly the same problem, wouldnt having the timeout in the .model case cause issues if the loading of the application (assuming the export Model = mongoose.model() pattern) takes longer than the bufferTimeoutMS (or in the case of disabled buffering immediately error)?

@vkarpov15
Copy link
Collaborator

One alternative would be to just disable autoCreate and autoIndex on the connection you don't intend to open, e.g. mongoose.createConnection(uri, { autoCreate: false, autoIndex: false }). That makes Model.init() not send any database operations. Or set autoCreate and autoIndex to false on your schema as follows, the following doesn't have any open handle warnings with the fix from #15247.

const mongoose = require('mongoose');

// Define a User schema and model
const userSchema = new mongoose.Schema({
  name: String,
  email: String,
  age: Number
}, { autoIndex: false, autoCreate: false });

const User = mongoose.model('User', userSchema);

describe('Database connection', () => {
  it('test', function () {

  });
});

The idea of buffering on .model() would be a reasonable one except for #15247, #12940: we need to buffer in init() to wait for connection options to come in, otherwise init() doesn't know whether autoCreate and autoIndex are enabled/disabled via connection level options.

Potential workaround: make init() buffer without buffer timeouts. That would prevent open handles, and not be entirely unreasonable since init() happens under the hood. What do you think @hasezoey ?

@hasezoey
Copy link
Collaborator

Potential workaround: make init() buffer without buffer timeouts. That would prevent open handles, and not be entirely unreasonable since init() happens under the hood. What do you think @hasezoey ?

I think it would be good to have automatic things not have a timeout.
(automatic as in "background tasks that are not required to be awaited", like everything in init, which is called through .model, a sync operation)

vkarpov15 added a commit that referenced this issue Feb 11, 2025
fix: avoid connection buffering on init if autoCreate: false
vkarpov15 added a commit that referenced this issue Feb 11, 2025
vkarpov15 added a commit that referenced this issue Feb 12, 2025
fix(model): avoid adding timeout on `Model.init()` buffering to avoid unintentional dangling open handles
@Freezystem
Copy link
Contributor Author

The only way I've been able to repro this is with creating a model but never connecting to MongoDB:

Yes that was exactly my case. It was in tests that were instanciating a mongoose instance but did not connect to the DB.

@vkarpov15
Copy link
Collaborator

Fix should be in 8.10.1, can you please upgrade and let me know if that fixed the issue @Freezystem ?

@Freezystem
Copy link
Contributor Author

It's fixed ! Thank you a lot 🙏🏼

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