Auto increment reference number


#1

Hello! I have “Reports” collection, and have been incrementing a reference number for each report added by using Simple Schema autovalue.

However one time I got duplicated reference numbers, maybe because 2 reports have been created at the same time. How can I avoid this?

  refNumber: {
    type: String,
    label: 'Reference Number',
    autoValue() {
      if (this.isInsert) {
        const organizationId = this.field('organizationId');

        if (organizationId.isSet) {
          const lastReport = Reports.findOne({ organizationId: organizationId.value }, { sort: { createdAt: -1 } });
          if (!lastReport) {
            const refNumber = '000001';
            return refNumber;
          }
          const lastRefNumber = Number(lastReport.refNumber);
          const refNumber = String('00000' + (lastRefNumber + 1)).slice(-6);
          return refNumber;
        }
      }

      this.unset();
    },
  },

#2

I think you need to handle the error if write fail, then try to insert again. I don’t know how to do it in simple schema only.
Maybe you can try other kind of refNumber, use random combine letters and numbers?


#3

Actually the insert will never fail, it will always insert but with a risk of being the same reference number of another one if done simultaneously. Maybe I should add a verification in a before.insert hook.


#4

That will also run the risk of duplication, because the check-and-set in that scenario is not atomic.

The best way to do what you want, which will work with multiple server instances, is to let MongoDB do it for you. Here’s one way:

// this will only work on the server
import { Promise } from 'meteor/promise';
const getNextSequenceNumber = (collection) => {
  return Promise.await(collection.rawCollection().findOneAndUpdate(
    { _id: 'next-autoincrement' }, 
    { $inc: { currentValue: 1 } }, 
    { upsert: true, returnOriginal: false })).value.currentValue;
 }
}

That works by adding one new document to each collection you call the getNextSequenceNumber on. The new document has a structure of:

{
  _id: 'next-autoincrement',
  currentValue: <n>
}

where increments from 0 each time the function is called. If you don’t use the number given in a document it’s not repeated again. The increment is independent of usage. Most importantly, it’s atomic and can be called simultaneously from any application against that collection and will always return the next number once only.


#5

There’s also the possibility of using GUIDs if you’re not absolutely dependant on a strict sequence of numbers.

If you use a proper GUID generator you’ll experience the heat death of the universe before a collision of ids :slight_smile: