简体   繁体   中英

mongodb concurrency on inserting document

Says we currently have two collections: Teachers and Students , we limit each teacher can only owns up to 30 students, surely I have already set a reference-key like studentsId , teachersId on each document.

When I'm about to create a new Student, how can I make sure I only create it when the specify teacher has less than 30 students?

// get students count under specify teacher
const StudentNums = await Teachers.findById(1234).studentsId.length;
if (StudentNums < 30) /* create student */

(above code is written JS)

undoubtedly above code would fail when concurrency, cause mongodb share read locks, any ideas? tks in advance.

You can apply standard Optimistic lock on application level.

Assuming a document in teacher collection has following fields:

db.teachers.insert({
    _id: 1234,
    students: [12,34],
    version: 8
});

Where _id and students are object's payload, and version is an internal field for concurrency control. The update in the question may look like following:

var teacher = db.teachers.findById(1234);

if(teacher.students.length < 30) {
    var res = db.teachers.update({_id: 1234, version: teacher.version}, {$push: {students: 56}, $inc: {version: 1}});
}

if(res.nModified < 1) {
    // apply retry logic
}

If the concurrent update happened, version of the document in the database won't match the query part of the update, which will result with 0 documents updated.

Instead of handling anything related to concurrency, why not just check if your teacher can accomodate any more students ? You can check and update in a single operation to ensure atomicity.

It essentially boils down to adding student length check in find query like below :

teachers.update({id: 1, '$where': 'this.students.length < 8'}, {'$push': {students: 8}}

Added a test case below:

 const mongodb = require('mongodb')
 const mongoClient = mongodb.MongoClient

 mongoClient.connect('mongodb://localhost:27017/stackoverflow', function (err, db) {
   if (err) {
     console.log(`DB connect error ${JSON.stringify(err)}`)
   } else {
     const teachers = db.collection('teachers')
     let doc = {id: 1, name: 'Ms Anderson', students: [1, 2, 3, 4, 5, 6, 7], subject: 'Matrix'}
     teachers.insert(doc, {w: 1}, function (err, result) {
       console.log(`insert error ${JSON.stringify(err)}`)
       console.log(`insert result : ${JSON.stringify(result)}`)
//Check the $where parameter, it will return only if student array has size less than 8
       teachers.update({id: 1, '$where': 'this.students.length < 8'}, {'$push': {students: 8}}, {w: 1}, function (err, result) {
         console.log(`update error ${JSON.stringify(err)}`)
         console.log(`update result : ${JSON.stringify(result)}`)
       })
     })
   }
 })

result is 1 if document is updated else 0.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM