-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Some Changes to Quartz to Allow Filtering based on JobType
- Loading branch information
Showing
6 changed files
with
323 additions
and
12 deletions.
There are no files selected for viewing
13 changes: 13 additions & 0 deletions
13
Shoko.Server/Scheduling/Delegates/IFilteredDriverDelegate.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Quartz.Impl.AdoJobStore; | ||
|
||
namespace Shoko.Server.Scheduling.Delegates; | ||
|
||
public interface IFilteredDriverDelegate : IDriverDelegate | ||
{ | ||
Task<IReadOnlyCollection<TriggerAcquireResult>> SelectTriggerToAcquire(ConnectionAndTransactionHolder conn, DateTimeOffset noLaterThan, | ||
DateTimeOffset noEarlierThan, int maxCount, Type[] jobTypesToExclude, CancellationToken cancellationToken = default); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Quartz.Impl.AdoJobStore; | ||
|
||
namespace Shoko.Server.Scheduling.Delegates; | ||
|
||
public class MySQLDelegate : Quartz.Impl.AdoJobStore.MySQLDelegate, IFilteredDriverDelegate | ||
{ | ||
private string _schedulerName; | ||
|
||
protected override string GetSelectNextTriggerToAcquireSql(int maxCount) | ||
{ | ||
return GetSelectNextTriggerToAcquireSql(maxCount, null); | ||
} | ||
|
||
protected string GetSelectNextTriggerToAcquireSql(int maxCount, Type[] jobTypesToExclude) | ||
{ | ||
return $@"SELECT | ||
t.{ColumnTriggerName}, t.{ColumnTriggerGroup}, jd.{ColumnJobClass} | ||
FROM | ||
{TablePrefixSubst}{TableTriggers} t | ||
JOIN | ||
{TablePrefixSubst}{TableJobDetails} jd ON (jd.{ColumnSchedulerName} = t.{ColumnSchedulerName} AND jd.{ColumnJobGroup} = t.{ColumnJobGroup} AND jd.{ColumnJobName} = t.{ColumnJobName}) | ||
WHERE | ||
t.{ColumnSchedulerName} = @schedulerName AND {ColumnTriggerState} = @state AND {ColumnNextFireTime} <= @noLaterThan AND ({ColumnMifireInstruction} = -1 OR ({ColumnMifireInstruction} <> -1 AND {ColumnNextFireTime} >= @noEarlierThan)) | ||
{(jobTypesToExclude == null || jobTypesToExclude.Length == 0 ? "" : $"AND jd.{ColumnJobClass} NOT IN ({string.Join(",", jobTypesToExclude.Select(a => $"'{GetStorableJobTypeName(a)}'"))})")} | ||
ORDER BY | ||
{ColumnNextFireTime} ASC, {ColumnPriority} DESC | ||
LIMIT {maxCount};"; | ||
} | ||
|
||
public override void Initialize(DelegateInitializationArgs args) | ||
{ | ||
base.Initialize(args); | ||
_schedulerName = args.InstanceName; | ||
} | ||
|
||
public override async Task<IReadOnlyCollection<TriggerAcquireResult>> SelectTriggerToAcquire( | ||
ConnectionAndTransactionHolder conn, | ||
DateTimeOffset noLaterThan, | ||
DateTimeOffset noEarlierThan, | ||
int maxCount, | ||
CancellationToken cancellationToken = default) | ||
{ | ||
return await SelectTriggerToAcquire(conn, noLaterThan, noEarlierThan, maxCount, null, cancellationToken); | ||
} | ||
|
||
public async Task<IReadOnlyCollection<TriggerAcquireResult>> SelectTriggerToAcquire( | ||
ConnectionAndTransactionHolder conn, | ||
DateTimeOffset noLaterThan, | ||
DateTimeOffset noEarlierThan, | ||
int maxCount, | ||
Type[] jobTypesToExclude, | ||
CancellationToken cancellationToken = default) | ||
{ | ||
if (maxCount < 1) | ||
{ | ||
maxCount = 1; // we want at least one trigger back. | ||
} | ||
|
||
using var cmd = PrepareCommand(conn, ReplaceTablePrefix(GetSelectNextTriggerToAcquireSql(maxCount, jobTypesToExclude))); | ||
List<TriggerAcquireResult> nextTriggers = new(); | ||
|
||
AddCommandParameter(cmd, "schedulerName", _schedulerName); | ||
AddCommandParameter(cmd, "state", StateWaiting); | ||
AddCommandParameter(cmd, "noLaterThan", GetDbDateTimeValue(noLaterThan)); | ||
AddCommandParameter(cmd, "noEarlierThan", GetDbDateTimeValue(noEarlierThan)); | ||
|
||
using var rs = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); | ||
// signal cancel, otherwise ADO.NET might have trouble handling partial reads from open reader | ||
var shouldStop = false; | ||
while (await rs.ReadAsync(cancellationToken).ConfigureAwait(false)) | ||
{ | ||
if (shouldStop) | ||
{ | ||
cmd.Cancel(); | ||
break; | ||
} | ||
|
||
if (nextTriggers.Count < maxCount) | ||
{ | ||
var result = new TriggerAcquireResult( | ||
(string)rs[ColumnTriggerName], | ||
(string)rs[ColumnTriggerGroup], | ||
(string)rs[ColumnJobClass]); | ||
nextTriggers.Add(result); | ||
} | ||
else | ||
{ | ||
shouldStop = true; | ||
} | ||
} | ||
|
||
return nextTriggers; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Quartz.Impl.AdoJobStore; | ||
|
||
namespace Shoko.Server.Scheduling.Delegates; | ||
|
||
public class SQLiteDelegate : Quartz.Impl.AdoJobStore.SQLiteDelegate, IFilteredDriverDelegate | ||
{ | ||
private string _schedulerName; | ||
|
||
protected override string GetSelectNextTriggerToAcquireSql(int maxCount) | ||
{ | ||
return GetSelectNextTriggerToAcquireSql(maxCount, null); | ||
} | ||
|
||
protected string GetSelectNextTriggerToAcquireSql(int maxCount, Type[] jobTypesToExclude) | ||
{ | ||
return $@"SELECT | ||
t.{ColumnTriggerName}, t.{ColumnTriggerGroup}, jd.{ColumnJobClass} | ||
FROM | ||
{TablePrefixSubst}{TableTriggers} t | ||
JOIN | ||
{TablePrefixSubst}{TableJobDetails} jd ON (jd.{ColumnSchedulerName} = t.{ColumnSchedulerName} AND jd.{ColumnJobGroup} = t.{ColumnJobGroup} AND jd.{ColumnJobName} = t.{ColumnJobName}) | ||
WHERE | ||
t.{ColumnSchedulerName} = @schedulerName AND {ColumnTriggerState} = @state AND {ColumnNextFireTime} <= @noLaterThan AND ({ColumnMifireInstruction} = -1 OR ({ColumnMifireInstruction} <> -1 AND {ColumnNextFireTime} >= @noEarlierThan)) | ||
{(jobTypesToExclude == null || jobTypesToExclude.Length == 0 ? "" : $"AND jd.{ColumnJobClass} NOT IN ({string.Join(",", jobTypesToExclude.Select(a => $"'{GetStorableJobTypeName(a)}'"))})")} | ||
ORDER BY | ||
{ColumnNextFireTime} ASC, {ColumnPriority} DESC | ||
LIMIT {maxCount};"; | ||
} | ||
|
||
public override void Initialize(DelegateInitializationArgs args) | ||
{ | ||
base.Initialize(args); | ||
_schedulerName = args.InstanceName; | ||
} | ||
|
||
public override async Task<IReadOnlyCollection<TriggerAcquireResult>> SelectTriggerToAcquire( | ||
ConnectionAndTransactionHolder conn, | ||
DateTimeOffset noLaterThan, | ||
DateTimeOffset noEarlierThan, | ||
int maxCount, | ||
CancellationToken cancellationToken = default) | ||
{ | ||
return await SelectTriggerToAcquire(conn, noLaterThan, noEarlierThan, maxCount, null, cancellationToken); | ||
} | ||
|
||
public async Task<IReadOnlyCollection<TriggerAcquireResult>> SelectTriggerToAcquire( | ||
ConnectionAndTransactionHolder conn, | ||
DateTimeOffset noLaterThan, | ||
DateTimeOffset noEarlierThan, | ||
int maxCount, | ||
Type[] jobTypesToExclude, | ||
CancellationToken cancellationToken = default) | ||
{ | ||
if (maxCount < 1) | ||
{ | ||
maxCount = 1; // we want at least one trigger back. | ||
} | ||
|
||
using var cmd = PrepareCommand(conn, ReplaceTablePrefix(GetSelectNextTriggerToAcquireSql(maxCount, jobTypesToExclude))); | ||
List<TriggerAcquireResult> nextTriggers = new(); | ||
|
||
AddCommandParameter(cmd, "schedulerName", _schedulerName); | ||
AddCommandParameter(cmd, "state", StateWaiting); | ||
AddCommandParameter(cmd, "noLaterThan", GetDbDateTimeValue(noLaterThan)); | ||
AddCommandParameter(cmd, "noEarlierThan", GetDbDateTimeValue(noEarlierThan)); | ||
|
||
using var rs = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); | ||
// signal cancel, otherwise ADO.NET might have trouble handling partial reads from open reader | ||
var shouldStop = false; | ||
while (await rs.ReadAsync(cancellationToken).ConfigureAwait(false)) | ||
{ | ||
if (shouldStop) | ||
{ | ||
cmd.Cancel(); | ||
break; | ||
} | ||
|
||
if (nextTriggers.Count < maxCount) | ||
{ | ||
var result = new TriggerAcquireResult( | ||
(string)rs[ColumnTriggerName], | ||
(string)rs[ColumnTriggerGroup], | ||
(string)rs[ColumnJobClass]); | ||
nextTriggers.Add(result); | ||
} | ||
else | ||
{ | ||
shouldStop = true; | ||
} | ||
} | ||
|
||
return nextTriggers; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Quartz.Impl.AdoJobStore; | ||
|
||
namespace Shoko.Server.Scheduling.Delegates; | ||
|
||
public class SqlServerDelegate : Quartz.Impl.AdoJobStore.SqlServerDelegate, IFilteredDriverDelegate | ||
{ | ||
private string _schedulerName; | ||
|
||
protected override string GetSelectNextTriggerToAcquireSql(int maxCount) | ||
{ | ||
return GetSelectNextTriggerToAcquireSql(maxCount, null); | ||
} | ||
|
||
protected string GetSelectNextTriggerToAcquireSql(int maxCount, Type[] jobTypesToExclude) | ||
{ | ||
return $@"SELECT TOP {maxCount} | ||
t.{ColumnTriggerName}, t.{ColumnTriggerGroup}, jd.{ColumnJobClass} | ||
FROM | ||
{TablePrefixSubst}{TableTriggers} t | ||
JOIN | ||
{TablePrefixSubst}{TableJobDetails} jd ON (jd.{ColumnSchedulerName} = t.{ColumnSchedulerName} AND jd.{ColumnJobGroup} = t.{ColumnJobGroup} AND jd.{ColumnJobName} = t.{ColumnJobName}) | ||
WHERE | ||
t.{ColumnSchedulerName} = @schedulerName AND {ColumnTriggerState} = @state AND {ColumnNextFireTime} <= @noLaterThan AND ({ColumnMifireInstruction} = -1 OR ({ColumnMifireInstruction} <> -1 AND {ColumnNextFireTime} >= @noEarlierThan)) | ||
{(jobTypesToExclude == null || jobTypesToExclude.Length == 0 ? "" : $"AND jd.{ColumnJobClass} NOT IN ({string.Join(",", jobTypesToExclude.Select(a => $"'{GetStorableJobTypeName(a)}'"))})")} | ||
ORDER BY | ||
{ColumnNextFireTime} ASC, {ColumnPriority} DESC;"; | ||
} | ||
|
||
public override void Initialize(DelegateInitializationArgs args) | ||
{ | ||
base.Initialize(args); | ||
_schedulerName = args.InstanceName; | ||
} | ||
|
||
public override async Task<IReadOnlyCollection<TriggerAcquireResult>> SelectTriggerToAcquire( | ||
ConnectionAndTransactionHolder conn, | ||
DateTimeOffset noLaterThan, | ||
DateTimeOffset noEarlierThan, | ||
int maxCount, | ||
CancellationToken cancellationToken = default) | ||
{ | ||
return await SelectTriggerToAcquire(conn, noLaterThan, noEarlierThan, maxCount, null, cancellationToken); | ||
} | ||
|
||
public async Task<IReadOnlyCollection<TriggerAcquireResult>> SelectTriggerToAcquire( | ||
ConnectionAndTransactionHolder conn, | ||
DateTimeOffset noLaterThan, | ||
DateTimeOffset noEarlierThan, | ||
int maxCount, | ||
Type[] jobTypesToExclude, | ||
CancellationToken cancellationToken = default) | ||
{ | ||
if (maxCount < 1) | ||
{ | ||
maxCount = 1; // we want at least one trigger back. | ||
} | ||
|
||
using var cmd = PrepareCommand(conn, ReplaceTablePrefix(GetSelectNextTriggerToAcquireSql(maxCount, jobTypesToExclude))); | ||
List<TriggerAcquireResult> nextTriggers = new(); | ||
|
||
AddCommandParameter(cmd, "schedulerName", _schedulerName); | ||
AddCommandParameter(cmd, "state", StateWaiting); | ||
AddCommandParameter(cmd, "noLaterThan", GetDbDateTimeValue(noLaterThan)); | ||
AddCommandParameter(cmd, "noEarlierThan", GetDbDateTimeValue(noEarlierThan)); | ||
|
||
using var rs = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); | ||
// signal cancel, otherwise ADO.NET might have trouble handling partial reads from open reader | ||
var shouldStop = false; | ||
while (await rs.ReadAsync(cancellationToken).ConfigureAwait(false)) | ||
{ | ||
if (shouldStop) | ||
{ | ||
cmd.Cancel(); | ||
break; | ||
} | ||
|
||
if (nextTriggers.Count < maxCount) | ||
{ | ||
var result = new TriggerAcquireResult( | ||
(string)rs[ColumnTriggerName], | ||
(string)rs[ColumnTriggerGroup], | ||
(string)rs[ColumnJobClass]); | ||
nextTriggers.Add(result); | ||
} | ||
else | ||
{ | ||
shouldStop = true; | ||
} | ||
} | ||
|
||
return nextTriggers; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters