有一个应用程序事件可能会在几微秒内多次触发。事件的处理程序包含最终调用SP的代码。
存储过程
CREATE PROC CreateCampaign (@dripCampaignId bigint, @orderGroupId bigint, @userDocumentId bigint, @ownerId bigint, @totalExecution int, @status tinyint, @createdOn datetime, @lastExecutions datetime)
AS
DECLARE @lresult int;
--PRINT 'BEFORE LOCK: '+CONVERT(nvarchar(100),GETDATE(),113)
EXEC @lresult = sp_getapplock @Resource = 'DC_CreateCampaign_Lock',
@LockMode = 'Exclusive', @DbPrincipal = 'dbo', @LockOwner = 'Session';
--PRINT 'LOCK ACQUIRED: '+CONVERT(nvarchar(100),GETDATE(),113) + ' '+cast(@lresult as varchar(10))
IF @lresult = 0
BEGIN
BEGIN TRY
BEGIN TRAN
IF NOT EXISTS (SELECT TOP 1 DripCampaignInstanceId FROM DripCampaignInstances WHERE DripCampaignId = @dripCampaignId AND OrderGroupId = @orderGroupId AND UserDocumentId = @userDocumentId)
BEGIN
INSERT INTO DripCampaignInstances(DripCampaignId, OrderGroupId, UserDocumentId, OwnerId, TotalExecutions, Status, CreatedOn, LastExecution)
VALUES (@dripCampaignId, @orderGroupId, @userDocumentId, @ownerId, @totalExecution, @status, @createdOn, @lastExecutions)
INSERT INTO [dbo].[EventLog]
([EventSessionID],[EventOrderGroupID],[EventDocID],[EventMachine],[EventSource],[EventCategory],[EventClass],[EventLevel]
,[EventCode],[EventMessage],[EventDateTime],[EventSourceFile],[EventSourceData],[EventMsgExtra],[b_SeenByUserAsInternalError])
VALUES
(NULL,@orderGroupId,@userDocumentId,0,0,'DripCampaign CreateCampaignInstance',0,0,701
,'Drip campaign instance created.',GETDATE(),NULL,NULL
,'Drip campaign instance created. Order id:'+cast(@orderGroupId as varchar(255))+' documentId: '+cast(@userDocumentId as varchar(255))+'',0)
END
ELSE
BEGIN
INSERT INTO [dbo].[EventLog]
([EventSessionID],[EventOrderGroupID],[EventDocID],[EventMachine],[EventSource],[EventCategory],[EventClass],[EventLevel]
,[EventCode],[EventMessage],[EventDateTime],[EventSourceFile],[EventSourceData],[EventMsgExtra],[b_SeenByUserAsInternalError])
VALUES
(NULL,@orderGroupId,@userDocumentId,0,0,'DripCampaign CreateCampaignInstance',0,0,701
,'Drip campaign instance attempted to insert duplicate.',GETDATE(),NULL,NULL
,'Drip campaign instance already exists. Order id:'+cast(@orderGroupId as varchar(255))+' documentId: '+cast(@userDocumentId as varchar(255))+'',0)
END
COMMIT TRAN
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;
ROLLBACK TRAN
END CATCH
END
EXEC @lresult = sp_releaseapplock @Resource = 'DC_CreateCampaign_Lock', @DbPrincipal ='dbo', @LockOwner = 'Session';
--PRINT 'LOCK RELEASED: '+CONVERT(nvarchar(100),GETDATE(),113)
GO
(表中应该只有一个项,所以代码只能在sp_getapplock返回的0上运行,不需要为1调用它。)
使用
在SSMS中进行测试waitfor delay '00:00:05.000'
在SP体内(为了能够在其他选项卡中执行相同的程序以验证锁是否正常工作或使用.exe创建4个线程来直接运行此SP)并且所有内容都完全符合理论(从{ {3}})。
当将相同的SP部署到目标环境时,我得到重复(每200次甚至火灾几乎重复1次;有时甚至会产生三次重复)。
由于我的手动测试具有100%的成功率,我想知道如果这个SP将在极短的时间内被多个线程调用会发生什么?我理解事件发生的概率非常快,CLR将以完全相同的速度通过事件处理程序代码,以完全相同的速度连接数据库,但如果运行时将多次进入sp_getapplock,则可以获取多个锁并且那么关键部分将由多个实例输入?
P.S。出于某些(或相同)原因而锁定.NET代码级别不起作用(整个处理程序主体位于使用私有静态成员作为参数的锁定语句中)。