Is there any way to force mongoose to always validate document versions on save? As far as I can tell, the default behavior enforces the version number only when modifying arrays in the document. Even worse than that, it seems that adding an element to an array is allowed even when the document versions do not match, so that currently, even if you're modifying an array, you need to replace the array in order to get the version check. (Note that the examples I'm using use schemaless subdocuments (defined simply as "{}") which may be affecting the behavior). Other than this article I can't find any documentation on the topic. Perhaps there's a plugin that does this?
With Optimistic Concurrency The idea behind optimistic concurrency is to track the version of the document and increment the version every time you save() . If the version of the document in the database changed between when you loaded the document and when you save() , Mongoose will throw an error.
The versionKey is a property set on each document when first created by Mongoose. This keys value contains the internal revision of the document. The name of this document property is configurable.
Optimistic concurrency control transactions involve these phases: Begin: Record a timestamp marking the transaction's beginning. Modify: Read database values, and tentatively write changes. Validate: Check whether other transactions have modified data that this transaction has used (read or written).
Mongoose uses the model name, as passed when it was created: mongoose. model("User", UserSchema) , converted to lower case and with an 's' appended. For the model User it uses the collection users by default. You can change this by explicitly specifying the collection name in the schema.
Disclaimer: I wrote the plugin myself to tackle this issue in my own code, based on this issue on GitHub.
This is late, but hopefully better than never.
The mongoose-update-if-current plugin might give you the functionality you're looking for. It adds optimistic concurrency control when calling .save()
on a document, using the version field. It'll increment __v
each time the document is saved, and will prevent different versions from being saved over each other. For example:
// saves with __v = 0
let product = await new Product({ name: 'apple pie' }).save();
// query a copy of the document for later (__v = 0)
let oldProduct = await Product.findById(product._id);
// increments to __v = 1
product.name = 'mince pie';
product = await product.save();
// throws an error due to __v not matching the DB version
oldProduct.name = 'blueberry pie';
oldProduct = await oldProduct.save();
As a result, we now have optimistic concurrency control for Product
.
Caveat: The plugin validates the version in .save()
only, not in static model methods such as Product.findByIdAndUpdate()
.
Mongoose 5.10.0 was released with optimisticConcurrency
option for schemas.
const userSchema = Schema({
name: {
type: String,
required: true,
},
// ...
}, {
optimisticConcurrency: true,
// versionKey: 'version' // => Default: __v
});
Probably, you need connection transactions too. Read more at https://mongoosejs.com/docs/api/connection.html#connection_Connection-transaction
const doc = new Person({ name: 'Will Riker' });
await db.transaction(async (session) => {
doc.rank = 'Captain';
await doc.save({ session });
doc.isNew; // false
// Throw an error to abort the transaction
throw new Error('Oops!');
},{ readPreference: 'primary' }).catch(console.error);
Source: https://thecodebarbarian.com/whats-new-in-mongoose-5-10-optimistic-concurrency.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With