Implemented IBackgroundJobStore

pull/395/head
Halil ibrahim Kalkan 7 years ago
parent f1da2241e9
commit a7187dcf57

@ -63,7 +63,7 @@ namespace Volo.Abp.BackgroundJobs
try
{
jobExecuteMethod.Invoke(job, new[] { argsObj });
AsyncHelper.RunSync(() => Store.DeleteAsync(jobInfo));
AsyncHelper.RunSync(() => Store.DeleteAsync(jobInfo.Id));
}
catch (Exception ex)
{

@ -9,18 +9,6 @@ namespace Volo.Abp.BackgroundJobs
/// </summary>
public class BackgroundJobInfo
{
/// <summary>
/// Maximum length of <see cref="JobName"/>.
/// Value: 512.
/// </summary>
public const int MaxJobTypeLength = 512;
/// <summary>
/// Maximum length of <see cref="JobArgs"/>.
/// Value: 1 MB (1,048,576 bytes).
/// </summary>
public const int MaxJobArgsLength = 1024 * 1024;
/// <summary>
/// Default duration (as seconds) for the first wait on a failure.
/// Default value: 60 (1 minutes).
@ -46,15 +34,11 @@ namespace Volo.Abp.BackgroundJobs
/// Type of the job.
/// It's AssemblyQualifiedName of job type.
/// </summary>
[Required]
[StringLength(MaxJobTypeLength)]
public virtual string JobName { get; set; }
/// <summary>
/// Job arguments as JSON string.
/// </summary>
[Required]
[MaxLength(MaxJobArgsLength)]
public virtual string JobArgs { get; set; } //TODO: Consider to conver to byte[]
/// <summary>

@ -34,8 +34,8 @@ namespace Volo.Abp.BackgroundJobs
/// <summary>
/// Deletes a job.
/// </summary>
/// <param name="jobInfo">Job information.</param>
Task DeleteAsync(BackgroundJobInfo jobInfo);
/// <param name="jobId">The Job Unique Identifier.</param>
Task DeleteAsync(Guid jobId);
/// <summary>
/// Updates a job.

@ -48,9 +48,9 @@ namespace Volo.Abp.BackgroundJobs
return Task.FromResult(waitingJobs);
}
public Task DeleteAsync(BackgroundJobInfo jobInfo)
public Task DeleteAsync(Guid jobId)
{
_jobs.TryRemove(jobInfo.Id, out _);
_jobs.TryRemove(jobId, out _);
return Task.FromResult(0);
}
@ -59,7 +59,7 @@ namespace Volo.Abp.BackgroundJobs
{
if (jobInfo.IsAbandoned)
{
return DeleteAsync(jobInfo);
return DeleteAsync(jobInfo.Id);
}
return Task.FromResult(0);

@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Autofac;
using Volo.Abp.BackgroundJobs.EntityFrameworkCore;
using Volo.Abp.Modularity;
namespace Volo.Abp.BackgroundJobs.DemoApp
{
[DependsOn(
typeof(BackgroundJobsEntityFrameworkCoreModule),
typeof(AbpAutofacModule)
)]
public class DemoAppModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAssemblyOf<DemoAppModule>();
}
}
}

@ -6,7 +6,16 @@ namespace Volo.Abp.BackgroundJobs.DemoApp
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
using (var application = AbpApplicationFactory.Create<DemoAppModule>(options =>
{
options.UseAutofac();
}))
{
application.Initialize();
Console.WriteLine("Press ENTER to stop the application..!");
Console.ReadLine();
}
}
}
}

@ -6,6 +6,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.BackgroundJobs.EntityFrameworkCore\Volo.Abp.BackgroundJobs.EntityFrameworkCore.csproj" />
</ItemGroup>

@ -0,0 +1,9 @@
namespace Volo.Abp.BackgroundJobs
{
public static class BackgroundJobRecordConsts
{
public const int MaxJobNameLength = 128;
public const int MaxJobArgsLength = 1024 * 1024;
}
}

