Skip to content

Commit 10dbdd6

Browse files
committed
准备加入 sql存储 执行历史
1 parent de3d828 commit 10dbdd6

File tree

10 files changed

+214
-15
lines changed

10 files changed

+214
-15
lines changed

sample2/.config/dotnet-tools.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"version": 1,
3+
"isRoot": true,
4+
"tools": {
5+
"dotnet-ef": {
6+
"version": "9.0.8",
7+
"commands": [
8+
"dotnet-ef"
9+
],
10+
"rollForward": false
11+
}
12+
}
13+
}

sample2/Program.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using Microsoft.AspNetCore.Authentication.Cookies;
22
using Microsoft.AspNetCore.Identity;
33
using Microsoft.EntityFrameworkCore;
4+
using Newtonsoft.Json.Linq;
45
using Quartz;
56
using SilkierQuartz;
67
using SilkierQuartz.Example;
78
using SilkierQuartz.Example.Jobs;
9+
using System.Collections.Specialized;
810
using System.Configuration;
911
using WebApplication1.Data;
1012

@@ -53,6 +55,11 @@
5355
authenticationOptions.AccessRequirement = SilkierQuartzAuthenticationOptions.SimpleAccessRequirement.AllowAnonymous;
5456
}
5557
#endif
58+
, stdSchedulerFactoryOptions =>
59+
{
60+
stdSchedulerFactoryOptions.Add("quartz.plugin.recentHistory.type", $"{nameof(Quartz.Plugins.RecentHistory.ExecutionHistoryPlugin)},{nameof(Quartz.Plugins.RecentHistory)}");
61+
stdSchedulerFactoryOptions.Add("quartz.plugin.recentHistory.storeType", $"{nameof(Quartz.Plugins.RecentHistory.Impl.SqlServerExecutionHistoryStore)},{nameof(Quartz.Plugins.RecentHistory)}");
62+
}
5663
);
5764
services.AddOptions();
5865
services.Configure<AppSettings>(configuration);
@@ -97,7 +104,7 @@
97104

98105
app.UseAuthentication();
99106
app.UseAuthorization();
100-
app.UseSilkierQuartz();
107+
app.UseSilkierQuartz( );
101108
app.MapRazorPages();
102109
#region 不使用 SilkierQuartzAttribe 属性的进行注册和使用的IJob,这里通过UseQuartzJob的IJob必须在 ConfigureServices进行AddQuartzJob
103110

sample2/sample2.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.0" />
12-
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.0" />
13-
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.0" />
14-
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
15-
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0">
11+
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.8" />
12+
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.8" />
13+
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.8" />
14+
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.8" />
15+
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.8">
1616
<PrivateAssets>all</PrivateAssets>
1717
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1818
</PackageReference>

src/Quartz.Plugins.RecentHistory/IExecutionHistoryStore.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44

