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

MongoError: writeConcern is not allowed within a multi-statement transaction #11382

Closed
MikeKoval opened this issue Feb 11, 2022 · 9 comments · Fixed by #14391
Closed

MongoError: writeConcern is not allowed within a multi-statement transaction #11382

MikeKoval opened this issue Feb 11, 2022 · 9 comments · Fixed by #14391
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Milestone

Comments

@MikeKoval
Copy link

MikeKoval commented Feb 11, 2022

Hi!

I have 5.13.14 mongoose.

My rs db connection string contains retryWrites=true&w=majority&wtimeout=15000

My code looks like this:

const session = await mongoose.startSession();

await session
      .withTransaction(
        () => Promise.all([
 User.updateOne({ _id: userId }, { $set: userFields }, { session, }).exec()),
CustomFieldValue.bulkWrite(ops, { session }),
]),
      )
      .catch((err) => {
        session.endSession();
        console.error(err);
        throw err;
      })
      .then(() => {
        session.endSession();
      });

It throws

MongoError: writeConcern is not allowed within a multi-statement transaction
    at MessageStream.messageHandler (/home/mike/app/node_modules/mongoose/node_modules/mongodb/lib/cmap/connection.js:299:20)
    at MessageStream.emit (events.js:314:20)
    at MessageStream.EventEmitter.emit (domain.js:483:12)
    at processIncomingData (/home/mike/app/node_modules/mongoose/node_modules/mongodb/lib/cmap/message_stream.js:144:12)
    at MessageStream._write (/home/mike/app/node_modules/mongoose/node_modules/mongodb/lib/cmap/message_stream.js:42:5)
    at doWrite (_stream_writable.js:403:12)
    at writeOrBuffer (_stream_writable.js:387:5)
    at MessageStream.Writable.write (_stream_writable.js:318:11)
    at TLSSocket.ondata (_stream_readable.js:718:22)
    at TLSSocket.emit (events.js:314:20)
    at TLSSocket.EventEmitter.emit (domain.js:483:12)
    at addChunk (_stream_readable.js:297:12)
    at readableAddChunk (_stream_readable.js:272:9)
    at TLSSocket.Readable.push (_stream_readable.js:213:10)
    at TLSWrap.onStreamRead (internal/stream_base_commons.js:188:23) {
  operationTime: Timestamp { _bsontype: 'Timestamp', low_: 7, high_: 1644575976 },
  ok: 0,
  code: 72,
  codeName: 'InvalidOptions',
  '$clusterTime': {
    clusterTime: Timestamp { _bsontype: 'Timestamp', low_: 7, high_: 1644575976 },
    signature: { hash: [Binary], keyId: [Long] }
  }
}

Why? Does mongoose apply write concern for single operation even if they are run in session?

Would be grateful if you could help.

@MikeKoval
Copy link
Author

Screenshot from 2022-02-11 12-57-58
I played a bit with debugger and i found that conflicting options are actually passed to mongo...

@MikeKoval
Copy link
Author

I think there is an issue with bulkWrite as code works fine without it, even if promise array contains only bulkWrite promise - it fails with same error...

@IslandRhythms IslandRhythms added the help This issue can likely be resolved in GitHub issues. No bug fixes, features, or docs necessary label Feb 11, 2022
@Uzlopak
Copy link
Collaborator

Uzlopak commented Feb 11, 2022

Is an Upgrade to v6 not an alternative?

@vkarpov15 vkarpov15 added this to the 6.2.4 milestone Feb 12, 2022
@MikeKoval
Copy link
Author

MikeKoval commented Feb 13, 2022

I'm getting same error when i updated to 6.2.1 version.
If i debug slowly, it appears working

@Uzlopak
Copy link
Collaborator

Uzlopak commented Feb 13, 2022

I think this is maybe connected with one bug I reported.

#11115 (comment)

@MikeKoval
Copy link
Author

MikeKoval commented Feb 13, 2022

Thanks, it works if i execute promises in serial inside withTransaction. However i haven't noticed that somebody forbidden parallel operations. I think something might be wrong with concurrency and transactions not mongoose but in mongo driver itself, so i will keep it opened...

@Uzlopak
Copy link
Collaborator

Uzlopak commented Feb 13, 2022

Yes this is a common issue in mongodb, as it seems that all drivers are effected. We could theoretically avoid this by doing some no op operation in transaction when using mongoose specific transaction helper. But on the other hand it would slow down the whole transaction even if you don't have the issue of parallel db operations like when you know what you are doing.

Probably mongo wants to optimize the database transactions and the locks by starting them actually with the first transaction related operation.
And this is why all drivers are suffering from the same issue.