@ -9,7 +9,9 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.BackgroundJobs.Domain.Shared\Volo.Abp.BackgroundJobs.Domain.Shared.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.BackgroundJobs\Volo.Abp.BackgroundJobs.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,56 @@
using System;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.BackgroundJobs
{
public class BackgroundJobRecord : AggregateRoot<Guid>, IHasCreationTime
{
/// <summary>
/// Type of the job.
/// It's AssemblyQualifiedName of job type.
/// </summary>
public virtual string JobName { get; set; }
/// <summary>
/// Job arguments as JSON string.
/// </summary>
public virtual string JobArgs { get; set; } //TODO: Consider to conver to byte[]
/// <summary>
/// Try count of this job.
/// A job is re-tried if it fails.
/// </summary>
public virtual short TryCount { get; set; }
/// <summary>
/// Creation time of this job.
/// </summary>
public virtual DateTime CreationTime { get; set; }
/// <summary>
/// Next try time of this job.
/// </summary>
public virtual DateTime NextTryTime { get; set; }
/// <summary>
/// Last try time of this job.
/// </summary>
public virtual DateTime? LastTryTime { get; set; }
/// <summary>
/// This is true if this job is continously failed and will not be executed again.
/// </summary>
public virtual bool IsAbandoned { get; set; }
/// <summary>
/// Priority of this job.
/// </summary>
public virtual BackgroundJobPriority Priority { get; set; }
public BackgroundJobRecord()
{
}
}
}

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ObjectMapping;
namespace Volo.Abp.BackgroundJobs
{
public class BackgroundJobStore : IBackgroundJobStore, ITransientDependency
{
protected IBackgroundJobRepository BackgroundJobRepository { get; }
protected IObjectMapper ObjectMapper { get; }
public BackgroundJobStore(
IBackgroundJobRepository backgroundJobRepository,
IObjectMapper objectMapper)
{
ObjectMapper = objectMapper;
BackgroundJobRepository = backgroundJobRepository;
}
public async Task<BackgroundJobInfo> FindAsync(Guid jobId)
{
return ObjectMapper.Map<BackgroundJobRecord, BackgroundJobInfo>(
await BackgroundJobRepository.FindAsync(jobId)
);
}
public async Task InsertAsync(BackgroundJobInfo jobInfo)
{
await BackgroundJobRepository.InsertAsync(
ObjectMapper.Map<BackgroundJobInfo, BackgroundJobRecord>(jobInfo)
);
}
public async Task<List<BackgroundJobInfo>> GetWaitingJobsAsync(int maxResultCount)
{
return ObjectMapper.Map<List<BackgroundJobRecord>, List<BackgroundJobInfo>>(
await BackgroundJobRepository.GetWaitingListAsync(maxResultCount)
);
}
public async Task DeleteAsync(Guid jobId)
{
await BackgroundJobRepository.DeleteAsync(jobId);
}
public async Task UpdateAsync(BackgroundJobInfo jobInfo)
{
await BackgroundJobRepository.UpdateAsync(
ObjectMapper.Map<BackgroundJobInfo, BackgroundJobRecord>(jobInfo)
);
}
}
}

@ -2,7 +2,7 @@
{
public static class BackgroundJobsConsts
{
public const string DefaultDbTablePrefix = "BackgroundJobs";
public const string DefaultDbTablePrefix = "Abp";
public const string DefaultDbSchema = null;
}

@ -0,0 +1,13 @@
using AutoMapper;
namespace Volo.Abp.BackgroundJobs
{
public class BackgroundJobsDomainAutoMapperProfile : Profile
{
public BackgroundJobsDomainAutoMapperProfile()
{
CreateMap<BackgroundJobInfo, BackgroundJobRecord>()
.ReverseMap();
}
}
}

@ -1,15 +1,23 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AutoMapper;
using Volo.Abp.Modularity;
namespace Volo.Abp.BackgroundJobs
{
[DependsOn(
typeof(BackgroundJobsDomainSharedModule)
typeof(BackgroundJobsDomainSharedModule),
typeof(AbpBackgroundJobsModule),
typeof(AbpAutoMapperModule)
)]
public class BackgroundJobsDomainModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<BackgroundJobsDomainAutoMapperProfile>(validate: true);
});
context.Services.AddAssemblyOf<BackgroundJobsDomainModule>();
}
}

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace Volo.Abp.BackgroundJobs
{
public interface IBackgroundJobRepository : IBasicRepository<BackgroundJobRecord, Guid>
{
Task<List<BackgroundJobRecord>> GetWaitingListAsync(int maxResultCount);
}
}

