I am trying to optimize Quartz .NET
scheduler by eliminating the foreach iteration through each jobs.
It is a question related to SQL, and not in .NET.
The code iterates through each jobs to perform operations and, for each jobs, does the following:
bool existingJob = await JobExists(conn, newJob.Key, cancellationToken).ConfigureAwait(false);
try
{
if (existingJob)
{
if (!replaceExisting)
{
throw new ObjectAlreadyExistsException(newJob);
}
await Delegate.UpdateJobDetail(conn, newJob, cancellationToken).ConfigureAwait(false);
}
else
{
await Delegate.InsertJobDetail(conn, newJob, cancellationToken).ConfigureAwait(false);
}
}
catch (IOException e)
{
throw new JobPersistenceException("Couldn't store job: " + e.Message, e);
}
catch (Exception e)
{
throw new JobPersistenceException("Couldn't store job: " + e.Message, e);
}
Where in every await lies an sql request.
I would like to do the opposite: do a massive JobExists
for all jobs which would then tell me which jobs exists and which don't, and then update the jobs that exists and add those who don't.
So, for example 200 000 jobs, instead of doing 200000 times exists, then add, or update, we would have 3 sql transactions, one which would validate which one exists, and then a transaction to add in bulk and a last one to update in bulk.
But I do not know how to do the massive Exists
in SQL, I only know how to do an IF EXISTS (SELECT A FROM B)
query for one. Is such a thing possible? Or should I do a massive SELECT
, or a JOIN
of some sort? How could I proceed?
* EDIT *
As I have progressed the code now runs, I think, in a stored procedure like below:
USE [Quartz]
GO
/****** Object: StoredProcedure [dbo].[procProcessJobs] Script Date: 7/31/2018 10:46:09 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[procProcessJobs]
-- Add the parameters for the stored procedure here
@Jobs JobsTableType READONLY
AS
DECLARE MYCURSOR CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR Select * FROM @Jobs
Declare @Sched NVARCHAR(120), @JobName NVARCHAR(150), @JobGroup NVARCHAR(150),
@Description NVARCHAR(250), @JobClass NVARCHAR(250), @IsDurable BIT, @IsNonConcurrent BIT,
@IsUpdateData BIT, @RequestsRecovery BIT, @JobData BIT
OPEN MYCURSOR
FETCH NEXT FROM MYCURSOR
INTO @Sched, @JobName, @JobGroup, @Description, @JobClass, @IsDurable, @IsNonConcurrent, @IsUpdateData, @RequestsRecovery, @JobData
WHILE @@FETCH_STATUS = 0
BEGIN
If EXISTS(SELECT * FROM QRTZ_JOB_DETAILS WHERE SCHED_NAME = @Sched AND JOB_NAME = @JobName AND JOB_GROUP = @JobGroup)
BEGIN
/*do your update code here*/
UPDATE QRTZ_JOB_DETAILS
SET DESCRIPTION = @Description,
JOB_CLASS_NAME = @JobClass,
IS_DURABLE = @IsDurable,
IS_NONCONCURRENT = @IsNonConcurrent,
IS_UPDATE_DATA = @IsUpdateData,
REQUESTS_RECOVERY = @RequestsRecovery,
JOB_DATA = @JobData
WHERE SCHED_NAME = @Sched AND JOB_NAME = @JobName AND JOB_GROUP = @JobGroup
END
ELSE BEGIN
/*do your insert code here*/
INSERT INTO QRTZ_JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP, DESCRIPTION, JOB_CLASS_NAME, IS_DURABLE, IS_NONCONCURRENT, IS_UPDATE_DATA, REQUESTS_RECOVERY, JOB_DATA)
VALUES(@Sched, @JobName, @JobGroup, @Description, @JobClass, @IsDurable, @IsNonConcurrent, @IsUpdateData, @RequestsRecovery, @JobData)
END
END
CLOSE MYCURSOR
DEALLOCATE MYCURSOR
However the code takes a long time running and I don't know why...