CREATE TABLE [dbo].[Jobs] ( [Id] BIGINT NOT NULL CONSTRAINT [PK_Jobs] PRIMARY KEY IDENTITY,[Data] VARBINARY(MAX) NOT NULL,[CreationTimestamp] DATETIME2(7) NOT NULL,[Type] INT NOT NULL,[ModificationTimestamp] DATETIME2(7) NOT NULL,[State] INT NOT NULL,[RowVersion] ROWVERSION NOT NULL,[Activity] INT NULL,[Parent_Id] BIGINT NULL ) GO CREATE NONCLUSTERED INDEX [IX_Jobs_Type_State_RowVersion] ON [dbo].[Jobs]([Type],[State],[RowVersion] ASC) WHERE ([State] <> 100) GO CREATE NONCLUSTERED INDEX [IX_Jobs_Parent_Id_State] ON [dbo].[Jobs]([Parent_Id],[State] ASC) GO
作业正在添加到状态= 0(新)的表 – 它可以被这个状态的任何工作人员使用.当工作者获得此队列项时,状态更改为50(处理),作业变为不可用于其他用户(工作人员调用[dbo].[Jobs_GetFirstByType]与参数:Type = any,@ CurrentState = 0,@ NewState = 50).
CREATE PROCEDURE [dbo].[Jobs_GetFirstByType] @Type INT,@CurrentState INT,@NewState INT AS BEGIN SET TRANSACTION ISOLATION LEVEL READ COMMITTED; DECLARE @JobId BIGINT; BEGIN TRAN SELECT TOP(1) @JobId = Id FROM [dbo].[Jobs] WITH (UPDLOCK,READPAST) WHERE [Type] = @Type AND [State] = @CurrentState ORDER BY [RowVersion]; UPDATE [dbo].[Jobs] SET [State] = @NewState,[ModificationTimestamp] = SYSUTCDATETIME() OUTPUT INSERTED.[Id],INSERTED.[RowVersion],INSERTED.[Data],INSERTED.[Type],INSERTED.[State],INSERTED.[Activity] WHERE [Id] = @JobId; COMMIT TRAN END
处理完成后,作业状态可以再次更改为0(新建),也可以将其设置为100(已完成).
CREATE PROCEDURE [dbo].[Jobs_UpdateStatus] @Id BIGINT,@State INT,@Activity INT AS BEGIN UPDATE j SET j.[State] = @State,j.[Activity] = @Activity,j.[ModificationTimestamp] = SYSUTCDATETIME() OUTPUT INSERTED.[Id],INSERTED.[RowVersion] FROM [dbo].[Jobs] j WHERE j.[Id] = @Id; END
作业具有层次结构,只有当所有子项完成时,父作业才会获得State = 100(Completed).
一些工作人员调用存储过程([dbo].[Jobs_GetCountWithExcludedState] with @ ExcludedState = 100)返回未完成作业的数量,当它返回0时,父作业状态可以设置为100(已完成).
CREATE PROCEDURE [dbo].[Jobs_GetCountWithExcludedState] @ParentId INT,@ExcludedState INT AS BEGIN SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SELECT COUNT(1) FROM [dbo].[Jobs] WHERE [Parent_Id] = @ParentId AND [State] <> @ExcludedState END
这个存储过程的主要问题是奇怪的行为.有时候,为父工作返回0,但它完全没有完成工作.我尝试打开更改数据跟踪和一些调试信息(包括分析) – 子作业100%没有状态= 100,当SP返回0.
看来,SP跳过不在100(已完成)状态的记录,但是为什么会发生,如何防止这种情况呢?
UPD:
调用[dbo].[Jobs_GetCountWithExcludedState]在父作业有子代时启动.当工作人员开始检查未存在的子作业时,由于创建子项并将其设置为包含在事务中的父作业检查活动,那么就不会有这种情况:
using (var ts = new TransactionScope()) { _jobManager.AddChilds(parentJob); parentJob.State = 0; parentJob.Activity = 30; // in this activity worker starts checking child jobs ts.Complete(); }
解决方法
>由于sql Server或数据的问题,查询失败
腐败.
>实际上没有符合条件的确定记录
程序运行.
腐败是不可能的,但可能的原因.您可以使用DBCC CHECKDB检查是否存在损坏.
最有可能的是,实际上没有任何一个Parent_ID等于@ParentId参数并且在运行时处于100状态的作业记录.
我强调承诺,因为这是交易将会看到的.
你从来没有真正解释你的问题如何在工作上设置Parent_ID.我的第一个想法是,也许你正在检查未处理的子作业,它没有找到,但是另一个进程将其添加为另一个不完整作业的Parent_ID.这是可能吗?
我看到你添加了一个更新,以显示当您添加一个子作业记录时,父和子记录的更新被包装在事务中.这是好的,但不是我问的问题.这是我正在考虑的一种可能性:
>为父母插入并提交作业记录.
> Jobs_GetFirstByType抓取父作业.
>工作线程处理它并调用Jobs_UpdateStatus并将其状态更新为100.
>某些东西调用Job_GetCountWithExcludedState作业并返回0.
>创建子作业并将其附加到已完成的父作业记录…,使其再次成为不完整的.
我不是说这是发生了什么…我只是问是否可能,并采取什么步骤来防止它?例如,在您的问题更新中的上述代码中,您选择一个ParentJob将该子项附加到事务外部.可能是您正在选择父作业,然后在运行将子添加到父项的事务之前完成它?或者也可能是父作业的最后一个子作业完成,以便工作线程检查并标记父项完成,但是一些其他工作线程已经选择作为新的子作业的父级作业?
有许多不同的情况可能会导致你所描述的症状.我相信这个问题是在你没有与我们分享的代码中找到的,特别是关于如何创建作业以及调用Jobs_GetCountWithExcludedState的代码.如果你能给出更多的信息,我想你会更有可能找到一个可用的答案,否则我们可以做的最好的事情是猜测在代码中可能发生的所有事情,我们看不到.