mysql 超时时间已到而操作尚未完成_HangFire循环作业中作业因执行时间太长未完成新作业开启导致重复数据的问题...

usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingHangfire.Common;usingHangfire.States;usingHangfire.Storage;namespaceHangfire.Pro

{///

///Represents a background job filter that helps to disable concurrent execution///without causing worker to wait as in.///

public classMutexAttribute : JobFilterAttribute, IElectStateFilter, IApplyStateFilter

{private static readonly TimeSpan DistributedLockTimeout = TimeSpan.FromMinutes(1);private readonly string_resource;public MutexAttribute(stringresource)

{

_resource=resource;

RetryInSeconds= 15;

}public int RetryInSeconds { get; set; }public int MaxAttempts { get; set; }public voidOnStateElection(ElectStateContext context)

{//We are intercepting transitions to the Processed state, that is performed by//a worker just before processing a job. During the state election phase we can//change the target state to another one, causing a worker not to process the//backgorund job.

if (context.CandidateState.Name != ProcessingState.StateName ||context.BackgroundJob.Job== null)

{return;

}//This filter requires an extended set of storage operations. It's supported//by all the official storages, and many of the community-based ones.

var storageConnection = context.Connection asJobStorageConnection;if (storageConnection == null)

{throw new NotSupportedException("This version of storage doesn't support extended methods. Please try to update to the latest version.");

}stringblockedBy;try{//Distributed lock is needed here only to prevent a race condition, when another//worker picks up a background job with the same resource between GET and SET//operations.//There will be no race condition, when two or more workers pick up background job//with the same id, because state transitions are protected with distributed lock//themselves.

using(AcquireDistributedSetLock(context.Connection, context.BackgroundJob.Job.Args))

{//Resource set contains a background job id that acquired a mutex for the resource.//We are getting only one element to see what background job blocked the invocation.

var range =storageConnection.GetRangeFromSet(

GetResourceKey(context.BackgroundJob.Job.Args),0,0);

blockedBy= range.Count > 0 ? range[0] : null;//We should permit an invocation only when the set is empty, or if current background//job is already owns a resource. This may happen, when the localTransaction succeeded,//but outer transaction was failed.

if (blockedBy == null || blockedBy ==context.BackgroundJob.Id)

{//We need to commit the changes inside a distributed lock, otherwise it's//useless. So we create a local transaction instead of using the//context.Transaction property.

var localTransaction =context.Connection.CreateWriteTransaction();//Add the current background job identifier to a resource set. This means//that resource is owned by the current background job. Identifier will be//removed only on failed state, or in one of final states (succeeded or//deleted).

localTransaction.AddToSet(GetResourceKey(context.BackgroundJob.Job.Args), context.BackgroundJob.Id);

localTransaction.Commit();//Invocation is permitted, and we did all the required things.

return;

}

}

}catch(DistributedLockTimeoutException)

{//We weren't able to acquire a distributed lock within a specified window. This may//be caused by network delays, storage outages or abandoned locks in some storages.//Since it is required to expire abandoned locks after some time, we can simply//postpone the invocation.

context.CandidateState = newScheduledState(TimeSpan.FromSeconds(RetryInSeconds))

{

Reason= "Couldn't acquire a distributed lock for mutex: timeout exceeded"};return;

}//Background job execution is blocked. We should change the target state either to//the Scheduled or to the Deleted one, depending on current retry attempt number.

var currentAttempt = context.GetJobParameter("MutexAttempt") + 1;

context.SetJobParameter("MutexAttempt", currentAttempt);

context.CandidateState= MaxAttempts == 0 || currentAttempt <=MaxAttempts?CreateScheduledState(blockedBy, currentAttempt)

: CreateDeletedState(blockedBy);

}public voidOnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction)

{if (context.BackgroundJob.Job == null) return;if (context.OldStateName ==ProcessingState.StateName)

{using(AcquireDistributedSetLock(context.Connection, context.BackgroundJob.Job.Args))

{var localTransaction =context.Connection.CreateWriteTransaction();

localTransaction.RemoveFromSet(GetResourceKey(context.BackgroundJob.Job.Args), context.BackgroundJob.Id);

localTransaction.Commit();

}

}

}public voidOnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction)

{

}private static DeletedState CreateDeletedState(stringblockedBy)

{return newDeletedState

{

Reason= $"Execution was blocked by background job {blockedBy}, all attempts exhausted"};

}private IState CreateScheduledState(string blockedBy, intcurrentAttempt)

{var reason = $"Execution is blocked by background job {blockedBy}, retry attempt: {currentAttempt}";if (MaxAttempts > 0)

{

reason+= $"/{MaxAttempts}";

}return newScheduledState(TimeSpan.FromSeconds(RetryInSeconds))

{

Reason=reason

};

}private IDisposable AcquireDistributedSetLock(IStorageConnection connection, IEnumerableargs)

{returnconnection.AcquireDistributedLock(GetDistributedLockKey(args), DistributedLockTimeout);

}private string GetDistributedLockKey(IEnumerableargs)

{return $"extension:job-mutex:lock:{GetKeyFormat(args, _resource)}";

}private string GetResourceKey(IEnumerableargs)

{return $"extension:job-mutex:set:{GetKeyFormat(args, _resource)}";

}private static string GetKeyFormat(IEnumerable args, stringkeyFormat)

{returnString.Format(keyFormat, args.ToArray());

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值