Search code examples
javascriptparse-platformparse-server

Parse server atomic increment result


In my Parse backend I have an array that contains unique number codes, so users must not be able to get the same code twice. For that reason somewhere in a column of some table I am keeping an index to this array.

Now there is a very simple operation - users ask for a unique code. The cloud function increments the current value of the index and returns the value of the array at the new index. The problem is that at first glance the Parse JS API has only increment operation performed atomically, but not the following read operation, since increment doesn't return a promise with a value which was set during THAT increment.

Now imagine the following scenario (pseudocode):

Field index has value 76, two users try to get the next code at the same time:

User1 -> increment('index') -> save -> then(obj1) -> return array[obj1.index]

User2 -> increment('index') -> save -> then(obj2) -> return array[obj2.index]

Now atomic increment will guarantee that after these 2 calls the index column will have value 78. But what about obj1 and obj2? If their value reading was not done atomically together with the increment operation, but was done through fetching after the increment was performed, then they both might have value 78! And the whole uniqueness logic will be broken.

Is there a way to get the atomic write operation result in Parse?


Solution

  • Increment does return the final value that was atomically incremented:

    1. First the unit test to show how it is used:

      fit('increment', (done) => {
          new Parse.Object('Counter')
            .set('value', 1)
            .save()
            .then(result => {
              console.log('just saved', JSON.stringify(result));
              return result
                .increment('value')
                .save();
            })
            .then(result => {
              console.log('incremented', JSON.stringify(result))
              expect(result.get('value')).toBe(2);
              done();
            })
            .catch(done.fail);
        });
      
    2. Behind the scenes, here's what's happening (if you're using mongo, there's similar for postgress too):

      • MongoAdapter turns into a mongo $inc operation which is returned
      • Mongo documentation explaining $inc which includes "$inc is an atomic operation within a single document."