TSQL mutual exclusive access in a stored procedure

霸气de小男生 提交于 2019-11-29 02:41:19
gbn

SERIALIZABLE is an isolation level for locking, not a semaphore.

It won't work in this case all you'll do is persist a read lock to the end of the TXN that doesn't prevent another process into the code reading.

You need to use sp_getapplock in Transaction mode. You can configure it to wait, bomb immediatekly etc: up to you

This is based on my template from Nested stored procedures containing TRY CATCH ROLLBACK pattern?

ALTER PROCEDURE get_code 
AS
SET XACT_ABORT, NOCOUNT ON

DECLARE @starttrancount int, @result int;

BEGIN TRY
    SELECT @starttrancount = @@TRANCOUNT

    IF @starttrancount = 0 BEGIN TRANSACTION

    EXEC @result = sp_getapplock 'get_code', 'Exclusive', 'Transaction', 0 
    IF @result < 0
        RAISERROR('INFO: One at a time please`!', 16, 1);

    [...Perform work...]


    IF @starttrancount = 0 
        COMMIT TRANSACTION
    ELSE
        EXEC sp_releaseapplock 'get_code';
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0 AND @starttrancount = 0 
        ROLLBACK TRANSACTION
    RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
GO

This is how I did it. Given a table MetaInfo with columns MetaKey varchar(max) and MeatValueLong bigInt.

Note, in my case there goal was to exclusively get a increasing value without duplicates. I used a rowlock to create the isolation on this single operation. (Yes I know I could have used inserting and an auto-increment key, but there was an addition requirement that the caller can pass in a minimum value.)

CREATE PROCEDURE [dbo].[uspGetNextID]
(
  @inID bigInt 
)
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRANSACTION

    -- This section can be removed if you want to pass in an id.
    SET @inID = 0

    UPDATE MetaInfo WITH (ROWLOCK) 
      SET MetaValueLong = CASE 
                            WHEN ISNULL(MetaValueLong,0) > @inID THEN MetaValueLong+1 
                            ELSE @inID+1
                          END 
    WHERE MetaKey = 'Internal-ID-Last'

    SELECT MetaValueLong 
    FROM MetaInfo
    WHERE MetaKey = 'Internal-ID-Last'

    COMMIT TRANSACTION 

END

yes, SET ISOLATION LEVEL SERIALIZABLE is exactly what you need. It does not permit dirty writes and dirty reads. All db-objets that are inside serializable transactions are locked so other connections will be able read/write only when first one does commit or rollback.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!