Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I avoid using sp_getAppLock?

Tags:

I have a stored procedure, and I want to ensure it cannot be executed concurrently.

My (multi-threaded) application does all necessary work on the underlying table via this stored procedure.

IMO, locking the table itself is an unnecessarily drastic action to take, and so when I found out about sp_GetAppLock, which essentially enforces a critical section, this sounded ideal.

My plan was to encase the stored procedure in a transaction and to set up spGetAppLock with transaction scope. The code was written and tested successfully.

The code has now been put forward for review and I have been told that I should not call this function. However when asking the obvious question "why not?", the only reasons I am getting are highly subjective, to do with any form of locking being complicated.

I don't necessarily buy this, but I was wondering whether anyone had any objective reasons why I should avoid this construct. Like I say, given my circumstances a critical section sounds an ideal approach to me.

Further info: An application sits on top of this with 2 threads T1 and T2. Each thread is waiting for a different message M1 and M2. The business logic involved says that processing can only happen once both M1 and M2 have arrived. The stored procedure logs that Mx has arrived (insert) and then checks whether My is present (select). The built-in locking is fine to make sure the inserts happen serially. But the selects need to happen serially too and I think I need to do something over and above the built-in functionality here.

Just for clarity, I want the "processing" to happen exactly once. So I can't afford for the stored procedure to return either false positives or false negatives. I'm worried that if the stored proc runs twice in very quick succession, then both "selects" might return data which indicates that it is appropriate to perform processing.

like image 291
PeteH Avatar asked Oct 25 '12 14:10

PeteH


People also ask

Why use sp_ getapplock?

It's always better to write code that allows multiple instance to run at the same time, but when that's impossible, or just would take too much effort, I use sp_getapplock to ensure that critical sections are single threaded.

What is Sp_getapplock?

The lock resource created by sp_getapplock is created in the current database for the session. Each lock resource is identified by the combined values of: The database ID of the database containing the lock resource. The database principal specified in the @DbPrincipal parameter.

How do I decrypt a stored procedure in SQL Server?

To do this, go to the Action menu and select 'Decryption Wizard…'. Once the wizard has opened, you can select all the objects you want to decrypt at once and what to do with the output of the wizard. You can have the T-SQL output go into a single file, create one file per object, or decrypt all the objects in place.


2 Answers

What is the procedure doing that you cannot rely on SQL Servers built-in concurrency control mechanisms? Often queries can be rewritten to allow real concurrency.

But if this procedure indeed has to be executed "alone", locking the table itself on first access is most likely going to be a lot faster than using the call to sp_GetAppLock. It sounds like this procedure is going to be called often. If that is the case you should look for a way to achieve the goal with minimal impact.


If the table contains no other rows besides of M1 and M2 a table lock is still your best bet.

If you have multiple threads sending multiple messages you can get more fine-grained by using "serializable" as transaction level and check if the other message is there before you do the insert but within the same transaction. To prevent deadlocks in this case make sure you check for both messages for example like this:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;   BEGIN TRAN;  SELECT   @hasM1 = MAX(CASE WHEN msg_type='M1' THEN 1 ELSE 0 END),   @hasM2 = MAX(CASE WHEN msg_type='M2' THEN 1 ELSE 0 END)  FROM messages WITH(UPDLOCK)  WHERE msg_type IN ('M1','M2')   INSERT ...   IF(??) EXEC do_other_stuff_and_delete_messages; COMMIT 

In the IF statement before(!) the COMMIT you can use the information collected before the insert together with the information that you inserted to decide if additional processing is necessary.

In that processing step make sure to either mark those messages as processed or to delete them all still within the same transaction. That will make sure that you will not process those messages twice.

SERIALIZABLE is the only transaction isolation level that allows to lock rows that do not exist yet, so the first select statement with the WITH(UPDLOCK) effectively prevents the other row being inserted while the first execution is still running.

Finally, these are a lot of things to be aware of that could go wrong. You might want to have a look at service broker instead. you could use three queues with that. one for type M1 and one for type M2. Every time a message arrives within those queues a procedure can automatically be called to insert a token into the third queue. The third queue then could activate a process to check if both messages exist and do work. That would make the entire process asynchronous but for that it would be easy to restrict the queue 3 response to always only do one check at a time.

Service broker on msdn, also look at "activation" for the automatic message processing.

like image 87
Sebastian Meine Avatar answered Oct 14 '22 02:10

Sebastian Meine


sp_GetAppLock is just like many other tools and as such it can be misused, overused, or correctly used. It is an exact match for the type of problem described by the original poster. This is a good MSSQL Tips post on the usage Prevent multiple users from running the same SQL Server stored procedure at the same time http://www.mssqltips.com/sqlservertip/3202/prevent-multiple-users-from-running-the-same-sql-server-stored-procedure-at-the-same-time/

like image 27
Ray Avatar answered Oct 14 '22 03:10

Ray