@ -4,16 +4,14 @@ using Volo.Abp.EntityFrameworkCore;
namespace Volo.Abp.BackgroundJobs.EntityFrameworkCore
{
[ConnectionStringName("BackgroundJobs")]
[ConnectionStringName("AbpBackgroundJobs")]
public class BackgroundJobsDbContext : AbpDbContext<BackgroundJobsDbContext>, IBackgroundJobsDbContext
{
public static string TablePrefix { get; set; } = BackgroundJobsConsts.DefaultDbTablePrefix;
public static string Schema { get; set; } = BackgroundJobsConsts.DefaultDbSchema;
/* Add DbSet for each Aggregate Root here. Example:
* public DbSet<Question> Questions { get; set; }
*/
public DbSet<BackgroundJobRecord> BackgroundJobs { get; set; }
public BackgroundJobsDbContext(DbContextOptions<BackgroundJobsDbContext> options)
: base(options)

@ -1,6 +1,6 @@
using System;
using Microsoft.EntityFrameworkCore;
using Volo.Abp;
using Volo.Abp.EntityFrameworkCore.Modeling;
namespace Volo.Abp.BackgroundJobs.EntityFrameworkCore
{
@ -15,24 +15,23 @@ namespace Volo.Abp.BackgroundJobs.EntityFrameworkCore
var options = new BackgroundJobsModelBuilderConfigurationOptions();
optionsAction?.Invoke(options);
builder.Entity<BackgroundJobRecord>(b =>
{
b.ToTable(options.TablePrefix + "BackgroundJobs", options.Schema);
/* Configure all entities here. Example:
b.ConfigureCreationTime();
builder.Entity<Question>(b =>
{
//Configure table & schema name
//b.ToTable(options.TablePrefix + "Questions", options.Schema);
b.Property(x => x.JobName).IsRequired().HasMaxLength(BackgroundJobRecordConsts.MaxJobNameLength);
b.Property(x => x.JobArgs).IsRequired().HasMaxLength(BackgroundJobRecordConsts.MaxJobArgsLength);
b.Property(x => x.TryCount).HasDefaultValue(0);
b.Property(x => x.NextTryTime);
b.Property(x => x.LastTryTime);
b.Property(x => x.IsAbandoned).HasDefaultValue(false);
b.Property(x => x.Priority).HasDefaultValue(BackgroundJobPriority.Normal);
//Properties
//b.Property(q => q.Title).IsRequired().HasMaxLength(QuestionConsts.MaxTitleLength);
//Configure relations
//b.HasMany(question => question.Tags).WithOne().HasForeignKey(qt => qt.QuestionId);
//Configure indexes
//b.HasIndex(q => q.CreationTime);
b.HasIndex(x => new { x.IsAbandoned, x.NextTryTime });
});
*/
}
}
}

@ -14,9 +14,7 @@ namespace Volo.Abp.BackgroundJobs.EntityFrameworkCore
{
context.Services.AddAbpDbContext<BackgroundJobsDbContext>(options =>
{
/* Add custom repositories here. Example:
* options.AddRepository<Question, EfCoreQuestionRepository>();
*/
options.AddRepository<BackgroundJobRecord, EfCoreBackgroundJobRepository>();
});
context.Services.AddAssemblyOf<BackgroundJobsEntityFrameworkCoreModule>();

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Timing;
namespace Volo.Abp.BackgroundJobs.EntityFrameworkCore
{
public class EfCoreBackgroundJobRepository : EfCoreRepository<IBackgroundJobsDbContext, BackgroundJobRecord, Guid>, IBackgroundJobRepository
{
protected IClock Clock { get; }
public EfCoreBackgroundJobRepository(
IDbContextProvider<IBackgroundJobsDbContext> dbContextProvider,
IClock clock)
: base(dbContextProvider)
{
Clock = clock;
}
public async Task<List<BackgroundJobRecord>> GetWaitingListAsync(int maxResultCount)
{
return await DbSet
.Where(t => !t.IsAbandoned && t.NextTryTime <= Clock.Now)
.OrderByDescending(t => t.Priority)
.ThenBy(t => t.TryCount)
.ThenBy(t => t.NextTryTime)
.Take(maxResultCount)
.ToListAsync();
}
}
}

@ -1,13 +1,12 @@
using Volo.Abp.Data;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
namespace Volo.Abp.BackgroundJobs.EntityFrameworkCore
{
[ConnectionStringName("BackgroundJobs")]
[ConnectionStringName("AbpBackgroundJobs")]
public interface IBackgroundJobsDbContext : IEfCoreDbContext
{
/* Add DbSet for each Aggregate Root here. Example:
* DbSet<Question> Questions { get; }
*/
DbSet<BackgroundJobRecord> BackgroundJobs { get; }
}
}

@ -0,0 +1,7 @@
namespace Volo.Abp.BackgroundJobs.EntityFrameworkCore
{
public class BackgroundJobRepositoryTests : BackgroundJobRepository_Tests<BackgroundJobsEntityFrameworkCoreTestModule>
{
}
}

@ -1,7 +0,0 @@
namespace Volo.Abp.BackgroundJobs.EntityFrameworkCore
{
public class MyEntityRepository_Tests : MyEntityRepository_Tests<BackgroundJobsEntityFrameworkCoreTestModule>
{
}
}

@ -0,0 +1,7 @@
namespace Volo.Abp.BackgroundJobs.MongoDB
{
public class BackgroundJobRepositoryTests : BackgroundJobRepository_Tests<BackgroundJobsMongoDbTestModule>
{
}
}

@ -1,7 +0,0 @@
namespace Volo.Abp.BackgroundJobs.MongoDB
{
public class MyEntityRepository_Tests : MyEntityRepository_Tests<BackgroundJobsMongoDbTestModule>
{
}
}

@ -0,0 +1,35 @@
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Modularity;
using Volo.Abp.Timing;
using Xunit;
namespace Volo.Abp.BackgroundJobs
{
public abstract class BackgroundJobRepository_Tests<TStartupModule> : BackgroundJobsTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
private readonly IBackgroundJobRepository _backgroundJobRepository;
private readonly IClock _clock;
protected BackgroundJobRepository_Tests()
{
_backgroundJobRepository = GetRequiredService<IBackgroundJobRepository>();
_clock = GetRequiredService<IClock>();
}
[Theory]
[InlineData(2)]
[InlineData(5)]
public async Task GetWaitingListAsync(int maxResultCount)
{
var backgroundJobs = await _backgroundJobRepository.GetWaitingListAsync(maxResultCount);
backgroundJobs.Count.ShouldBeGreaterThan(0);
backgroundJobs.Count.ShouldBeLessThanOrEqualTo(maxResultCount);
backgroundJobs.ForEach(j => j.IsAbandoned.ShouldBeFalse());
backgroundJobs.ForEach(j => j.NextTryTime.ShouldBeLessThanOrEqualTo(_clock.Now.AddSeconds(1))); //1 second tolerance
}
}
}

@ -13,6 +13,11 @@ namespace Volo.Abp.BackgroundJobs
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Configure<BackgroundJobOptions>(options =>
{
options.IsJobExecutionEnabled = false;
});
context.Services.AddAssemblyOf<BackgroundJobsTestBaseModule>();
}

@ -1,8 +1,12 @@
using Volo.Abp.DependencyInjection;
using System;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.BackgroundJobs
{
public class BackgroundJobsTestData : ISingletonDependency
{
public Guid JobId1 { get; } = Guid.NewGuid();
public Guid JobId2 { get; } = Guid.NewGuid();
public Guid JobId3 { get; } = Guid.NewGuid();
}
}

@ -1,24 +1,71 @@
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using System;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Timing;
namespace Volo.Abp.BackgroundJobs
{
public class BackgroundJobsTestDataBuilder : ITransientDependency
{
private readonly IGuidGenerator _guidGenerator;
private BackgroundJobsTestData _testData;
private readonly BackgroundJobsTestData _testData;
private readonly IBackgroundJobRepository _backgroundJobRepository;
private readonly IClock _clock;
public BackgroundJobsTestDataBuilder(
IGuidGenerator guidGenerator,
BackgroundJobsTestData testData)
BackgroundJobsTestData testData,
IBackgroundJobRepository backgroundJobRepository,
IClock clock)
{
_guidGenerator = guidGenerator;
_testData = testData;
_backgroundJobRepository = backgroundJobRepository;
_clock = clock;
}
public void Build()
{
_backgroundJobRepository.Insert(
new BackgroundJobRecord
{
Id = _testData.JobId1,
JobName = "TestJobName",
JobArgs = "{ value: 1 }",
NextTryTime = _clock.Now.Subtract(TimeSpan.FromMinutes(1)),
Priority = BackgroundJobPriority.Normal,
IsAbandoned = false,
LastTryTime = null,
CreationTime = _clock.Now.Subtract(TimeSpan.FromMinutes(2)),
TryCount = 0
}
);
_backgroundJobRepository.Insert(
new BackgroundJobRecord
{
Id = _testData.JobId2,
JobName = "TestJobName",
JobArgs = "{ value: 2 }",
NextTryTime = _clock.Now.AddMinutes(42),
Priority = BackgroundJobPriority.AboveNormal,
IsAbandoned = true,
LastTryTime = _clock.Now.Subtract(TimeSpan.FromDays(1)),
CreationTime = _clock.Now.Subtract(TimeSpan.FromDays(2)),
TryCount = 3
}
);
_backgroundJobRepository.Insert(
new BackgroundJobRecord
{
Id = _testData.JobId3,
JobName = "TestJobName",
JobArgs = "{ value: 3 }",
NextTryTime = _clock.Now,
Priority = BackgroundJobPriority.BelowNormal,
IsAbandoned = false,
LastTryTime = _clock.Now.Subtract(TimeSpan.FromMinutes(60)),
CreationTime = _clock.Now.Subtract(TimeSpan.FromMinutes(90)),
TryCount = 2
}
);
}
}
}

@ -1,16 +0,0 @@
using System.Threading.Tasks;
using Volo.Abp.Modularity;
using Xunit;
namespace Volo.Abp.BackgroundJobs
{
public abstract class MyEntityRepository_Tests<TStartupModule> : BackgroundJobsTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
[Fact]
public async Task Test1()
{
}
}
}
Loading…
Cancel
Save