question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Save on condition (Optimistic concurrency)

See original GitHub issue

Background

This is a proposal for optimistic concurrency for mongoose.

We sometimes use the following code to modify the document instead of update because validation is not ok (runValidator doesn’t support everything)

let doc = yield Model.findById(id);
doc.prop = 123;
doc.val += 5;
yield doc.save();

However this is troublesome when another request is also modifying this document, it may cause the doc overwritten without any errors. I don’t know what to call this phenomenon, maybe race condition

Optimistic Concurrency Control

A better solution is that, when reading the model, store some important values, e.g. updatedAt or __v version key.

and when updating, add a condition that the timestamp should be the same. This mechanism is called Optimistic concurrency control, or OCC or update-if-current.

Reference: Mongodb Official document: Update Document if Current

let doc = yield Model.findById(id);
let timestamp = doc.updatedAt;

let updateProps = {
  prop: 123,
  val: doc.val + 5
};

// update only when timestamp match the original one
let updater = yield Model.update({ _id: doc._id, updatedAt: timestamp }, { $set: updateProps });

if (updater.result.nMatched === 0) {
   // if update operation doesn't match, it means this document is modified by others or is removed.
   throw new OCCError('This document is modified by others at this moment. Please try again');
}

//successful!

However, this is very ugly. Hence I propose an elegant way in mongoose.

Proposal 1 (General)

let doc = yield Model.findById(id);
doc.prop = 123;
doc.val += 5;
yield doc.save({cond: {updatedAt: doc.updatedAt }}); // if condition doesn't match, throw an error.

Proposal 2 (Native Support)

The previous proposal is a general way. Maybe we can include occ during find:

let doc = yield Model.findById(id).occ('updatedAt');
doc.prop = 123;
doc.val += 5;
yield doc.save(); //an OCCError is thrown if `updatedAt` doesn't match the first one 

If there are multiple values needed to be checked, use this way

let doc = yield Model.findById(id).occ('firstProp secondProp')
// or
let doc = yield Model.findById(id).occ('firstProp').occ('secondProp')
// prop in subdocument
let doc = yield Model.findById(id).occ('parent.child')

Proposal 3 (Schema options)

new Schema({..}, { saveIfCurrent: '__v' });
new Schema({..}, { saveIfCurrent: 'updatedAt parent.child' });
schema.set('saveIfCurrent', 'updatedAt parent.child');

update-if-current is the formal name. Here, we emphasize that this behavior is working on save-if-current.

Issue Analytics

  • State:closed
  • Created 8 years ago
  • Reactions:3
  • Comments:16 (2 by maintainers)

github_iconTop GitHub Comments

3reactions
vkarpov15commented, Jan 4, 2019

@lonix1 we added the ability to implement OCC plugins, and it looks like mongoose-update-if-current does exactly what we suggest. It looks like a solid plugin 👍 Caveat is that it only handles save(), not updateOne(), etc.

OCC hasn’t really been a concern because for most apps I’ve worked on, Mongoose’s ability to only update the paths that have actually changed is good enough to prevent accidentally overwriting.

2reactions
vkarpov15commented, Feb 1, 2017

https://github.com/Automattic/mongoose/commit/8b4870c18c49c3bd1581c6a470ccf107addfb5f3 should give you the general direction of how one would write a plugin for this

Read more comments on GitHub >

github_iconTop Results From Across the Web

Optimistic locking with version number ... - AWS Documentation
Optimistic locking is a strategy to ensure that the client-side item that you are updating (or deleting) is the same as the item...
Read more >
Optimistic concurrency (Microsoft Dataverse) - Power Apps
Optimistic concurrency provides the ability for your applications to detect whether a table record has changed on the server in the time ...
Read more >
DynamoDB Optimistic vs Pessimistic Locking [The Ultimate ...
Optimistic Locking is a locking strategy offered by DynamoDB to detect whether a record is modified prior to updating it.
Read more >
Optimistic Locking in JPA - Baeldung
OPTIMISTIC_INCREMENT must meet the same conditions as OPTIMISTIC lock mode. Additionally, it increments the value of a version attribute.
Read more >
How to handle concurrent updates with optimistic and ...
Also, if the number of concurrent updates is very high, then pessimistic locking gives more reliable results as compared to optimistic locking.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found