2. Using Application Locks
The SQL Server Lock Manager knows nothing about the
object or the structure of the object it is locking. The Lock Manager
simply checks whether two processes are trying to obtain incompatible
locks on the same resource. If so, blocking occurs.
SQL Server allows you to extend the resources that
can be locked beyond the ones automatically provided. You can define
your own custom locking resources and let the Lock Manager control the
access to those resources as it would for any resource in a database.
This essentially allows you to choose to lock anything you want. These
user-defined lock resources are called application locks. To define an application lock, you use the sp_getapplock
stored procedure and specify a name for the resource you are locking, a
mode, an optional lock owner, and an optional lock timeout interval.
The syntax for sp_getapplock is as follows:
sp_getapplock [ @Resource = ] 'resource_name',
[ @LockMode = ] 'lock_mode'
[ , [ @LockOwner = ] { 'transaction' | 'session' } ]
[ , [ @LockTimeout = ] 'value' ]
[ , [ @DbPrincipal = ] 'database_principal' ]
Two resources are considered to be the same resource
and are subject to lock contention if they have the same name and the
same lock owner in the same database. The resource name used in these
procedures can be any identifier up to 255 characters long. The lock
owner can be specified as either transaction or session.
Multiple requests for locks on the same resource can be granted only if
the locking modes of the requests are compatible. The
possible modes of the lock allowed are shared, update, exclusive, intent
exclusive, and intent shared. The database principal is the user, role,
or application role that has permissions to an object in a database.
The default is public.
For what purpose can you use application locks, and
how do you use them? Suppose you have a table that contains a queue of
items to be processed by the system. You need a way to serialize the
retrieval of the next item from the queue so that the multiple
concurrent processes do not grab the same item at the same time. In the
past, one way this could be accomplished was by forcing an exclusive
lock on the table. Only the first process to acquire the
exclusive lock would be able to retrieve the next item from the queue.
The other processes would have to wait until the exclusive lock was
released. The problem with this approach is that the exclusive lock
would also block other processes that might need to simply retrieve data
from the table.
You can use application locks to avoid having to place an exclusive lock on the entire table. By using sp_getapplock,
you can define and lock a custom lock resource for a transaction or
session. Locks that are owned by the current transaction are released
when the transaction commits or rolls back. Locks that are owned by the
session are released when the session is closed. Locks can also be
explicitly released at any time, with the sp_releaseapplock stored procedure. The syntax for sp_releaseapplock is as follows:
sp_releaseapplock [ @Resource = ] 'resource_name'
[ , [ @LockOwner = ] { 'transaction' | 'session' }]
[ , [ @DbPrincipal = ] 'database_principal' ]
Note
If a process calls sp_getapplock multiple times for the same lock resource, sp_releaseapplock must be called the same number of times to fully release the lock. In addition, if sp_getapplock
is called multiple times on the same lock resource but specifies
different lock modes each time, the resulting lock on the resource is a
union of the different lock modes. Generally, the lock mode ends up
being promoted to the more restrictive level of the existing lock mode
and the newly requested mode. The resulting lock mode is held until the
last lock release call is made to fully release the lock. For example,
assume that a process initially called sp_getapplock requested a shared lock. If it subsequently called sp_getapplock again and requested an exclusive lock, an exclusive lock would be held on the resource until sp_releaseapplock was executed twice.
In the following example, you first request an exclusive lock on an application lock called 'QueueLock' by using sp_getapplock. You then invoke the procedure to get the next item in the queue. After the procedure returns, you call sp_releaseapplock to release the application lock called 'QueueLock' to let another session acquire the application lock:
exec sp_getapplock 'QueueLock', 'Exclusive', 'session'
exec get_next_item_from_queue
exec sp_releaseapplock 'QueueLock', 'session'
As long as all processes that need to retrieve items
from the queue execute this same sequence of statements, no other
process can execute the get_next_item_from_queue process until
the application lock is released. The other processes block attempts to
acquire the exclusive lock on the resource 'QueueLock'. For example, Listing 3 shows an example of a query against the sys.dm_tran_locks view, showing one process (SPID 53) holding an exclusive lock on QueueLock, while another process (SPID 57) is waiting for an exclusive lock on QueueLock. (The hash value generated internally for QueueLock is shown as 18fb067e in the Resource_Desc field.)
Listing 3. Viewing Application Locks Using sys.dm_tran_locks
select str(request_session_id, 4,0) as spid,
convert (varchar(12), db_name(resource_database_id)) As db_name,
case when resource_database_id = db_id() and resource_type = 'OBJECT'
then convert(char(6), object_name(resource_Associated_Entity_id))
else convert(char(6), resource_Associated_Entity_id)
end as object,
convert(varchar(12), resource_type) as resource_type,
convert(varchar(4), request_mode) as mode,
convert(varchar(24), resource_description) as resource_desc,
convert(varchar(6), request_status) as status
from sys.dm_tran_locks
order by request_session_id, 3 desc
go
spid db_name object resource_type mode resource_desc status
---- ------------ ------ ------------- ---- ------------------------ ------
52 msdb 0 DATABASE S GRANT
53 bigpubs2008 0 DATABASE S GRANT
53 bigpubs2008 0 APPLICATION X 0:[QueueLock]:(18fb067e) GRANT
57 bigpubs2008 0 APPLICATION X 0:[QueueLock]:(18fb067e) WAIT
57 bigpubs2008 0 DATABASE S GRANT
|
Caution
This
method of using application locks to control access to the queue works
only if all processes that are attempting to retrieve the next item in
the queue follow the same protocol. The get_next_item_from_queue procedure itself is not actually locked. If another process attempts to execute the get_next_item_from_queue
process without attempting to acquire the application lock first, the
Lock Manager in SQL Server does not prevent the session from executing
the stored procedure.