Skip to content

Commit 120b442

Browse files
authored
Merge pull request #14017 from Automattic/vkarpov15/gh-13973-2
fix(connection): retain modified status for documents created outside a transaction during transaction retries
2 parents 8a7f2f2 + a1dc45b commit 120b442

File tree

3 files changed

+40
-1
lines changed

3 files changed

+40
-1
lines changed

lib/connection.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,10 @@ function _resetSessionDocuments(session) {
563563
doc.$__.activePaths.states.modify = {};
564564
}
565565
for (const path of state.modifiedPaths) {
566+
const currentState = doc.$__.activePaths.paths[path];
567+
if (currentState != null) {
568+
delete doc.$__.activePaths[currentState][path];
569+
}
566570
doc.$__.activePaths.paths[path] = 'modify';
567571
doc.$__.activePaths.states.modify[path] = true;
568572
}

lib/document.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ const scopeSymbol = require('./helpers/symbols').scopeSymbol;
5353
const schemaMixedSymbol = require('./schema/symbols').schemaMixedSymbol;
5454
const parentPaths = require('./helpers/path/parentPaths');
5555
const getDeepestSubdocumentForPath = require('./helpers/document/getDeepestSubdocumentForPath');
56+
const sessionNewDocuments = require('./helpers/symbols').sessionNewDocuments;
5657

5758
let DocumentArray;
5859
let MongooseArray;
@@ -1474,7 +1475,16 @@ Document.prototype.$set = function $set(path, val, type, options) {
14741475

14751476
this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal);
14761477

1477-
if (savedState != null && savedState.hasOwnProperty(savedStatePath) && utils.deepEqual(val, savedState[savedStatePath])) {
1478+
const isInTransaction = !!this.$__.session?.transaction;
1479+
const isModifiedWithinTransaction = this.$__.session &&
1480+
this.$__.session[sessionNewDocuments] &&
1481+
this.$__.session[sessionNewDocuments].has(this) &&
1482+
this.$__.session[sessionNewDocuments].get(this).modifiedPaths &&
1483+
!this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath);
1484+
if (savedState != null &&
1485+
savedState.hasOwnProperty(savedStatePath) &&
1486+
(!isInTransaction || isModifiedWithinTransaction) &&
1487+
utils.deepEqual(val, savedState[savedStatePath])) {
14781488
this.unmarkModified(path);
14791489
}
14801490
}

test/docs/transactions.test.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,4 +396,29 @@ describe('transactions', function() {
396396
assert.equal(docs.length, 1);
397397
assert.equal(docs[0].name, 'test');
398398
});
399+
400+
it('transaction() retains modified status for documents created outside of the transaction then modified inside the transaction (gh-13973)', async function() {
401+
db.deleteModel(/Test/);
402+
const Test = db.model('Test', Schema({ status: String }));
403+
404+
await Test.createCollection();
405+
await Test.deleteMany({});
406+
407+
const { _id } = await Test.create({ status: 'test' });
408+
const doc = await Test.findById(_id);
409+
410+
let i = 0;
411+
await db.transaction(async(session) => {
412+
doc.status = 'test2';
413+
assert.ok(doc.$isModified('status'));
414+
await doc.save({ session });
415+
if (++i < 3) {
416+
throw new mongoose.mongo.MongoServerError({
417+
errorLabels: ['TransientTransactionError']
418+
});
419+
}
420+
});
421+
422+
assert.equal(i, 3);
423+
});
399424
});

0 commit comments

Comments
 (0)