Repository: aspnetboilerplate/aspnetboilerplate (csharp)

Issue:
execute GetWaitingJobsAsync with input parameter 1000 in some case cause high waiting time for tasks with higher priority
Running the method GetWaitingJobsAsync with input parameter 1000
```
protected override async Task DoWorkAsync()
        {
            var waitingJobs = await _store.GetWaitingJobsAsync(1000);

            foreach (var job in waitingJobs)
            {
                await TryProcessJobAsync(job);
            }
        }
```
  in cases where there are many tasks with _low priority_ will cause _high waiting time_ for tasks with **higher priority**. Consider this scenario: There are 1,000 jobs waiting to be executed. With the first execution of **GetWaitingJobsAsync,** the first 1,000 jobs are loaded. The system is executing these 1000 jobs, and 10 other jobs with higher priority are placed in the queue, as a result, until the execution of those 1000 jobs, it is not time to execute the job with higher priority.

Pull request:
resolves #6647

Patch:
diff --git a/doc/WebSite/Background-Jobs-And-Workers.md b/doc/WebSite/Background-Jobs-And-Workers.md
index fd863769be..640c632da1 100644
--- a/doc/WebSite/Background-Jobs-And-Workers.md
+++ b/doc/WebSite/Background-Jobs-And-Workers.md
@@ -226,7 +226,7 @@ other problems. To prevent it, you have two options:
     application and create a separated, standalone application (example:
     a Windows Service) that executes background jobs.
 
-#### User token removal period
+##### User token removal period
 
 ABP Framework defines a background worker named UserTokenExpirationWorker which cleans the records in table AbpUserTokens. If you disable background job execution, this worker will not run. By default, UserTokenExpirationWorker runs every one hour. If you want to change this period, you can configure it like below:
 
@@ -239,6 +239,23 @@ ABP Framework defines a background worker named UserTokenExpirationWorker which
     
         //...
     }
+
+##### Max Waiting Jobs To Process
+
+By default, ABP Framework processes 1000 waiting background jobs. You can change it using the configuration below;
+
+```csharp
+public class MyProjectWebModule : AbpModule
+{
+    public override void PreInitialize()
+    {
+        Configuration.BackgroundJobs.MaxWaitingJobToProcessPerPeriod = 10;
+    }
+
+    //...
+}
+```
+
 #### Exception Handling
 
 Since the default background job manager should re-try failed jobs, it
diff --git a/src/Abp/Authorization/SimplePermissionDependency.cs b/src/Abp/Authorization/SimplePermissionDependency.cs
index ed6b837efd..78ee506b5f 100644
--- a/src/Abp/Authorization/SimplePermissionDependency.cs
+++ b/src/Abp/Authorization/SimplePermissionDependency.cs
@@ -19,7 +19,7 @@ public class SimplePermissionDependency : IPermissionDependency
         /// Default: false.
         /// </summary>
         public bool RequiresAll { get; set; }
-
+        
         /// <summary>
         /// Initializes a new instance of the <see cref="SimplePermissionDependency"/> class.
         /// </summary>
