Quartz.net 2.6.2 - JobDataMap not persisting across triggers

1,443 views
Skip to first unread message

Marty Wasznicky

unread,
Sep 25, 2019, 11:23:50 PM9/25/19
to Quartz.NET

Hi,

 

I'm a new user...and am following a pretty simple sample using .NET C#

 

I'm using a SQL Server datastore.  I have one sample job scheduled against 2 triggers. my job and its associated job data map sample is below. 

 

 

          List<DateTime> state = new List<DateTime>();

            var jobDataMap = new Dictionary<string, object>

                {

                    { "job.bookmark", "some stuff" },

                    { "job.mydate", System.DateTime.Now },

                    { "myStateData", state },

                    { "job.subscriberId",  "more stuff" },

                    { "job.machineName",  "crazy stuff" }

                };

 

           

            IJobDetail job = JobBuilder.Create<Job>()

                .RequestRecovery(true)

                //.SetJobData(new JobDataMap(jobDataMap as IDictionary<string, object>))

                .UsingJobData(new JobDataMap(jobDataMap as IDictionary<string, object>))

                .WithIdentity("myJob", "group1") /

                .Build();

 

When the job fires, I retrieve the value of the myStateData like using the code below and update it.  The idea is to retrieve the empty collection, then add a new item each time the job fires.

 

                List<DateTime> state = (List<DateTime>)context.JobDetail.JobDataMap["myStateData"];

                state.Add(System.DateTime.UtcNow);

 

However, I'm finding that number of objects returned in the state collection (before I do the add) is always Zero, regardless if I have passed 2 or 3 different triggers with the Job to the scheduler.

 

I did however find that it does get incremented as expected if I use the In Memory store instead of SQL Server.

 

Does anyone know if there is some other setting I need to make this work with a persistent store like SQL server?  Or is this just a bug?

 

kind regards,

Marko Lahma

unread,
Sep 27, 2019, 1:27:58 AM9/27/19
to Quartz. NET

Do you have PersistJobDataAfterExecution attribute set for your job? This sample is incomplete so hard to say. You might also want to consider 3.x series, there's no further development for 2.x series.

-Marko


--
You received this message because you are subscribed to the Google Groups "Quartz.NET" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quartznet+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/quartznet/13926cf7-cd49-459a-ace1-c9c19404f8a4%40googlegroups.com.

Marty Wasznicky

unread,
Sep 27, 2019, 9:55:01 AM9/27/19
to Quartz.NET
Yes...that's one of the attributes I have on the class. 

I tried using the 3.0.7....but the scheduler always locks up on the Start() method when I try to use the same sql provider that we use in 2.6.2.  
To unsubscribe from this group and stop receiving emails from it, send an email to quar...@googlegroups.com.

Marty Wasznicky

unread,
Sep 27, 2019, 3:33:58 PM9/27/19
to Quartz.NET
ok...got 3.0.7 working with my custom sql server datasource.  However, it still shows the same problem.....the state does NOT persist.  so it looks like this appears to be broken in 2.6.2 as well as the latest 3.0.7.....

My code seems to be pretty straight forward....I'm using the sql provider in .net 4.7.2.

            var schedulerProperties = new NameValueCollection
            {
                {"quartz.serializer.type", "binary" },
                { "quartz.scheduler.dbFailureRetryInterval","15000" },
                { "quartz.scheduler.instanceName", "ESBNeuronScheduler"},
                {"quartz.scheduler.instanceId", "AUTO"},
                {"quartz.threadPool.type", "Quartz.Simpl.SimpleThreadPool, Quartz" },
                {"quartz.threadPool.threadCount", "10"},
                { "quartz.jobStore.misfireThreshold", "60000" },
                {"quartz.jobStore.type", "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz"},
                {"quartz.jobStore.driverDelegateType", "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz"},
                {"quartz.jobStore.tablePrefix", "QRTZ_"},
                {"quartz.jobStore.dataSource", "myDS"},
                {"quartz.dataSource.myDS.connectionString", "Data Source=.;Initial Catalog=NeuronScheduler;Integrated Security=True" },
                {"quartz.dataSource.myDS.provider", "ESBSqlServer"},
                {"quartz.jobStore.acquireTriggersWithinLock", "true"},
                {"quartz.scheduler.batchTriggerAcquisitionMaxCount", "10"}

            };
            
            var schedulerFactory = new StdSchedulerFactory(schedulerProperties);
            var scheduler =  await schedulerFactory.GetScheduler();
            scheduler.Start();
 
            List<DateTime> state = new List<DateTime>();
            var jobDataMap = new Dictionary<string, object>
                {
                    { "job.bookmark", "some stuff" },
                    { "job.mydate", System.DateTime.Now },
                    { "myStateData", state },
                    { "job.subscriberId",  "more stuff" },
                    { "job.machineName",  "crazy stuff" }
                };

            // define the job and tie it to our HelloJob class
            IJobDetail job = JobBuilder.Create<Job>()
                .RequestRecovery(true)
                .SetJobData(new JobDataMap(jobDataMap as IDictionary<string, object>))
                .WithIdentity("myJob", "group1") // name should be the endpoint guid id, and group should be type of object i.e. process, workflow, adapter endpoint
                .Build();

            // maybe use a job factory to create jobs...that way we can pass our log object. we should see if we can use jobfactory for creating different 
            // types of jobs
            //_scheduler.JobFactory = new ResumeBookmarkJobFactory(
            //    Argument.DatabaseConnectionString, Log);


            // attributes taht can be added to a job class are DisallowConcurrentExecution  and PersistJobDataAfterExecution 

            ITrigger trigger = TriggerBuilder.Create()
             .WithIdentity("IDGJob", "IDG")  // name should be the endpoint guid id, and group should be type of object i.e. process, workflow, adapter endpoint
               .StartAt(date)
               .WithPriority(1)
               .Build();

            ITrigger trigger2 = TriggerBuilder.Create()
             .WithIdentity("IDGJob2", "IDG")  // name should be the endpoint guid id, and group should be type of object i.e. process, workflow, adapter endpoint
               .StartAt(date.AddMinutes(1))
               .WithPriority(1)
               .Build();

            List<ITrigger> listTriggers = new List<ITrigger>();
            listTriggers.Add(trigger);
            listTriggers.Add(trigger2);

            scheduler.DeleteJob(job.Key);
            scheduler.ScheduleJob(job, listTriggers,false);


Then my job class is pretty simple as well...the state variable that's always returned though contains 0 objects...on the second trigger...it should already contain 1 before I do the add

    [Quartz.PersistJobDataAfterExecutionAttribute()]
    [Quartz.DisallowConcurrentExecutionAttribute()]
    public class Job : IJob
    {
        public async Task Execute(IJobExecutionContext context)
        {

            try
            {
                JobKey key = context.JobDetail.Key;
                JobDataMap dataMap = context.MergedJobDataMap;
               
                var jobSays = dataMap.GetString("job.bookmark");                    
                var myFloatValue = dataMap.GetDateTime("job.mydate");
                var triggerId = dataMap.GetString("trigger.ID");

                List<DateTime> state = (List<DateTime>)context.JobDetail.JobDataMap["myStateData"];
                state.Add(System.DateTime.UtcNow);

                Form2 fm = new Form2();

                fm.textBoxTriggerID.Text = triggerId;
                fm.textBoxBookMark.Text = jobSays;
                fm.listBoxState.DataSource = state;
                fm.ShowDialog();
            }
            catch(Exception ex)
            {
                // only throw a JobExecutionException!!
            }
        }
    }

On Thursday, September 26, 2019 at 10:27:58 PM UTC-7, Marko Lahma wrote:
To unsubscribe from this group and stop receiving emails from it, send an email to quar...@googlegroups.com.
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted

Marty Wasznicky

unread,
Oct 1, 2019, 9:29:50 AM10/1/19
to Quartz.NET
any luck?


On Thursday, September 26, 2019 at 10:27:58 PM UTC-7, Marko Lahma wrote:
To unsubscribe from this group and stop receiving emails from it, send an email to quar...@googlegroups.com.
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted

Marty Wasznicky

unread,
Oct 1, 2019, 1:19:39 PM10/1/19
to Quartz.NET
HI Marko,

I put together a 2017 Visual Studio solution that replicates the issue.  you can download it from here:

let me know if you need anything else.  Any help would be appreciated.

kind regards

Marty


On Thursday, September 26, 2019 at 10:27:58 PM UTC-7, Marko Lahma wrote:
To unsubscribe from this group and stop receiving emails from it, send an email to quar...@googlegroups.com.

Marko Lahma

unread,
Oct 1, 2019, 2:00:55 PM10/1/19
to Quartz. NET
Thank you, I'll check it out, sorry for the inconvenience. It takes some time to find some free time.

-Marko

To unsubscribe from this group and stop receiving emails from it, send an email to quartznet+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/quartznet/06ff6320-a394-4a27-ab56-f0f3b4394925%40googlegroups.com.

Marty Wasznicky

unread,
Oct 8, 2019, 11:55:41 AM10/8/19
to Quartz.NET

Marty Wasznicky

unread,
Oct 30, 2019, 6:17:08 PM10/30/19
to Quartz.NET
Have you had any time yet?  We can arrange a consulting fee if needed...


On Tuesday, October 1, 2019 at 11:00:55 AM UTC-7, Marko Lahma wrote:

Marko Lahma

unread,
Nov 6, 2019, 2:49:50 PM11/6/19
to Quartz. NET
Hi Marty,

Thanks for hanging in there. I finally had a quiet night to go through your sample. The underlying issue was this:

                List<DateTime> state = (List<DateTime>)context.JobDetail.JobDataMap["myStateData"];
                state.Add(System.DateTime.UtcNow);

This only changes state that Quartz "can't see". The correct way to achieve a dirty flag firing (which indicates that scheduler needs to persist data after job execution) is:

                List<DateTime> state = (List<DateTime>)context.JobDetail.JobDataMap["myStateData"];
                state.Add(System.DateTime.UtcNow);
                context.JobDetail.JobDataMap["myStateData"] = state;

So setting the data back will trigger dirty check as something that Quartz can see is being changed. So the job state dictionary tracks the puts and index sets, but cannot track if you are mutating some data structures behind the data keys.

Hope this helps,
Marko



To unsubscribe from this group and stop receiving emails from it, send an email to quartznet+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/quartznet/e8f5f74f-3577-444b-8a2c-a15df6437016%40googlegroups.com.

Marty Wasznicky

unread,
Nov 8, 2019, 12:50:16 PM11/8/19
to Quartz.NET
Thanks Marko.  I'll check it out
Reply all
Reply to author
Forward
0 new messages