Sign In Register

How can we help you today?

Start a new topic
Answered

Spark.lock, Spark.lockKey - How do they work?

Hey all,


So, I want to make sure I understand how the locking mechanisms work behind the scenes.


I understand that lockKey has a retry time. You decide a period of time after which you think the other script which has the lock will be done. The method retries after that period of time, and if it still can't acquire the lock, it gives up and returns false.


How does Spark.lock(chal) work given that it does not have a retry time? Is this a block-and-lock-owner-will-notify call? Does the script which failed to acquire a lock continue executing? If so, what is the behavior on reads/writes in this case?


Thanks,

Ryan


Best Answer

Hi Ryan,


That isn't quite correct.

Spark.lockKey("customKey", 5000) will continually attempt to acquire the lock "customKey", until either it successfully acquires it, or the timeout is reached (in this example, 5s).

Spark.lock(chal) does basically the same thing, using the challenge as a lock key, without a timeout.


For example, if you call Spark.lockKey("customKey", 5000) and it initially fails to acquire the lock (because it is locked by another running script) it wouldn't wait for 5s to retry.

If the other script releases the lock after 2s (for example) then the first script will acquire that lock as soon as it is available (e.g. after 2s in this example), returning "true" as the result of the function call. If it times out after 5s (because the other script doesn't release the lock) then it will return "false" after effectively blocking for the 5s timeout period.


Note that Spark.lock(chal) operates slightly differently in that it doesn't return true or false. This is because scripts can only run for 30s before they are terminated, so if it can't get the lock within 30s your script would have been terminated by this point anyway. If you wanted to lock on a challenge, but specify a timeout so you can gracefully deal with the lock acquisition failure, you could use lockKey() passing in the challenge ID as the key.


As best practice, you should always release any locks you acquire using Spark.unlockKey() or Spark.unlock(chal) as soon as the lock is no longer required. This helps to ensure that other scripts which also need the lock won't be held up any longer than necessary. When a script terminates (either normally, or due to running for more than 30s) any locks are released automatically, but you shouldn't rely on this mechanism for the reason already given.


Kind regards,


Jonathan.


Answer

Hi Ryan,


That isn't quite correct.

Spark.lockKey("customKey", 5000) will continually attempt to acquire the lock "customKey", until either it successfully acquires it, or the timeout is reached (in this example, 5s).

Spark.lock(chal) does basically the same thing, using the challenge as a lock key, without a timeout.


For example, if you call Spark.lockKey("customKey", 5000) and it initially fails to acquire the lock (because it is locked by another running script) it wouldn't wait for 5s to retry.

If the other script releases the lock after 2s (for example) then the first script will acquire that lock as soon as it is available (e.g. after 2s in this example), returning "true" as the result of the function call. If it times out after 5s (because the other script doesn't release the lock) then it will return "false" after effectively blocking for the 5s timeout period.


Note that Spark.lock(chal) operates slightly differently in that it doesn't return true or false. This is because scripts can only run for 30s before they are terminated, so if it can't get the lock within 30s your script would have been terminated by this point anyway. If you wanted to lock on a challenge, but specify a timeout so you can gracefully deal with the lock acquisition failure, you could use lockKey() passing in the challenge ID as the key.


As best practice, you should always release any locks you acquire using Spark.unlockKey() or Spark.unlock(chal) as soon as the lock is no longer required. This helps to ensure that other scripts which also need the lock won't be held up any longer than necessary. When a script terminates (either normally, or due to running for more than 30s) any locks are released automatically, but you shouldn't rely on this mechanism for the reason already given.


Kind regards,


Jonathan.


1 person likes this

Perfect! Thanks for the explanation Jonathan!

Can I run by one more scenario for you?


We have a word game where word submission order is important. Let's say we have 3 clients, A, B, and C.


A, B, and C all submit words at approximately the same time. A gets to the server first, and acquires a lock on a somewhat long running operation. B and C arrive shortly after in that order, and both block on a Spark.lock(chal).


A finally reaches the end of its critical section and releases the lock.


1) Who goes first? B? C? Is it arbitrary?

2) In the case that A's request takes an overly long amount of time, is there any concept of "try this request again later" built into GameSparks?


1 person likes this

Hi Ryan,


1) If there are 2 scripts waiting to acquire a lock (B and C), it is not defined which one will acquire the lock - it is arbitrary.


2) No, there is no built-in retry mechanism for this scenario. You would have to implement simple "retry" functionality in your client logic. From GameSparks' point of view the request "succeeded" in that it did what it was supposed to do without any platform errors... the "failure" in this example is a business logic failure, where that logic would be specific to your game.


Kind regards,


Jon.


1 person likes this

Hey Jon,


Thanks for all the info. Two more questions along those lines: 


1) Is there any way for us to response to a Cloud Code timeout on the server via Cloud Code?

2) If a request times out part way through the processing time, what happens to any requests made against persistent storage (SparkCache, Redis, Mongo)? Do those calls still occur? Are they cancelled? Is it possible for a script thread to be killed halfway through execution and not complete the entire contents of the script?

Ryan,


If a script is running for longer than 30s, it is terminated immediately - subsequent instructions will not be executed. An entry will be placed in your game's script.log collection indicating the script timed out, and an error response will be returned to the client.

As such, there is no way to respond to a cloud code timeout on the server directly - however, you can code your client to detect these conditions and make a subsequent call to the server (e.g. as a different event) to perform whatever cleanup is required.


You should ensure your cloud code executes as quickly as possible - if you genuinely expect a client call to take longer than 30s executing code, this won't scale to large numbers of players anyway (which is why the restriction is there in the first place). Executing an event that takes half a minute would result in a poor experience for your players and should be avoided. Typically most calls should execute in under 1s to ensure good performance.

If you have a genuine requirement for running cloud code (invoked from clients) that will take anything close to 30s, you should discuss this with our customer support team by raising a ticket to discuss your specific use case, as there will almost certainly be a better design for your requirements.


Kind regards,


Jonathan.

Hey again Jonathan,


Just to give you a little more detail: we're trying to support a game where many users make submissions against the server, possibly concurrently. The previous state of the game is important. To facilitate these submissions, we lock the challenge and make a few Redis/database calls, which are the only things I anticipate taking any significant amount of time. However, other clients waiting on this will need to block while one client's submission is finishing being validated and recorded in the database.


I don't anticipate my calls taking 30 seconds, but if many users submit concurrently, that lock could extend execution time of a script. I am aware of the 1 second average execution time Fair Use Policy. I just wanted to make sure I understood the behavior for all possible failure cases in case we start seeing anything when we start testing our game, and know what cases I should be covering either in cloud code or on the client as you said.


I've covered everything I could think of here as far as possible failure cases go. Thanks for the information!


Ryan

Hi Ryan,


No problem, happy to help. Good luck with your game!


Jonathan.

Login to post a comment