whole_func_string: function doDelete(datasetId, pendingChange, callback) {
    debug('[%s] DELETE Start', datasetId);
    var metaData = pendingChange.meta_data;
    var uid = pendingChange.uid;
    dataHandlers.doRead(datasetId, uid, metaData, function(err, data) {
      if (err) {
        debugError('READ for DELETE Failed - uid = %s : err = %s', datasetId, uid, err);
        return saveUpdate(datasetId, pendingChange, SYNC_UPDATE_TYPES.FAILED, util.inspect(err), callback);
      }
      debug('[%s] READ for DELETE Success', datasetId);
      debug('[%s] READ for DELETE Data : \n%j', datasetId, data);
  
      var preHash = hashProvider.recordHash(datasetId, pendingChange.pre);
      var dataHash = hashProvider.recordHash(datasetId, data);
  
      debug('[%s] DELETE Hash Check %s (client :: dataStore) = %s :: %s', datasetId, uid, preHash.dataHash);
  
      if (!dataHash) {
        //record has already been deleted
        debug('[%s] DELETE Already performed - uid=%s', datasetId, uid);
        return saveUpdate(datasetId, pendingChange, SYNC_UPDATE_TYPES.APPLIED, null, callback);
      }
      else {
        if (preHash === dataHash) {
          dataHandlers.doDelete(datasetId, uid, metaData, function(err) {
            if (err) {
              debugError('[%s] DELETE Failed - uid=%s : err = %s', datasetId, err);
            } else {
              debug('[%s] DELETE Success - uid=%s : hash = %s', datasetId, dataHash);
            }
            return saveUpdate(datasetId, pendingChange, err ? SYNC_UPDATE_TYPES.FAILED : SYNC_UPDATE_TYPES.APPLIED, err ? util.inspect(err) : null, callback);
          });
        } else {
          debug('[%s] DELETE COLLISION \n Pre record from client:\n%j\nCurrent record from data store:\n%j', datasetId, syncUtil.sortObject(pendingChange.pre), syncUtil.sortObject(data));
          handleCollision(datasetId, metaData, pendingChange, dataHash, function(err) {
            if (err) {
              debugError('[%s] Failed to save collision uid = %s : err = %s', datasetId, err);
            }
          });
          return saveUpdate(datasetId, pendingChange, SYNC_UPDATE_TYPES.COLLISION, null, callback);
        }
      }
    });
  }
func_code_string: function doDelete(datasetId, pendingChange, callback) {
    debug('[%s] DELETE Start', datasetId);
    var metaData = pendingChange.meta_data;
    var uid = pendingChange.uid;
    dataHandlers.doRead(datasetId, uid, metaData, function(err, data) {
      if (err) {
        debugError('READ for DELETE Failed - uid = %s : err = %s', datasetId, uid, err);
        return saveUpdate(datasetId, pendingChange, SYNC_UPDATE_TYPES.FAILED, util.inspect(err), callback);
      }
      debug('[%s] READ for DELETE Success', datasetId);
      debug('[%s] READ for DELETE Data : \n%j', datasetId, data);
  
      var preHash = hashProvider.recordHash(datasetId, pendingChange.pre);
      var dataHash = hashProvider.recordHash(datasetId, data);
  
      debug('[%s] DELETE Hash Check %s (client :: dataStore) = %s :: %s', datasetId, uid, preHash.dataHash);
  
      if (!dataHash) {
        //record has already been deleted
        debug('[%s] DELETE Already performed - uid=%s', datasetId, uid);
        return saveUpdate(datasetId, pendingChange, SYNC_UPDATE_TYPES.APPLIED, null, callback);
      }
      else {
        if (preHash === dataHash) {
          dataHandlers.doDelete(datasetId, uid, metaData, function(err) {
            if (err) {
              debugError('[%s] DELETE Failed - uid=%s : err = %s', datasetId, err);
            } else {
              debug('[%s] DELETE Success - uid=%s : hash = %s', datasetId, dataHash);
            }
            return saveUpdate(datasetId, pendingChange, err ? SYNC_UPDATE_TYPES.FAILED : SYNC_UPDATE_TYPES.APPLIED, err ? util.inspect(err) : null, callback);
          });
        } else {
          debug('[%s] DELETE COLLISION \n Pre record from client:\n%j\nCurrent record from data store:\n%j', datasetId, syncUtil.sortObject(pendingChange.pre), syncUtil.sortObject(data));
          handleCollision(datasetId, metaData, pendingChange, dataHash, function(err) {
            if (err) {
              debugError('[%s] Failed to save collision uid = %s : err = %s', datasetId, err);
            }
          });
          return saveUpdate(datasetId, pendingChange, SYNC_UPDATE_TYPES.COLLISION, null, callback);
        }
      }
    });
  }
func_documentation_string: Delete the given pending change from the backend.
  It will only delete the record if the hash value of the `pre` record matches the hash value of the record from backend.
  Otherwise the change is either already applied, or a collision will be generated.
  @param {String} datasetId the dataset id
  @param {Object} pendingChange the pending change. It should have the following fields
  @param {String} pendingChange.datasetId the dataset id of the pending change
  @param {Object} pendingChange.meta_data the meta_data of the dataset
  @param {String} pendingChange.action the action of the pending change, should be "delete"
  @param {String} pendingChange.uid the unique id of the data record
  @param {String} pendingChange.cuid the unique client id of the client device
  @param {String} pendingChange.hash a unique id of the pending change, normally it is a hash value generated from the content of the pendingChange object
  @param {Object} pendingChange.pre the record before change.
  @param {Function} callback