Controlling Simultaneous Updates

Many users can execute server code simultaneously, so you sometimes need to implement the "exclusive control" feature. Unlike relational databases that use the transaction and locking for providing the exclusive control, Kii Cloud adopts the optimistic lock so as to control simultaneous execution. See Overwrite Check for more information about the optimistic lock.

Making your server code synchronous does not solve the simultaneous update issue. When multiple users execute your server code simultaneously, these requests will be executed in parallel.

To leverage the overwrite check, set the second parameter (overwrite) of the KiiObject.save method to false. An error message starting with OBJECT_VERSION_IS_STALE: will be returned if the update conflict occurs. In such a case, you can retry so as to update the data without losing the consistency.

The next code shows a sample of server code that returns unique sequence numbers (1, 2, 3, ...).

function main(params, context, done) {
  // Instantiate a KiiObject.
  var object = context.getAppAdminContext().objectWithURI('Set the URI of an existing KiiObject here');

  // Return the value of the sequence field.
  getSequence(object, 1, done);
}

function getSequence(object, loop, done) {
  // Return an error if the retry count is more than 100.
  if (loop > 100) {
    done("Server Busy");
    return;
  }

  // Refresh the KiiObject.
  object.refresh({
    success: function(object) {
      // Increment the value of the sequence field.
      var sequence = object.get('sequence') + 1;
      object.set('sequence', sequence);

      // Save the KiiObject.
      object.save({
        success: function(theObject) {
          // Return the incremented value of the sequence field.
          done(sequence);
        },
        failure: function(theObject, errorString) {
          // If the overwrite check returned an error
          if (errorString.split(":")[0] === "OBJECT_VERSION_IS_STALE") {
            // Increment the retry count and retry the processing.
            getSequence(object, loop + 1, done);
          } else {
            done("Unexpected Error:" + errorString);
            return;
          }
        }
      }, false);
    },
    failure: function(theObject, errorString) {
      done("Unexpected Error:" + errorString);
      return;
    }
  });
}

Here is the brief explanation of the code:

  • The main function is an endpoint for the manual operation. This function calls the getSequence method after creating a KiiObject. It is assumed that the objectWithURI() method takes the URI of a KiiObject that has the sequence field in the application scope as the argument.
  • The getSequence() method refreshes the KiiObject, increments the sequence field, and saves the KiiObject. The method returns the sequence number when the save process succeeds.
  • Since we set the overwrite parameter false in the KiiObject.save() method, the OBJECT_VERSION_IS_STALE error will be returned if the KiiObject is updated by another process after it is refreshed and before it is saved. If this is the case, the method recursively starts over from the refresh process.
  • To prevent the stack overflow, the retry is made only up to 100 times. If the retry fails 100 times, an error will be returned.

Simultaneous updates can often occur when you share the application scope and group scopes with multiple users. It could also occur in a user scope if a user logs in from multiple devices at the same time. How to deal with data consistency depends on the specification of your application. The application can, for example, retry the process to ensure consistency, allow inconsistency, or just throw an update error.

You can use the above procedure for the system to generate unique IDs such as vendorThingID. On the other hand, you can also let the client generate IDs. You can set a generated UUID or the current time as an ID and retry the process if the same ID has been used.