We could kind of track in mongoose specific transaction wrapper that if we are in transactions that we buffer the operations and run the first operation in session separately and delay the rest till the first operation in session is done. Maybe @vkarpov15 has some idea on this.

@vkarpov15 vkarpov15 modified the milestones: 6.2.4, 6.2.5 Feb 24, 2022
@vkarpov15 vkarpov15 modified the milestones: 6.2.5, 6.2.6, 6.2.10 Mar 9, 2022
@vkarpov15
Copy link
Collaborator

I'm unable to repro this, the below script executes without error. Please modify the below script to demonstrate the issue you're seeing.

'use strict';
  
const mongoose = require('mongoose');

mongoose.set('debug', true);

const { Schema } = mongoose;

run().catch(err => console.log(err));

async function run() {
  await mongoose.connect('mongodb://localhost:27017,localhost:27018/test?replicaSet=rs&w=majority&wtimeout=15000', { autoCreate: true });

  await mongoose.connection.dropDatabase();

  const schema = new Schema({ name: String });
  const Model = mongoose.model('Test', schema);
  await Model.init();

  const session = await mongoose.startSession();

  await session.withTransaction(async function() {
    await Promise.all([
      Model.updateOne({}, { name: 'test' }, { session }),
      Model.bulkWrite([
        {
          insertOne: {
            document: {
              name: 'Eddard Stark',
              title: 'Warden of the North'
            }
          }
        },
      ], { session })
    ]);
  });

  await session.endSession();

  console.log('Done', await Model.findOne());
}

@vkarpov15 vkarpov15 removed this from the 6.2.10 milestone Apr 4, 2022
@vkarpov15 vkarpov15 added can't reproduce Mongoose devs have been unable to reproduce this issue. Close after 14 days of inactivity. and removed help This issue can likely be resolved in GitHub issues. No bug fixes, features, or docs necessary labels Apr 4, 2022
@ms76
Copy link

ms76 commented Feb 26, 2024

@vkarpov15 This is an old issue, but I am commenting because I reproduced the error. Although different from the original code, the following code results in an error.

'use strict';
  
const mongoose = require('mongoose');

mongoose.set('debug', true);

const { Schema } = mongoose;

run().catch(err => console.log(err));

async function run() {
  await mongoose.connect('mongodb://localhost:27017,localhost:27018/test?replicaSet=rs&w=majority&wtimeout=15000', { autoCreate: true });

  await mongoose.connection.dropDatabase();

  const schema = new Schema({ name: String }, { writeConcern: { w: 'majority' }});
  const Model = mongoose.model('Test', schema);
  await Model.init();

  const session = await mongoose.startSession();

  await session.withTransaction(async function() {
    await Promise.all([
      Model.findOneAndUpdate({}, { name: 'test' }, { session }),
      Model.bulkWrite([
        {
          insertOne: {
            document: {
              name: 'Eddard Stark',
              title: 'Warden of the North'
            }
          }
        },
      ], { session })
    ]);
  });

  await session.endSession();

  console.log('Done', await Model.findOne());
}
@@ -13,7 +13,7 @@
 
   await mongoose.connection.dropDatabase();
 
-  const schema = new Schema({ name: String });
+  const schema = new Schema({ name: String }, { writeConcern: { w: 'majority' }});
   const Model = mongoose.model('Test', schema);
   await Model.init();
 
@@ -21,7 +21,7 @@
 
   await session.withTransaction(async function() {
     await Promise.all([
-      Model.updateOne({}, { name: 'test' }, { session }),
+      Model.findOneAndUpdate({}, { name: 'test' }, { session }),
       Model.bulkWrite([
         {
           insertOne: {

With the Schema option set to writeConsern, if there is a findOneAndUpdate in the transaction, it is an error. If updateOne, it is not an error.
My environment is Node.js v18.19.0, Mongoose v6.12.6, MongoDB 4.4.24.

@vkarpov15 vkarpov15 reopened this Feb 27, 2024
@vkarpov15 vkarpov15 added has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue and removed can't reproduce Mongoose devs have been unable to reproduce this issue. Close after 14 days of inactivity. labels Feb 27, 2024
@vkarpov15 vkarpov15 added this to the 6.12.7 milestone Feb 27, 2024
@vkarpov15 vkarpov15 added confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. and removed has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue labels Feb 28, 2024
vkarpov15 added a commit that referenced this issue Feb 28, 2024
@vkarpov15 vkarpov15 modified the milestones: 6.12.7, 8.2.1 Feb 28, 2024
vkarpov15 added a commit that referenced this issue Feb 29, 2024
fix(schema): avoid applying default write concern to operations that are in a transaction
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Projects
None yet
5 participants