55
namespace Quartz.Plugins.RecentHistory
66
{
7+
[Serializable]
8+
public class JobStats
9+
{
10+
public int TotalJobsExecuted { get; set; }
11+
public int TotalJobsFailed { get; set; }
12+
}
13+
714
[Serializable]
815
public class ExecutionHistoryEntry
916
{
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
using Dapper;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Data;
5+
using System.Data.Common;
6+
using System.Linq;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
10+
namespace Quartz.Plugins.RecentHistory.Impl
11+
{
12+
[Serializable]
13+
public class SqlServerExecutionHistoryStore : IExecutionHistoryStore
14+
{
15+
public string SchedulerName { get; set; }
16+
17+
18+
DateTime _nextPurgeTime = DateTime.UtcNow;
19+
int _updatesFromLastPurge;
20+
21+
int _totalJobsExecuted = 0, _totalJobsFailed = 0;
22+
private const string _prefix = "tb_quartz_";
23+
private readonly IDbConnection _dbConnection;
24+
public Task<ExecutionHistoryEntry> Get(string fireInstanceId)
25+
{
26+
var sql = $"SELECT * FROM {_prefix}ExecutionHistoryStore WHERE fire_instance_id = @FireInstanceId";
27+
return _dbConnection.QuerySingleOrDefaultAsync<ExecutionHistoryEntry>(sql, new { FireInstanceId = fireInstanceId });
28+
}
29+
30+
public async Task Purge()
31+
{
32+
var ids = new HashSet<string>((await FilterLastOfEveryTrigger(10)).Select(x => x.FireInstanceId));
33+
var sql = $"DELETE FROM {_prefix}ExecutionHistoryStore WHERE id NOT IN @Ids";
34+
await _dbConnection.ExecuteAsync(sql, new { Ids = ids });
35+
}
36+
37+
public async Task Save(ExecutionHistoryEntry entry)
38+
{
39+
_updatesFromLastPurge++;
40+
41+
if (_updatesFromLastPurge >= 10 || _nextPurgeTime < DateTime.UtcNow)
42+
{
43+
_nextPurgeTime = DateTime.UtcNow.AddMinutes(1);
44+
_updatesFromLastPurge = 0;
45+
await Purge();
46+
}
47+
var sql = $@"
48+
INSERT INTO {_prefix}ExecutionHistoryStore (
49+
fire_instance_id,
50+
scheduler_instance_id,
51+
scheduler_name,
52+
job,
53+
trigger,
54+
scheduled_fire_time_utc,
55+
actual_fire_time_utc,
56+
recovering,
57+
vetoed,
58+
finished_time_utc,
59+
exception_message
60+
) VALUES (
61+
@FireInstanceId,
62+
@SchedulerInstanceId,
63+
@SchedulerName,
64+
@Job,
65+
@Trigger,
66+
@ScheduledFireTimeUtc,
67+
@ActualFireTimeUtc,
68+
@Recovering,
69+
@Vetoed,
70+
@FinishedTimeUtc,
71+
@ExceptionMessage
72+
)";
73+
await _dbConnection.ExecuteAsync(sql, entry);
74+
}
75+
76+
public Task<IEnumerable<ExecutionHistoryEntry>> FilterLastOfEveryJob(int limitPerJob)
77+
{
78+
var sql = $@"
79+
SELECT *
80+
FROM (
81+
SELECT
82+
*,
83+
ROW_NUMBER() OVER (
84+
PARTITION BY job
85+
ORDER BY actual_fire_time_utc DESC
86+
) AS rn
87+
FROM {_prefix}ExecutionHistoryStore
88+
WHERE scheduler_name = @SchedulerName
89+
) t
90+
WHERE t.rn <= @LimitPerJob
91+
ORDER BY job, actual_fire_time_utc ASC;
92+
";
93+
94+
var result = _dbConnection.QueryAsync<ExecutionHistoryEntry>(
95+
sql, new { SchedulerName = SchedulerName, LimitPerJob = limitPerJob });
96+
return result;
97+
}
98+
99+
public Task<IEnumerable<ExecutionHistoryEntry>> FilterLastOfEveryTrigger(int limitPerTrigger)
100+
{
101+
102+
var sql = $@"
103+
SELECT *
104+
FROM (
105+
SELECT
106+
*,
107+
ROW_NUMBER() OVER (
108+
PARTITION BY trigger
109+
ORDER BY actual_fire_time_utc DESC
110+
) AS rn
111+
FROM {_prefix}ExecutionHistoryStore
112+
WHERE scheduler_name = @SchedulerName
113+
) t
114+
WHERE t.rn <= @LimitPerTrigger
115+
ORDER BY trigger, actual_fire_time_utc ASC;
116+
";
117+
118+
var result = _dbConnection.QueryAsync<ExecutionHistoryEntry>(
119+
sql, new { SchedulerName, LimitPerTrigger = limitPerTrigger });
120+
return result;
121+
}
122+
123+
public Task<IEnumerable<ExecutionHistoryEntry>> FilterLast(int limit)
124+
{
125+
126+
var sql = $@"
127+
SELECT *
128+
FROM (
129+
SELECT *
130+
FROM {_prefix}ExecutionHistoryStore
131+
WHERE scheduler_name = @SchedulerName
132+
ORDER BY actual_fire_time_utc DESC
133+
LIMIT @Limit
134+
) t
135+
ORDER BY actual_fire_time_utc ASC;
136+
";
137+
138+
var result = _dbConnection.QueryAsync<ExecutionHistoryEntry>(
139+
sql, new { SchedulerName, Limit = limit });
140+
return result;
141+
}
142+
143+
private const int StatsId = 1; // 只有一行,ID恒为1
144+
145+
public async Task<int> GetTotalJobsExecuted()
146+
{
147+
var sql = $@"SELECT total_jobs_executed AS TotalJobsExecuted, total_jobs_failed AS TotalJobsFailed
148+
FROM {_prefix}JobStats WHERE id = @Id";
149+
var js= await _dbConnection.QuerySingleAsync<JobStats>(sql, new { Id = StatsId });
150+
return js.TotalJobsExecuted;
151+
}
152+
public async Task<int> GetTotalJobsFailed()
153+
{
154+
var sql = $@"SELECT total_jobs_executed AS TotalJobsExecuted, total_jobs_failed AS TotalJobsFailed
155+
FROM {_prefix}JobStats WHERE id = @Id";
156+
var js = await _dbConnection.QuerySingleAsync<JobStats>(sql, new { Id = StatsId });
157+
return js.TotalJobsFailed;
158+
}
159+
160+
public async Task IncrementTotalJobsExecuted()
161+
{
162+
var sql = $@"UPDATE {_prefix}JobStats SET total_jobs_executed = total_jobs_executed + 1 WHERE id = @Id";
163+
await _dbConnection.ExecuteAsync(sql, new { Id = StatsId });
164+
}
165+
166+
public async Task IncrementTotalJobsFailed()
167+
{
168+
var sql = $@"UPDATE {_prefix}JobStats SET total_jobs_failed = total_jobs_failed + 1 WHERE id = @Id";
169+
await _dbConnection.ExecuteAsync(sql, new { Id = StatsId });
170+
}
171+
}
172+
}

src/Quartz.Plugins.RecentHistory/Quartz.Plugins.RecentHistory.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
</ItemGroup>
3030

3131
<ItemGroup>
32-
<PackageReference Include="Quartz" Version="3.14.0" />
32+
<PackageReference Include="Dapper" Version="2.1.66" />
33+
<PackageReference Include="Quartz" Version="3.15.0" />
3334
</ItemGroup>
3435

3536
<ItemGroup>

src/SilkierQuartz/Configuration/ApplicationBuilderExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public static IApplicationBuilder UseSilkierQuartz(
6868
{
6969
options.Scheduler = app.ApplicationServices.GetRequiredService<ISchedulerFactory>()?.GetScheduler().Result;
7070
}
71-
catch (Exception)
71+
catch (Exception ex)
7272
{
7373
options.Scheduler = null;
7474
}

src/SilkierQuartz/HostedService/QuartzHostedService.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ public QuartzHostedService(
3232
public async Task StartAsync(CancellationToken cancellationToken)
3333
{
3434
var _scheduleJobs = Services.GetService<IEnumerable<IScheduleJob>>();
35-
3635
_scheduler = await _schedulerFactory.GetScheduler();
3736
_scheduler.JobFactory = _jobFactory;
3837

src/SilkierQuartz/SilkierQuartz.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
<ItemGroup>
4141
<PackageReference Include="Handlebars.Net" Version="2.1.6" />
4242
<PackageReference Include="JsonSubTypes" Version="2.0.1" />
43-
<PackageReference Include="Quartz" Version="3.14.0" />
43+
<PackageReference Include="Quartz" Version="3.15.0" />
4444
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
4545
</ItemGroup>
4646
<ItemGroup Condition="'$(TargetFramework)' == 'net9'">

test/SilkierQuartz.Test.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88

99
<ItemGroup>
1010
<PackageReference Include="FakeItEasy" Version="8.3.0" />
11-
<PackageReference Include="FluentAssertions" Version="7.0.0-alpha.5" />
12-
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
13-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
14-
<PackageReference Include="xunit" Version="2.9.2" />
15-
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
11+
<PackageReference Include="FluentAssertions" Version="8.5.0" />
12+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
14+
<PackageReference Include="xunit" Version="2.9.3" />
15+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3">
1616
<PrivateAssets>all</PrivateAssets>
1717
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
1818
</PackageReference>

0 commit comments

Comments
 (0)