0

Let me tell you a bit about my application and the problem: This application will be used to register students in a particular course launched by an organization. A student will provide the following in the form besides other information:

  • a course from available courses
  • his photo

He can print his admit card after the form has been successfully submitted.

Now for admin:

  • He can create as many courses as he want
  • He can toggle the courses' availablibility
  • He can choose where to start the roll number for a course
  • He can change the current roll number of a course

The steps taken after a student clicks submit are as follows:

  1. Get the course_id from the form, and get the current roll number of that course
  2. Save the image with the current roll number as its filename at path course_name/batch/roll_number.(png|jpg)
  3. Get the image if the image was successfully saved, reject otherwise
  4. Resize the image and overwrite the existing one, delete the image and reject if resizing failed
  5. Save the student's form in forms collection
  6. increment the current roll number of that course in courses collection
  7. send the response containing the form data along with the image's base64 string (will be used in admit card)

The Problem:

The problem is that it can generate duplicate roll numbers if users submit form almost at the same time since they will be using the same current roll number. I am not being able to solve this problem as I can't simply auto-increment the form's roll_number since it is coming from the courses collection.

Here's how my database looks like:

{   
    "courses":[
        {
            "_id" : ObjectId,
            "batches": [Object],
            "course_name" : String,
            "current": Number, // current roll number
            "start" : Number, // starting roll number
            "available": Boolean
        }
    ],
    "forms": [
        {
            "_id": ObjectId,
            "roll_number": Number,
            "course_id" : ObjectId
            // other stuff
        }
    ],
    "settings": [ // always have one object
        {
            "_id" : ObjectId,
            "accept_form": Boolean,
            "courses": [ObjectId]
        }
    ]
}

I am using:

multer for saving image : https://github.com/expressjs/multer

sharp for resizing image : https://github.com/lovell/sharp

expressjs for handling requests

Please tell me what should I do to solve this duplication problem.

Saikat Chakrabortty
  • 2,520
  • 4
  • 22
  • 39
Farooq AR
  • 314
  • 4
  • 9

1 Answers1

0

Unfortunately, there is no easy way to do that in MongoDB. However, there are various strategies you can use, to make sure you don't end up with duplicate roll numbers in your form collection.

First of all, make sure you have a unique index set on the roll_number field. At least mongo will let you know when you are trying to insert duplicate values.

Method #1

Create a loop that does the following:

  1. Read the current roll number from the courses collection, and add to the form object
  2. Try to save the form object and check if mongo db returns a duplicate error
  3. If the roll number has already been used, increment the course current number
  4. Repeat step 1

NOTE: Depending on how many users are likely to be concurrently saving a form, above method could prove to be quite exhaustive. But for a system with medium number of users, the probability of having more that one form saving operation happening exactly at the same fraction of a second should be very small, and this method should provide acceptable results.

Method #2

Another viable, but more difficult approach is to pass on the roll number assignment task to a SINGLE background worker thread once the form has been saved.

In nodeJs, this can easily be implemented using a priority job queue library such as Kue, which by default will queue your jobs, and execute them one at a time, (therefore assigning roll number to one form at a time) so that you don't have to worry about concurrency.

In a typical implementation, once the form object has successfully been persisted, you will create a Job for assigning a roll number and add it to the queue.

NOTE: Since roll number assignment will be done asynchronously in the background after response has been returned back to the client, you will need to implement a mechanism (i.e using web sockets, or polling) to let the client know when roll number has been assigned and display it on the UI.

There are possibly other easier-to-implement approaches than what I have described here. But top of my head, that is what I can think of.

You should also check out article Creating and Auto-Incrementing Sequence Field from MongoDB docs

bentesha
  • 898
  • 9
  • 14