Created
April 18, 2017 12:41
-
-
Save odinserj/334fdb7d18bd63451f2f34546985f639 to your computer and use it in GitHub Desktop.
DebounceAttribute
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public sealed class DebounceAttribute : JobFilterAttribute, IElectStateFilter | |
{ | |
private readonly string _resourceFormat; | |
private readonly int _milliseconds; | |
public DebounceAttribute(string resourceFormat, int milliseconds) | |
{ | |
_resourceFormat = resourceFormat ?? throw new ArgumentNullException(nameof(resourceFormat)); | |
_milliseconds = milliseconds; | |
} | |
public void OnStateElection(ElectStateContext context) | |
{ | |
// We are intercepting only transitions to the Processing state that happen, | |
// when a worker picks up a background job and is about to process it. | |
if (!(context.CandidateState is ProcessingState)) return; | |
var connection = context.Connection as JobStorageConnection; | |
if (connection == null) throw new NotSupportedException(); | |
var resource = String.Format(_resourceFormat, context.BackgroundJob.Job.Args.ToArray()); | |
var shouldRun = false; | |
try | |
{ | |
// Acquire a lock to prevent other workers to interfere between READ and WRITE operations | |
// It's better to use TimeSpan.Zero as a lock timeout, but Hangfire storages don't yet support | |
// zero timeouts. | |
using (connection.AcquireDistributedLock("debounce:lock", TimeSpan.FromMilliseconds(_milliseconds))) | |
{ | |
var previousRun = JobHelper.DeserializeNullableDateTime( | |
connection.GetValueFromHash("debounce:resources", resource)); | |
var now = DateTime.UtcNow; | |
if (previousRun == null || (now - previousRun.Value).TotalMilliseconds > _milliseconds) | |
{ | |
// If previous run date is missing (firing for the first time), or was too long ago, | |
// we should update the last run date/time and execute a job. | |
connection.SetRangeInHash("debounce:resources", new[] | |
{ | |
new KeyValuePair<string, string>(resource, JobHelper.SerializeDateTime(now)), | |
}); | |
shouldRun = true; | |
} | |
} | |
} | |
catch (DistributedLockTimeoutException) | |
{ | |
} | |
if (!shouldRun) | |
{ | |
context.CandidateState = new DeletedState { Reason = "Deleted by the debounce filter" }; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment