We need to configure Quartz to use SQL storage, but we can not store the connection string in the static quartz.config
file. In the AbpQuartzModule.PreInitialize()
the AbpQuartzConfiguration
is registred to resolve from the StdSchedulerFactory.GetDefaultScheduler()
, but we would need to register our own implementation of IAbpQuartzConfiguration
to create a scheduler with our configuration.
class AbpCustomQuartzConfiguration : IAbpQuartzConfiguration
{
public IScheduler Scheduler
{
get
{
var builder = SchedulerBuilder.Create();
builder.UsePersistentStore(options =>
{
options.UseClustering();
options.UseSqlServer(sqlServerOptions =>
{
sqlServerOptions.ConnectionString = "...";
});
options.UseSerializer<JsonObjectSerializer>();
});
builder.SchedulerId = StdSchedulerFactory.AutoGenerateInstanceId;
builder.MisfireThreshold = TimeSpan.FromMinutes(5);
var scheduler = builder.BuildScheduler().Result;
scheduler.JobFactory = new AbpQuartzJobFactory(IocManager.Instance);
scheduler.ListenerManager.AddJobListener(IocManager.Instance.Resolve<IJobListener>());
return scheduler;
}
}
}
How can we accomplish this?
- Version: Abp 5.14
- Framework: .NET Core
6 Answer(s)
-
0
Hi ivanosw1 You can use
Configuration.ReplaceService<TInterface,TImplemention>
to replace the registration. e.g.public class YourModule : AbpModule { public override void PreInitialize() { Configuration.ReplaceService<IAbpQuartzConfiguration, AbpCustomQuartzConfiguration>(); } }
-
0
Hi Zony, if I use the
ReplaceService
the registred type is still overwritten byAbp.Quartz
and thus the
IScheduler
is not properly configred. Am I missing something?Furhermore, the
Abp.Quartz.PreInitialize
calls indirectly theStdSchedulerFactory.GetDefaultScheduler()
, resulting in the creation of a default in-memory sheduler even before my customPreInitialize
gets called. In this way I will end up with 2 different scheduler instances. -
0
-
0
Hi, this registration does work, but from the logs I can confirm the suspect that 2 different schedulers are initialized:
... INFO 2021-01-08 09:33:15,969 [1 ] Quartz.Impl.StdSchedulerFactory - Default Quartz.NET properties loaded from embedded resource file INFO 2021-01-08 09:33:16,021 [1 ] Quartz.Core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: Quartz.Core.SchedulerSignalerImpl INFO 2021-01-08 09:33:16,027 [1 ] Quartz.Core.QuartzScheduler - Quartz Scheduler v.3.0.7.0 created. INFO 2021-01-08 09:33:16,028 [1 ] Quartz.Simpl.RAMJobStore - RAMJobStore initialized. INFO 2021-01-08 09:33:16,030 [1 ] Quartz.Core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v3.0.7.0) 'DefaultQuartzScheduler' with instanceId 'NON_CLUSTERED' Scheduler class: 'Quartz.Core.QuartzScheduler' - running locally. NOT STARTED. Currently in standby mode. Number of jobs executed: 0 Using thread pool 'Quartz.Simpl.DefaultThreadPool' - with 10 threads. Using job-store 'Quartz.Simpl.RAMJobStore' - which does not support persistence. and is not clustered. INFO 2021-01-08 09:33:16,034 [1 ] Quartz.Impl.StdSchedulerFactory - Quartz scheduler 'DefaultQuartzScheduler' initialized INFO 2021-01-08 09:33:16,034 [1 ] Quartz.Impl.StdSchedulerFactory - Quartz scheduler version: 3.0.7.0 INFO 2021-01-08 09:33:16,034 [1 ] Quartz.Core.QuartzScheduler - JobFactory set to: Abp.Quartz.AbpQuartzJobFactory WARN 2021-01-08 09:33:21,647 [1 ] tion.Repositories.EphemeralXmlRepository - Using an in-memory repository. Keys will not be persisted to storage. WARN 2021-01-08 09:33:21,648 [1 ] taProtection.KeyManagement.XmlKeyManager - Neither user profile nor HKLM registry available. Using an ephemeral key repository. Protected data will be unavailable when application exits. INFO 2021-01-08 09:33:24,436 [1 ] Quartz.Util.DBConnectionManager - Registering datasource 'UnoInsDataSource' with db provider: 'Quartz.Impl.AdoJobStore.Common.DbProvider' INFO 2021-01-08 09:33:24,440 [1 ] Quartz.Impl.StdSchedulerFactory - Using object serializer: Quartz.Simpl.JsonObjectSerializer, Quartz.Serialization.Json INFO 2021-01-08 09:33:25,304 [9 ] Quartz.Core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: Quartz.Core.SchedulerSignalerImpl INFO 2021-01-08 09:33:25,305 [9 ] Quartz.Core.QuartzScheduler - Quartz Scheduler v.3.0.7.0 created. INFO 2021-01-08 09:33:25,311 [9 ] Quartz.Impl.AdoJobStore.JobStoreTX - Detected usage of SqlServerDelegate - defaulting 'selectWithLockSQL' to 'SELECT * FROM QRTZ_LOCKS WITH (UPDLOCK,ROWLOCK) WHERE SCHED_NAME = 'Framework Scheduler' AND LOCK_NAME = @lockName'. INFO 2021-01-08 09:33:25,311 [9 ] Quartz.Impl.AdoJobStore.JobStoreTX - Using db table-based data access locking (synchronization). INFO 2021-01-08 09:33:25,314 [9 ] Quartz.Impl.AdoJobStore.JobStoreTX - JobStoreTX initialized. INFO 2021-01-08 09:33:25,314 [9 ] Quartz.Core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v3.0.7.0) 'Framework Scheduler' with instanceId 'DEV-N00015.637456916052992244' Scheduler class: 'Quartz.Core.QuartzScheduler' - running locally. NOT STARTED. Currently in standby mode. Number of jobs executed: 0 Using thread pool 'Quartz.Simpl.DefaultThreadPool' - with 10 threads. Using job-store 'Quartz.Impl.AdoJobStore.JobStoreTX' - which supports persistence. and is clustered. ...
The
Abp.Quartz
indeed creates a default scheduler regardless of what I do in myPreInitialize
public class AbpQuartzModule : AbpModule { public override void PreInitialize() { IocManager.Register<IAbpQuartzConfiguration, AbpQuartzConfiguration>(); // this line creates the default in-memory scheduler even if i replace the IAbpQuartzConfiguration in my PreInitialize Configuration.Modules.AbpQuartz().Scheduler.JobFactory = new AbpQuartzJobFactory(IocManager); }
For the moment I am going to shut the default one down before replacing it with my own.
-
0
This seems to be a cleaner solution
[DependsOn(typeof(AbpQuartzModule))] public class MyModule : AbpModule { public MyModule() { // patch configuration before PreInitialize. // Do not use IocManager property in the constructor as it is not populated Abp.Dependency.IocManager.Instance.IocContainer.Register( Component .For<IAbpQuartzConfiguration, QuartzStartupConfiguration>() .LifestyleSingleton() .IsDefault()); } // .. }
-
0
Hi @ivanosw1,
Thank you for sharing this :)