Lack of LockService

The LockService documentation: https://developers.google.com/apps-script/service_lock says that "getPublicLock () - obtains a lock that prevents concurrent access to a section of code by concurrent executions for the current user"

So the request is around the comment: " code section ". If I have multiple sections of code that use LockService.getPublicLock (), are they essentially independent locks?

For example:

function test1() {
    var lock = LockService.getPublicLock();

    if (lock.tryLock(10000)) {
        // Do some critical stuff
        lock.releaseLock();
    }
}


function test2() {
    var lock = LockService.getPublicLock();

    if (lock.tryLock(10000)) {
        // Do some critical stuff
        lock.releaseLock();
    }
}

      

If I have two calls to my script executing concurrently, with one user accessing test1 () and another user accessing test2 (), will they both succeed? Or, as he hints at this post: http://googleappsdeveloper.blogspot.co.uk/2011/10/concurrency-and-google-apps-script.html - are these locks just at the script level? So, for this scenario, only one of test1 () or test2 () will succeed, but not both.

If this is indeed the case as stated in the documentation and both succeed, what does "code section" mean? Are the line numbers pointed to by LockService.getPublicLock (), or is this a surrounding function?

+2


source to share


1 answer


There is only one open lock and only one closed lock.

If you want to have multiple locks, you will need to implement some kind of naming service by name. Example below using the script database functionality:

var validTime = 60*1000;  // maximum number of milliseconds for which a lock may be held
var lockType = "Named Locks";  // just a type in the database to identify these entries
function getNamedLock( name ) {
  return {
    locked: false,
    db : ScriptDb.getMyDb(),
    key: {type: lockType, name:name },
    lock: function( timeout ) {
      if ( this.locked ) return true;
      if ( timeout===undefined ) timeout = 10000;
      var endTime = Date.now()+timeout;
      while ( (this.key.time=Date.now()) < endTime ) {
        this.key = this.db.save( this.key );
        if ( this.db.query( 
              {type: lockType, 
               name:this.key.name, 
               time:this.db.between( this.key.time-validTime, this.key.time+1 ) }
            ).getSize()==1 )
          return this.locked = true;        // no other or earlier key in the last valid time, so we have it
        db.remove( this.key );              // someone else has, or might be trying to get, this lock, so try again
        Utilities.sleep(Math.random()*200); // sleep randomly to avoid another collision
      }
      return false;
    },
    unlock: function () {
      if (this.locked) this.db.remove(this.key);
      this.locked = false;
    }
  }
}

      

To use this service, we would do something like:



var l = getNamedLock( someObject );
if ( l.lock() ) {
  // critical code, can use some fields of l for convenience, such as
  // l.db - the database object
  // l.key.time - the time at which the lock was acquired
  // l.key.getId() - database ID of the lock, could be a convenient unique ID
} else {
  // recover somehow
}
l.unlock();

      

Notes:

  • This assumes that the db.save () database operation is essentially indivisible - I think it should be, because it would be a big problem in normal use otherwise.

  • Since the time is in milliseconds, we must assume that more than one task might be trying to block with the same timestamp, otherwise the function could be simplified.

  • We'll assume that locks are never held for more than one minute (but you can change that), since the runtime will stop your script anyway.

  • Periodically, you should remove all locks from the database, which is more than a minute away, to keep it cluttered with old locks from broken scripts.

+2


source







All Articles