diff --git a/src/Abp/BackgroundJobs/BackgroundJobConfiguration.cs b/src/Abp/BackgroundJobs/BackgroundJobConfiguration.cs
index a11034d948..e20b356d26 100644
--- a/src/Abp/BackgroundJobs/BackgroundJobConfiguration.cs
+++ b/src/Abp/BackgroundJobs/BackgroundJobConfiguration.cs
@@ -5,6 +5,8 @@ namespace Abp.BackgroundJobs
 {
     internal class BackgroundJobConfiguration : IBackgroundJobConfiguration
     {
+        public static int DefaultMaxWaitingJobToProcessPerPeriod = 1000;
+        
         public bool IsJobExecutionEnabled { get; set; }
 
         [Obsolete("Use UserTokenExpirationPeriod instead.")]
@@ -12,6 +14,8 @@ internal class BackgroundJobConfiguration : IBackgroundJobConfiguration
 
         public TimeSpan? UserTokenExpirationPeriod { get; set; }
 
+        public int MaxWaitingJobToProcessPerPeriod { get; set; } = DefaultMaxWaitingJobToProcessPerPeriod;
+        
         public IAbpStartupConfiguration AbpConfiguration { get; private set; }
 
         public BackgroundJobConfiguration(IAbpStartupConfiguration abpConfiguration)
diff --git a/src/Abp/BackgroundJobs/BackgroundJobManager.cs b/src/Abp/BackgroundJobs/BackgroundJobManager.cs
index 4bce9df9ac..2254821e66 100644
--- a/src/Abp/BackgroundJobs/BackgroundJobManager.cs
+++ b/src/Abp/BackgroundJobs/BackgroundJobManager.cs
@@ -28,6 +28,7 @@ public class BackgroundJobManager : AsyncPeriodicBackgroundWorkerBase, IBackgrou
 
         private readonly IIocResolver _iocResolver;
         private readonly IBackgroundJobStore _store;
+        private readonly IBackgroundJobConfiguration _backgroundJobConfiguration;
 
         static BackgroundJobManager()
         {
@@ -40,17 +41,19 @@ static BackgroundJobManager()
         public BackgroundJobManager(
             IIocResolver iocResolver,
             IBackgroundJobStore store,
+            IBackgroundJobConfiguration backgroundJobConfiguration,
             AbpAsyncTimer timer)
             : base(timer)
         {
             _store = store;
+            _backgroundJobConfiguration = backgroundJobConfiguration;
             _iocResolver = iocResolver;
 
             EventBus = NullEventBus.Instance;
 
             Timer.Period = JobPollPeriod;
         }
-        
+
         public virtual async Task<string> EnqueueAsync<TJob, TArgs>(TArgs args,
             BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null)
             where TJob : IBackgroundJobBase<TArgs>
@@ -80,7 +83,7 @@ public virtual async Task<string> EnqueueAsync<TJob, TArgs>(TArgs args,
 
             return jobInfoId;
         }
-        
+
         public virtual string Enqueue<TJob, TArgs>(TArgs args,
             BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null)
             where TJob : IBackgroundJobBase<TArgs>
@@ -105,7 +108,7 @@ public virtual string Enqueue<TJob, TArgs>(TArgs args,
                 CurrentUnitOfWork.SaveChanges();
 
                 jobInfoId = jobInfo.Id.ToString();
-                
+
                 uow.Complete();
             }
 
@@ -140,7 +143,7 @@ public bool Delete(string jobId)
 
         protected override async Task DoWorkAsync()
         {
-            var waitingJobs = await _store.GetWaitingJobsAsync(1000);
+            var waitingJobs = await _store.GetWaitingJobsAsync(_backgroundJobConfiguration.MaxWaitingJobToProcessPerPeriod);
 
             foreach (var job in waitingJobs)
             {
@@ -240,4 +243,4 @@ private async Task TryUpdateAsync(BackgroundJobInfo jobInfo)
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/Abp/BackgroundJobs/IBackgroundJobConfiguration.cs b/src/Abp/BackgroundJobs/IBackgroundJobConfiguration.cs
index 15cb8f4a32..c46eea3c90 100644
--- a/src/Abp/BackgroundJobs/IBackgroundJobConfiguration.cs
+++ b/src/Abp/BackgroundJobs/IBackgroundJobConfiguration.cs
@@ -24,6 +24,12 @@ public interface IBackgroundJobConfiguration
         /// </summary>
         TimeSpan? UserTokenExpirationPeriod { get; set; }
 
+        
+        /// <summary>
+        /// Maximum number of waiting jobs to process per period.
+        /// </summary>
+        int MaxWaitingJobToProcessPerPeriod { get; set; }
+        
         /// <summary>
         /// Gets the ABP configuration object.
         /// </summary>


Test patch:
diff --git a/test/Abp.Tests/BackgroundJobs/InMemoryBackgroundJobStore_Tests.cs b/test/Abp.Tests/BackgroundJobs/InMemoryBackgroundJobStore_Tests.cs
index 0ac2c059d5..6db9bd0280 100644
--- a/test/Abp.Tests/BackgroundJobs/InMemoryBackgroundJobStore_Tests.cs
+++ b/test/Abp.Tests/BackgroundJobs/InMemoryBackgroundJobStore_Tests.cs
@@ -1,15 +1,16 @@
 ﻿using System.Threading.Tasks;
 using Abp.BackgroundJobs;
 using Abp.Timing;
+using NSubstitute;
 using Shouldly;
 using Xunit;
 
 namespace Abp.Tests.BackgroundJobs
 {
-    public class InMemoryBackgroundJobStore_Tests
+    public class InMemoryBackgroundJobStore_Tests: TestBaseWithLocalIocManager
     {
         private readonly InMemoryBackgroundJobStore _store;
-
+        
         public InMemoryBackgroundJobStore_Tests()
         {
             _store = new InMemoryBackgroundJobStore();
@@ -26,7 +27,7 @@ public async Task Test_All()
             };
             
             await _store.InsertAsync(jobInfo);
-            (await _store.GetWaitingJobsAsync(1000)).Count.ShouldBe(1);
+            (await _store.GetWaitingJobsAsync(BackgroundJobConfiguration.DefaultMaxWaitingJobToProcessPerPeriod)).Count.ShouldBe(1);
 
             var jobInfoFromStore = await _store.GetAsync(1);
             jobInfoFromStore.ShouldNotBeNull();
@@ -34,7 +35,7 @@ public async Task Test_All()
             jobInfoFromStore.JobArgs.ShouldBeSameAs(jobInfo.JobArgs);
 
             await _store.DeleteAsync(jobInfo);
-            (await _store.GetWaitingJobsAsync(1000)).Count.ShouldBe(0);
+            (await _store.GetWaitingJobsAsync(BackgroundJobConfiguration.DefaultMaxWaitingJobToProcessPerPeriod)).Count.ShouldBe(0);
         }
     }
 }