├── Job.Scheduler.AspNetCore.Tests
├── Usings.cs
├── Mock
│ ├── OneTimeJob.cs
│ ├── HasRunJob.cs
│ └── OneTimeQueueJob.cs
├── Job.Scheduler.AspNetCore.Tests.csproj
├── JobSchedulerAspNetCoreTests.cs
└── QueueJobSchedulerTests.cs
├── .github
├── FUNDING.yml
├── .kodiak.toml
├── dependabot.yml
└── workflows
│ ├── codeql-analysis.yml
│ └── dotnet.yml
├── .idea
└── .idea.Job.Scheduler
│ └── .idea
│ ├── encodings.xml
│ ├── indexLayout.xml
│ ├── .gitignore
│ └── vcs.xml
├── nuget.config
├── Job.Scheduler
├── Queue
│ ├── QueueSettings.cs
│ ├── QueueJobContainer.cs
│ └── Queue.cs
├── Job
│ ├── Data
│ │ ├── JobId.cs
│ │ └── Debouncer.cs
│ ├── Exception
│ │ ├── JobException.cs
│ │ └── MaxRuntimeJobException.cs
│ ├── Action
│ │ ├── NoRetry.cs
│ │ ├── AlwaysRetry.cs
│ │ ├── IRetryAction.cs
│ │ ├── RetryNTimes.cs
│ │ ├── ExponentialBackoffRetry.cs
│ │ ├── BackoffRetry.cs
│ │ └── ExponentialDecorrelatedJittedBackoffRetry.cs
│ ├── Runner
│ │ ├── QueuedJobRunner.cs
│ │ ├── OneTimeJobRunner.cs
│ │ ├── DebounceJobRunner.cs
│ │ ├── DelayedJobRunner.cs
│ │ ├── RecurringJobRunner.cs
│ │ ├── IJobRunner.cs
│ │ └── JobRunner.cs
│ ├── IJobContainerBuilder.cs
│ └── IJob.cs
├── Builder
│ ├── IJobRunnerBuilder.cs
│ └── JobRunnerBuilder.cs
├── Utils
│ ├── TaskUtils.cs
│ └── DebounceDispatcher.cs
├── LICENSE.txt
├── Job.Scheduler.csproj
└── Scheduler
│ ├── IJobScheduler.cs
│ └── JobScheduler.cs
├── Job.Scheduler.AspNetCore
├── Builder
│ ├── IJobBuilder.cs
│ └── JobBuilder.cs
├── Extensions
│ └── ServiceCollectionExtensions.cs
├── Background
│ └── JobSchedulerHostedService.cs
├── Job.Scheduler.AspNetCore.csproj
└── Configuration
│ └── JobSchedulerStartupConfig.cs
├── Job.Scheduler.Tests
├── Mocks
│ ├── ThreadJob.cs
│ ├── OneTimeJob.cs
│ ├── MaxRuntimeJob.cs
│ ├── FailingRetringJob.cs
│ ├── LongRunningDebounceJob.cs
│ ├── OneTimeQueueJob.cs
│ ├── DebounceJob.cs
│ └── MockTaskScheduler.cs
├── Job.Scheduler.Tests.csproj
├── QueueJobSchedulerTests.cs
└── JobSchedulerTests.cs
├── LICENSE
├── package.json
├── Job.Scheduler.sln
├── README.md
├── .gitignore
└── CHANGELOG.md
/Job.Scheduler.AspNetCore.Tests/Usings.cs:
--------------------------------------------------------------------------------
1 | global using NUnit.Framework;
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: Belphemur
4 | custom: 'https://soundswitch.aaflalo.me/#donate'
5 |
--------------------------------------------------------------------------------
/.idea/.idea.Job.Scheduler/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/.idea.Job.Scheduler/.idea/indexLayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Job.Scheduler/Queue/QueueSettings.cs:
--------------------------------------------------------------------------------
1 | namespace Job.Scheduler.Queue;
2 |
3 | ///
4 | /// Settings of the queue
5 | ///
6 | /// UniqueId of the queue
7 | /// Max number of job that can be run concurrently on the queue
8 | public record QueueSettings(string QueueId, int MaxConcurrency);
--------------------------------------------------------------------------------
/Job.Scheduler/Queue/QueueJobContainer.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using Job.Scheduler.Job;
4 |
5 | namespace Job.Scheduler.Queue;
6 |
7 | internal record QueueJobContainer(IJobContainerBuilder JobContainer, TaskScheduler TaskScheduler, CancellationToken Token)
8 | {
9 | public string Key => JobContainer.Key;
10 | }
--------------------------------------------------------------------------------
/Job.Scheduler/Job/Data/JobId.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Job.Scheduler.Job.Data
4 | {
5 | public struct JobId
6 | {
7 | ///
8 | /// Unique ID of the job
9 | ///
10 | public Guid UniqueId { get; }
11 |
12 | internal JobId(Guid uniqueId)
13 | {
14 | UniqueId = uniqueId;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/Job.Scheduler/Job/Exception/JobException.cs:
--------------------------------------------------------------------------------
1 | using Job.Scheduler.Scheduler;
2 |
3 | namespace Job.Scheduler.Job.Exception
4 | {
5 | ///
6 | /// Wrapped exception of the
7 | ///
8 | public class JobException : System.Exception
9 | {
10 | public JobException(string message, System.Exception exception) : base(message, exception) {}
11 | }
12 | }
--------------------------------------------------------------------------------
/Job.Scheduler/Job/Exception/MaxRuntimeJobException.cs:
--------------------------------------------------------------------------------
1 | namespace Job.Scheduler.Job.Exception
2 | {
3 | ///
4 | /// Thrown when a Job took longer than it's max runtime
5 | ///
6 | public class MaxRuntimeJobException : JobException
7 | {
8 | public MaxRuntimeJobException(string message, System.Exception exception) : base(message, exception)
9 | {
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/.idea/.idea.Job.Scheduler/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Rider ignored files
5 | /modules.xml
6 | /projectSettingsUpdater.xml
7 | /contentModel.xml
8 | /.idea.Job.Scheduler.iml
9 | # Datasource local storage ignored files
10 | /../../../../../../../../../:\Users\Antoine\source\repos\Job.Scheduler\.idea\.idea.Job.Scheduler\.idea/dataSources/
11 | /dataSources.local.xml
12 | # Editor-based HTTP Client requests
13 | /httpRequests/
14 |
--------------------------------------------------------------------------------
/.idea/.idea.Job.Scheduler/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Job.Scheduler/Job/Action/NoRetry.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Job.Scheduler.Job.Action
4 | {
5 | ///
6 | /// Don't retry the job
7 | ///
8 | public class NoRetry : IRetryAction
9 | {
10 |
11 | public bool ShouldRetry(int currentRetry)
12 | {
13 | return false;
14 | }
15 |
16 | public TimeSpan? GetDelayBetweenRetries(int currentRetry)
17 | {
18 | return null;
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/Job.Scheduler.AspNetCore/Builder/IJobBuilder.cs:
--------------------------------------------------------------------------------
1 | using Job.Scheduler.Job;
2 |
3 | namespace Job.Scheduler.AspNetCore.Builder;
4 |
5 | ///
6 | /// Helper to build job using the DI of Asp.NET Core
7 | ///
8 | public interface IJobBuilder
9 | {
10 | ///
11 | /// Create a job container of the given type
12 | ///
13 | ///
14 | ///
15 | JobBuilder.Container Create() where T : IJob;
16 | }
--------------------------------------------------------------------------------
/Job.Scheduler/Builder/IJobRunnerBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Job.Scheduler.Job;
4 | using Job.Scheduler.Job.Runner;
5 |
6 | namespace Job.Scheduler.Builder
7 | {
8 | public interface IJobRunnerBuilder
9 | {
10 | ///
11 | /// Build a Job runner for the given job
12 | ///
13 | IJobRunner Build(IJobContainerBuilder builder, Func jobDone, TaskScheduler taskScheduler) where TJob : IJob;
14 | }
15 | }
--------------------------------------------------------------------------------
/.github/.kodiak.toml:
--------------------------------------------------------------------------------
1 | # Minimal config. version is the only required field.
2 | version = 1
3 | [merge]
4 | automerge_label = "ship it!"
5 |
6 | [merge.automerge_dependencies]
7 | # auto merge all PRs opened by "dependabot" that are "minor" or "patch" version upgrades. "major" version upgrades will be ignored.
8 | versions = ["minor", "patch"]
9 | usernames = ["dependabot"]
10 |
11 | # if using `update.always`, add dependabot to `update.ignore_usernames` to allow
12 | # dependabot to update and close stale dependency upgrades.
13 | [update]
14 | ignored_usernames = ["dependabot"]
--------------------------------------------------------------------------------
/Job.Scheduler.AspNetCore.Tests/Mock/OneTimeJob.cs:
--------------------------------------------------------------------------------
1 | using Job.Scheduler.Job;
2 | using Job.Scheduler.Job.Action;
3 | using Job.Scheduler.Job.Exception;
4 |
5 | namespace Job.Scheduler.AspNetCore.Tests.Mock;
6 |
7 | public class OneTimeJob : IJob
8 | {
9 | public IRetryAction FailRule { get; } = new AlwaysRetry();
10 | public TimeSpan? MaxRuntime { get; }
11 | public Task ExecuteAsync(CancellationToken cancellationToken)
12 | {
13 | return Task.CompletedTask;
14 | }
15 |
16 | public Task OnFailure(JobException exception)
17 | {
18 | return Task.CompletedTask;
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/Job.Scheduler/Utils/TaskUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace Job.Scheduler.Utils
6 | {
7 | public static class TaskUtils
8 | {
9 | ///
10 | /// Wait for the given delay or a cancellation token to expire.
11 | ///
12 | /// Doesn't trigger an exception
13 | ///
14 | public static async Task WaitForDelayOrCancellation(TimeSpan delay, CancellationToken token)
15 | {
16 | try
17 | {
18 | await Task.Delay(delay, token);
19 | }
20 | catch (OperationCanceledException)
21 | {
22 | }
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/Job.Scheduler/Job/Action/AlwaysRetry.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Job.Scheduler.Job.Action
4 | {
5 | ///
6 | /// Should always retry the job
7 | ///
8 | public class AlwaysRetry : IRetryAction
9 | {
10 | private readonly TimeSpan? _delayBetweenRetries;
11 |
12 | public AlwaysRetry(TimeSpan? delayBetweenRetries = null)
13 | {
14 | _delayBetweenRetries = delayBetweenRetries;
15 | }
16 |
17 | public bool ShouldRetry(int currentRetry)
18 | {
19 | return true;
20 | }
21 |
22 | public TimeSpan? GetDelayBetweenRetries(int currentRetry)
23 | {
24 | return _delayBetweenRetries;
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/Job.Scheduler.Tests/Mocks/ThreadJob.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 |
4 | namespace Job.Scheduler.Tests.Mocks
5 | {
6 | public class ThreadJob : OneTimeJob
7 | {
8 | public Thread InitThread { get; }
9 | public Thread RunThread { get; private set; }
10 |
11 | public int? TaskId { get; private set; }
12 |
13 | public ThreadJob(Thread initThread)
14 | {
15 | InitThread = initThread;
16 | }
17 | public override Task ExecuteAsync(CancellationToken cancellationToken)
18 | {
19 | RunThread = Thread.CurrentThread;
20 | TaskId = Task.CurrentId;
21 | return base.ExecuteAsync(cancellationToken);
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/Job.Scheduler.AspNetCore.Tests/Mock/HasRunJob.cs:
--------------------------------------------------------------------------------
1 | using Job.Scheduler.Job;
2 | using Job.Scheduler.Job.Action;
3 | using Job.Scheduler.Job.Exception;
4 |
5 | namespace Job.Scheduler.AspNetCore.Tests.Mock;
6 |
7 | public class HasRunJob : IJob
8 | {
9 | public class Runstate
10 | {
11 | public bool HasRun;
12 | }
13 |
14 | public Runstate Run = null!;
15 |
16 | public IRetryAction FailRule { get; } = new NoRetry();
17 | public TimeSpan? MaxRuntime { get; }
18 |
19 | public Task ExecuteAsync(CancellationToken cancellationToken)
20 | {
21 | Run.HasRun = true;
22 | return Task.CompletedTask;
23 | }
24 |
25 | public Task OnFailure(JobException exception)
26 | {
27 | return Task.CompletedTask;
28 | }
29 | }
--------------------------------------------------------------------------------
/Job.Scheduler/Job/Action/IRetryAction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Job.Scheduler.Job.Action
4 | {
5 | public interface IRetryAction
6 | {
7 | ///
8 | /// Should the job be retried
9 | ///
10 | ///
11 | bool ShouldRetry(int currentRetry);
12 |
13 | ///
14 | /// Should there be a delay between the retries.
15 | ///
16 | /// Also you're able to define you're own backoff strategy using the .
17 | ///
18 | ///
19 | ///
20 | ///
21 | public TimeSpan? GetDelayBetweenRetries(int currentRetry);
22 | }
23 | }
--------------------------------------------------------------------------------
/Job.Scheduler.Tests/Job.Scheduler.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Job.Scheduler/Job/Runner/QueuedJobRunner.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using JetBrains.Annotations;
5 |
6 | namespace Job.Scheduler.Job.Runner;
7 |
8 | internal class QueuedJobRunner : JobRunner
9 | {
10 | public QueuedJobRunner(IJobContainerBuilder builderJobContainer, Func jobDone, [CanBeNull] TaskScheduler taskScheduler) : base(builderJobContainer, jobDone, taskScheduler)
11 | {
12 | }
13 |
14 | protected override async Task StartJobAsync(IJobContainerBuilder builderJobContainer, CancellationToken token)
15 | {
16 | using var jobContainer = builderJobContainer.BuildJob();
17 | var job = jobContainer.Job;
18 | await InnerExecuteJob(job, token);
19 | }
20 | }
--------------------------------------------------------------------------------
/Job.Scheduler.Tests/Mocks/OneTimeJob.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Job.Scheduler.Job;
5 | using Job.Scheduler.Job.Action;
6 | using Job.Scheduler.Job.Exception;
7 |
8 | namespace Job.Scheduler.Tests.Mocks
9 | {
10 | public class OneTimeJob : IJob
11 | {
12 | public bool HasRun { get; set; }
13 |
14 | public IRetryAction FailRule { get; } = new NoRetry();
15 | public TimeSpan? MaxRuntime { get; }
16 |
17 | public virtual Task ExecuteAsync(CancellationToken cancellationToken)
18 | {
19 | HasRun = true;
20 | return Task.CompletedTask;
21 | }
22 |
23 | public Task OnFailure(JobException exception)
24 | {
25 | return Task.FromResult(new AlwaysRetry());
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/Job.Scheduler/Job/Action/RetryNTimes.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Job.Scheduler.Job.Action
4 | {
5 | ///
6 | /// Retry the job
7 | ///
8 | public class RetryNTimes : IRetryAction
9 | {
10 | private readonly int _maxRetries;
11 | private readonly TimeSpan? _delayBetweenRetries;
12 |
13 |
14 | public RetryNTimes(int maxRetries, TimeSpan? delayBetweenRetries = null)
15 | {
16 | _maxRetries = maxRetries;
17 | _delayBetweenRetries = delayBetweenRetries;
18 | }
19 |
20 | public bool ShouldRetry(int currentRetry)
21 | {
22 | return currentRetry < _maxRetries;
23 | }
24 |
25 | public TimeSpan? GetDelayBetweenRetries(int currentRetry)
26 | {
27 | return _delayBetweenRetries;
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/Job.Scheduler/Job/Runner/OneTimeJobRunner.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using JetBrains.Annotations;
5 |
6 | namespace Job.Scheduler.Job.Runner
7 | {
8 | internal class OneTimeJobRunner : JobRunner
9 | {
10 | public OneTimeJobRunner(IJobContainerBuilder builderJobContainer, Func jobDone, [CanBeNull] TaskScheduler taskScheduler) : base(builderJobContainer, jobDone, taskScheduler)
11 | {
12 | }
13 |
14 | protected override async Task StartJobAsync(IJobContainerBuilder builderJobContainer, CancellationToken token)
15 | {
16 | using var jobContainer = builderJobContainer.BuildJob();
17 | var job = jobContainer.Job;
18 | await InnerExecuteJob(job, token);
19 | }
20 |
21 | }
22 | }
--------------------------------------------------------------------------------
/Job.Scheduler/Job/Action/ExponentialBackoffRetry.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Job.Scheduler.Job.Action
4 | {
5 | ///
6 | /// Exponential backoff strategy where we wait more and more between retries
7 | ///
8 | public class ExponentialBackoffRetry : BackoffRetry
9 | {
10 | ///
11 | /// Exponential backoff
12 | ///
13 | /// Base delay that will be exponentially increased at each retries
14 | /// null = always retries. Any other value simple set the maximum of retries
15 | public ExponentialBackoffRetry(TimeSpan baseDelay, int? maxRetries) : base(currentRetry => TimeSpan.FromMilliseconds(baseDelay.TotalMilliseconds * Math.Pow(2, currentRetry)), maxRetries)
16 | {
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/Job.Scheduler.AspNetCore.Tests/Job.Scheduler.AspNetCore.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 | false
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Job.Scheduler/Job/Runner/DebounceJobRunner.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using JetBrains.Annotations;
5 | using Job.Scheduler.Utils;
6 |
7 | namespace Job.Scheduler.Job.Runner
8 | {
9 | internal class DebounceJobRunner : JobRunner
10 | {
11 | public DebounceJobRunner(IJobContainerBuilder builderJobContainer, Func jobDone, [CanBeNull] TaskScheduler taskScheduler) : base(builderJobContainer, jobDone, taskScheduler)
12 | {
13 | }
14 |
15 | public override string Key => BuilderJobContainer.Key;
16 |
17 | protected override async Task StartJobAsync(IJobContainerBuilder builderJobContainer, CancellationToken token)
18 | {
19 | using var jobContainer = builderJobContainer.BuildJob();
20 | var job = jobContainer.Job;
21 | await InnerExecuteJob(job, token);
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/Job.Scheduler.Tests/Mocks/MaxRuntimeJob.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Job.Scheduler.Job;
5 | using Job.Scheduler.Job.Action;
6 | using Job.Scheduler.Job.Exception;
7 |
8 | namespace Job.Scheduler.Tests.Mocks
9 | {
10 | public class MaxRuntimeJob : IJob
11 | {
12 | public IRetryAction FailRule { get; }
13 | public TimeSpan? MaxRuntime { get; }
14 |
15 | public MaxRuntimeJob(IRetryAction failRule, TimeSpan? maxRuntime)
16 | {
17 | FailRule = failRule;
18 | MaxRuntime = maxRuntime;
19 | }
20 |
21 |
22 | public async Task ExecuteAsync(CancellationToken cancellationToken)
23 | {
24 | while (true)
25 | {
26 | await Task.Delay(1, cancellationToken);
27 | }
28 | }
29 |
30 | public Task OnFailure(JobException exception)
31 | {
32 | return Task.CompletedTask;
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/Job.Scheduler.Tests/Mocks/FailingRetringJob.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Job.Scheduler.Job;
5 | using Job.Scheduler.Job.Action;
6 | using Job.Scheduler.Job.Exception;
7 |
8 | namespace Job.Scheduler.Tests.Mocks
9 | {
10 |
11 | public class FailingRetringJob : IJob
12 | {
13 |
14 | public int Ran { get; private set; }
15 |
16 | public IRetryAction FailRule { get; }
17 | public TimeSpan? MaxRuntime { get; }
18 |
19 | public FailingRetringJob(IRetryAction failRule)
20 | {
21 | FailRule = failRule;
22 | }
23 |
24 | public Task ExecuteAsync(CancellationToken cancellationToken)
25 | {
26 | Ran++;
27 | throw new Exception("Test");
28 | }
29 |
30 | public Task OnFailure(JobException exception)
31 | {
32 | return Task.CompletedTask;
33 | }
34 |
35 | public TimeSpan Delay { get; } = TimeSpan.FromMilliseconds(10);
36 | }
37 | }
--------------------------------------------------------------------------------
/Job.Scheduler.Tests/Mocks/LongRunningDebounceJob.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using Job.Scheduler.Job;
6 | using Job.Scheduler.Job.Action;
7 | using Job.Scheduler.Job.Exception;
8 | using Job.Scheduler.Utils;
9 |
10 | namespace Job.Scheduler.Tests.Mocks
11 | {
12 | public class LongRunningDebounceJob : DebounceJob
13 | {
14 | public bool HasBeenInterrupted { get; private set; }
15 | public LongRunningDebounceJob(List list, string key, int id) : base(list, key, id)
16 | {
17 | }
18 |
19 | public override async Task ExecuteAsync(CancellationToken cancellationToken)
20 | {
21 | await TaskUtils.WaitForDelayOrCancellation(TimeSpan.FromSeconds(10), cancellationToken);
22 | if (cancellationToken.IsCancellationRequested)
23 | {
24 | HasBeenInterrupted = true;
25 | return;
26 | }
27 | await base.ExecuteAsync(cancellationToken);
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/Job.Scheduler/Job/Data/Debouncer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Job.Scheduler.Job.Runner;
3 | using Job.Scheduler.Utils;
4 |
5 | namespace Job.Scheduler.Job.Data;
6 |
7 | internal class Debouncer : IDisposable
8 | {
9 | public DebounceJobRunner JobRunner { get; private set; }
10 | private readonly DebounceDispatcher