diff --git a/lib/connection.js b/lib/connection.js index 542bdf3c572..05ff52461b0 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -829,6 +829,30 @@ Connection.prototype.openUri = async function openUri(uri, options) { return this; }; +/*! + * Treat `on('error')` handlers as handling the initialConnection promise + * to avoid uncaught exceptions when using `on('error')`. See gh-14377. + */ + +Connection.prototype.on = function on(event, callback) { + if (event === 'error' && this.$initialConnection) { + this.$initialConnection.catch(() => {}); + } + return EventEmitter.prototype.on.call(this, event, callback); +}; + +/*! + * Treat `once('error')` handlers as handling the initialConnection promise + * to avoid uncaught exceptions when using `on('error')`. See gh-14377. + */ + +Connection.prototype.once = function on(event, callback) { + if (event === 'error' && this.$initialConnection) { + this.$initialConnection.catch(() => {}); + } + return EventEmitter.prototype.once.call(this, event, callback); +}; + /*! * ignore */ diff --git a/lib/helpers/schema/applyWriteConcern.js b/lib/helpers/schema/applyWriteConcern.js index fcce08f8410..27098110872 100644 --- a/lib/helpers/schema/applyWriteConcern.js +++ b/lib/helpers/schema/applyWriteConcern.js @@ -6,6 +6,12 @@ module.exports = function applyWriteConcern(schema, options) { if (options.writeConcern != null) { return; } + // Don't apply default write concern to operations in transactions, + // because setting write concern on an operation in a transaction is an error + // See: https://www.mongodb.com/docs/manual/reference/write-concern/ + if (options && options.session && options.session.transaction) { + return; + } const writeConcern = get(schema, 'options.writeConcern', {}); if (Object.keys(writeConcern).length != 0) { options.writeConcern = {}; diff --git a/test/connection.test.js b/test/connection.test.js index 9ea81e356d0..3d1170838cf 100644 --- a/test/connection.test.js +++ b/test/connection.test.js @@ -916,6 +916,21 @@ describe('connections:', function() { assert.equal(err.name, 'MongooseServerSelectionError'); }); + it('avoids unhandled error on createConnection() if error handler registered (gh-14377)', async function() { + const opts = { + serverSelectionTimeoutMS: 100 + }; + const uri = 'mongodb://baddomain:27017/test'; + + const conn = mongoose.createConnection(uri, opts); + await new Promise(resolve => { + conn.on('error', err => { + assert.equal(err.name, 'MongoServerSelectionError'); + resolve(); + }); + }); + }); + it('`watch()` on a whole collection (gh-8425)', async function() { this.timeout(10000); if (!process.env.REPLICA_SET) { diff --git a/test/docs/lean.test.js b/test/docs/lean.test.js index 4512c7ced7f..e571987864b 100644 --- a/test/docs/lean.test.js +++ b/test/docs/lean.test.js @@ -42,7 +42,7 @@ describe('Lean Tutorial', function() { const leanDoc = await MyModel.findOne().lean(); v8Serialize(normalDoc).length; // approximately 180 - v8Serialize(leanDoc).length; // 32, about 5x smaller! + v8Serialize(leanDoc).length; // approximately 55, about 3x smaller! // In case you were wondering, the JSON form of a Mongoose doc is the same // as the POJO. This additional memory only affects how much memory your @@ -50,7 +50,7 @@ describe('Lean Tutorial', function() { JSON.stringify(normalDoc).length === JSON.stringify(leanDoc).length; // true // acquit:ignore:start assert.ok(v8Serialize(normalDoc).length >= 150 && v8Serialize(normalDoc).length <= 200, v8Serialize(normalDoc).length); - assert.equal(v8Serialize(leanDoc).length, 32); + assert.ok(v8Serialize(leanDoc).length === 55 || v8Serialize(leanDoc).length === 32, v8Serialize(leanDoc).length); assert.equal(JSON.stringify(normalDoc).length, JSON.stringify(leanDoc).length); // acquit:ignore:end }); diff --git a/test/docs/transactions.test.js b/test/docs/transactions.test.js index 5d0277a5aa4..8b883e3388c 100644 --- a/test/docs/transactions.test.js +++ b/test/docs/transactions.test.js @@ -477,4 +477,20 @@ describe('transactions', function() { assert.equal(i, 3); }); + + it('doesnt apply schema write concern to transaction operations (gh-11382)', async function() { + db.deleteModel(/Test/); + const Test = db.model('Test', Schema({ status: String }, { writeConcern: { w: 'majority' } })); + + await Test.createCollection(); + await Test.deleteMany({}); + + const session = await db.startSession(); + + await session.withTransaction(async function() { + await Test.findOneAndUpdate({}, { name: 'test' }, { session }); + }); + + await session.endSession(); + }); });