Skip to content

Commit 7966a79

Browse files
committed
Optimize BackgroundTasks
1 parent 2673aa6 commit 7966a79

File tree

12 files changed

+190
-131
lines changed

12 files changed

+190
-131
lines changed

src/DragonFly.App.Server/Program.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,16 @@
5959

6060
//demo tasks
6161
IBackgroundTaskManager taskManager = app.Services.GetRequiredService<IBackgroundTaskManager>();
62-
taskManager.StartNew("Test", static async ctx => { await Task.Delay(TimeSpan.FromSeconds(60), ctx.CancellationToken); });
63-
taskManager.StartNew("Import", static async ctx =>
62+
await taskManager.StartNewAsync("Test", static async ctx => { await Task.Delay(TimeSpan.FromSeconds(60), ctx.CancellationToken); });
63+
await taskManager.StartNewAsync("Import", static async ctx =>
6464
{
65+
int counter = 0;
66+
6567
while (ctx.CancellationToken.IsCancellationRequested == false)
6668
{
6769
await Task.Delay(TimeSpan.FromSeconds(1));
6870

69-
await ctx.IncrementProgressValueAsync();
71+
await ctx.UpdateAsync($"test {counter}", counter++, ctx.Task.ProgressMaxValue);
7072

7173
if (ctx.Task.ProgressValue >= ctx.Task.ProgressMaxValue)
7274
{

src/DragonFly.AspNetCore/BackgroundTasks/BackgroundTask.cs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class BackgroundTask
1313
{
1414
public BackgroundTask(long id, string name, ClaimsPrincipal? createdBy)
1515
{
16-
Id = id;
16+
Id = id;
1717
Name = name;
1818
ProgressValue = 0;
1919
ProgressMaxValue = 100;
@@ -47,12 +47,12 @@ public BackgroundTask(long id, string name, ClaimsPrincipal? createdBy)
4747
/// <summary>
4848
/// StartedAt
4949
/// </summary>
50-
public DateTimeOffset? StartedAt { get; internal set; }
50+
public DateTimeOffset? StartedAt { get; private set; }
5151

5252
/// <summary>
5353
/// ExitAt
5454
/// </summary>
55-
public DateTimeOffset? ExitAt { get; internal set; }
55+
public DateTimeOffset? ExitAt { get; private set; }
5656

5757
/// <summary>
5858
/// ProgressValue
@@ -67,7 +67,7 @@ public BackgroundTask(long id, string name, ClaimsPrincipal? createdBy)
6767
/// <summary>
6868
/// Status
6969
/// </summary>
70-
public string Status { get; set; }
70+
public string? Status { get; set; }
7171

7272
/// <summary>
7373
/// Action
@@ -77,7 +77,7 @@ public BackgroundTask(long id, string name, ClaimsPrincipal? createdBy)
7777
/// <summary>
7878
/// State
7979
/// </summary>
80-
public BackgroundTaskState State { get; internal set; }
80+
public BackgroundTaskState State { get; private set; }
8181

8282
/// <summary>
8383
/// CancellationToken
@@ -89,6 +89,15 @@ public BackgroundTask(long id, string name, ClaimsPrincipal? createdBy)
8989
/// </summary>
9090
public Exception? Exception { get; private set; }
9191

92+
/// <summary>
93+
/// SetRunning
94+
/// </summary>
95+
public void SetRunning()
96+
{
97+
StartedAt = DateTimeOffset.Now;
98+
State = BackgroundTaskState.Running;
99+
}
100+
92101
/// <summary>
93102
/// SetException
94103
/// </summary>

src/DragonFly.AspNetCore/BackgroundTasks/BackgroundTaskContext.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// https://github.com/usercode/DragonFly
33
// MIT License
44

5+
using System.Collections.Generic;
56
using System.Runtime.CompilerServices;
67

78
namespace DragonFly;
@@ -35,24 +36,24 @@ public BackgroundTaskContext(BackgroundTask task, IServiceProvider serviceProvid
3536

3637
public event Func<Task>? StateChanged;
3738

38-
public async Task SetProgressAsync(long value)
39+
public async Task UpdateAsync(string? status, long progressValue, long maxProgressValue)
3940
{
40-
Task.ProgressValue = value;
41+
Task.Status = status;
42+
Task.ProgressValue = progressValue;
43+
Task.ProgressMaxValue = maxProgressValue;
4144

42-
await StateChanged?.Invoke();
45+
await StateHasChanged();
4346
}
4447

45-
public async Task IncrementProgressValueAsync()
48+
public async Task UpdateAsync(Action<BackgroundTask> action)
4649
{
47-
Task.ProgressValue++;
50+
action(Task);
4851

49-
await StateChanged?.Invoke();
52+
await StateHasChanged();
5053
}
5154

52-
public async Task SetProgressMaxValueAsync(long max)
55+
private async Task StateHasChanged()
5356
{
54-
Task.ProgressMaxValue = max;
55-
5657
await StateChanged?.Invoke();
5758
}
5859
}

src/DragonFly.AspNetCore/BackgroundTasks/BackgroundTaskManager.cs

Lines changed: 60 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -36,83 +36,97 @@ public BackgroundTaskManager(IHubContext<BackgroundTaskHub> hub, IServiceProvide
3636
/// </summary>
3737
private ILogger<BackgroundTaskManager> Logger { get; }
3838

39-
private readonly IDictionary<long, BackgroundTask> Tasks = new Dictionary<long, BackgroundTask>();
39+
private readonly IDictionary<long, BackgroundTask> Tasks = new Dictionary<long, BackgroundTask>();
4040

4141
private long _nextId = 0;
4242
private object _syncObject = new object();
4343

44+
private async Task TaskHasChangedAsnyc()
45+
{
46+
await Hub.Clients.All.SendAsync("TaskChanged");
47+
}
48+
4449
/// <summary>
45-
/// StartNew
50+
/// StartNewAsync
4651
/// </summary>
47-
/// <param name="task"></param>
48-
public BackgroundTask StartNew(string name, Func<BackgroundTaskContext, Task> action)
52+
public Task<BackgroundTask> StartNewAsync(string name, Func<BackgroundTaskContext, Task> action)
4953
{
50-
return StartNew(name, 0, (ctx) => action(ctx));
54+
return StartNewAsync(name, 0, (ctx) => action(ctx));
5155
}
5256

53-
public BackgroundTask StartNew<T>(string name, T input, Func<BackgroundTaskContext<T>, Task> action)
57+
/// <summary>
58+
/// StartNewAsync
59+
/// </summary>
60+
public async Task<BackgroundTask> StartNewAsync<T>(string name, T input, Func<BackgroundTaskContext<T>, Task> action)
5461
{
62+
long id;
63+
5564
lock (_syncObject)
5665
{
57-
BackgroundTask backgroundTask = new BackgroundTask(_nextId++, name, PermissionState.GetPrincipal());
66+
id = _nextId++;
67+
}
68+
69+
BackgroundTask backgroundTask = new BackgroundTask(id, name, PermissionState.GetPrincipal());
70+
71+
backgroundTask.Task = Task.Run(async () =>
72+
{
73+
BackgroundTaskContext<T> ctx = new BackgroundTaskContext<T>(backgroundTask, ServiceProvider, input, backgroundTask.CancellationTokenSource.Token);
74+
ctx.StateChanged += () => TaskHasChangedAsnyc();
5875

59-
backgroundTask.Task = Task.Run(async () =>
76+
try
6077
{
61-
try
62-
{
63-
await Task.Delay(TimeSpan.FromSeconds(2));
78+
await Task.Delay(TimeSpan.FromSeconds(2));
6479

65-
Logger.LogInformation("Background task is started: {name}", backgroundTask.Name);
80+
Logger.LogInformation("Background task is started: {name}", backgroundTask.Name);
6681

67-
backgroundTask.StartedAt = DateTimeOffset.Now;
68-
backgroundTask.State = BackgroundTaskState.Running;
82+
backgroundTask.SetRunning();
6983

70-
BackgroundTaskContext<T> ctx = new BackgroundTaskContext<T>(backgroundTask, ServiceProvider, input, backgroundTask.CancellationTokenSource.Token);
71-
ctx.StateChanged += () => Hub.Clients.All.SendAsync("TaskChanged");
84+
await TaskHasChangedAsnyc();
7285

73-
await action(ctx);
86+
await action(ctx);
7487

75-
if (backgroundTask.State == BackgroundTaskState.Canceling)
76-
{
77-
backgroundTask.SetCanceled();
78-
}
79-
else
80-
{
81-
backgroundTask.SetCompleted();
82-
}
83-
}
84-
catch (TaskCanceledException)
88+
if (backgroundTask.State == BackgroundTaskState.Canceling)
8589
{
8690
backgroundTask.SetCanceled();
8791
}
88-
catch (Exception ex)
92+
else
8993
{
90-
backgroundTask.SetException(ex);
94+
backgroundTask.SetCompleted();
9195
}
92-
finally
93-
{
94-
Logger.LogInformation("Background task is exited: {id} / {name}", backgroundTask.Id, backgroundTask.Name);
96+
}
97+
catch (TaskCanceledException)
98+
{
99+
backgroundTask.SetCanceled();
100+
}
101+
catch (Exception ex)
102+
{
103+
backgroundTask.SetException(ex);
104+
}
105+
finally
106+
{
107+
Logger.LogInformation("Background task is exited: {id} / {name}", backgroundTask.Id, backgroundTask.Name);
95108

96-
await Hub.Clients.All.SendAsync("TaskChanged");
109+
await TaskHasChangedAsnyc();
97110

98-
await Task.Delay(TimeSpan.FromMinutes(10));
111+
await Task.Delay(TimeSpan.FromMinutes(10));
99112

100-
lock (_syncObject)
101-
{
102-
Tasks.Remove(backgroundTask.Id);
103-
}
113+
lock (_syncObject)
114+
{
115+
Tasks.Remove(backgroundTask.Id);
104116
}
105-
});
106117

107-
Tasks.Add(backgroundTask.Id, backgroundTask);
118+
await TaskHasChangedAsnyc();
119+
}
120+
});
108121

109-
return backgroundTask;
122+
lock (_syncObject)
123+
{
124+
Tasks.Add(backgroundTask.Id, backgroundTask);
110125
}
111-
}
112126

113-
private void Ctx_StateChanged()
114-
{
115-
throw new NotImplementedException();
127+
await TaskHasChangedAsnyc();
128+
129+
return backgroundTask;
116130
}
117131

118132
public Task<IEnumerable<BackgroundTaskInfo>> GetTasksAsync()
@@ -134,6 +148,7 @@ public Task<IEnumerable<BackgroundTaskInfo>> GetTasksAsync()
134148
State = x.State,
135149
Exception = x.Exception?.Message
136150
})
151+
.OrderBy(x => x.Id)
137152
.ToList());
138153
}
139154
}

src/DragonFly.AspNetCore/BackgroundTasks/IBackgroundTaskManager.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace DragonFly;
66

77
public interface IBackgroundTaskManager : IBackgroundTaskService
88
{
9-
BackgroundTask StartNew(string name, Func<BackgroundTaskContext, Task> action);
9+
Task<BackgroundTask> StartNewAsync(string name, Func<BackgroundTaskContext, Task> action);
1010

11-
BackgroundTask StartNew<T>(string name, T input, Func<BackgroundTaskContext<T>, Task> action);
11+
Task<BackgroundTask> StartNewAsync<T>(string name, T input, Func<BackgroundTaskContext<T>, Task> action);
1212
}

src/DragonFly.AspNetCore/Interceptors/WebHookInterceptor.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ public WebHookInterceptor(
4747

4848
public async Task OnPublishedAsync(ContentItem contentItem)
4949
{
50-
BackgroundTaskManager.StartNew("WebHook.ContentPublished", contentItem, async ctx =>
50+
await BackgroundTaskManager.StartNewAsync("WebHook.ContentPublished", contentItem, async ctx =>
5151
{
5252
QueryResult<WebHook> result = await PermissionState.SuppressAsync(() => WebHookStorage.QueryAsync(new WebHookQuery()));
5353

54-
await ctx.SetProgressMaxValueAsync(result.TotalCount);
54+
int counter = 0;
5555

5656
foreach (WebHook item in result.Items)
5757
{
@@ -82,18 +82,18 @@ public async Task OnPublishedAsync(ContentItem contentItem)
8282
Logger.LogError("Failed to send webhook for published content: {schema}/{id} HTTP-Status:{status}", contentItem.Schema.Name, contentItem.Id, response.StatusCode);
8383
}
8484

85-
await ctx.IncrementProgressValueAsync();
85+
await ctx.UpdateAsync(item.Name, counter++, result.TotalCount);
8686
}
8787
});
8888
}
8989

9090
public async Task OnPublishedAsync(Asset asset)
9191
{
92-
BackgroundTaskManager.StartNew("WebHook.AssetPublished", asset, async ctx =>
92+
await BackgroundTaskManager.StartNewAsync("WebHook.AssetPublished", asset, async ctx =>
9393
{
9494
QueryResult<WebHook> result = await PermissionState.SuppressAsync(() => WebHookStorage.QueryAsync(new WebHookQuery()));
9595

96-
await ctx.SetProgressMaxValueAsync(result.TotalCount);
96+
int counter = 0;
9797

9898
foreach (WebHook item in result.Items)
9999
{
@@ -108,7 +108,7 @@ public async Task OnPublishedAsync(Asset asset)
108108
Logger.LogError("Failed to send webhook for published asset: {asset} HTTP-Status:{status}", asset.Id, response.StatusCode);
109109
}
110110

111-
await ctx.IncrementProgressValueAsync();
111+
await ctx.UpdateAsync(item.Name, counter++, result.TotalCount);
112112
}
113113
});
114114
}
@@ -120,11 +120,11 @@ public async Task OnPublishingAsync(ContentItem contentItem)
120120

121121
public async Task OnUnpublishedAsync(ContentItem contentItem)
122122
{
123-
BackgroundTaskManager.StartNew("WebHook.ContentUnpublished", contentItem, async ctx =>
123+
await BackgroundTaskManager.StartNewAsync("WebHook.ContentUnpublished", contentItem, async ctx =>
124124
{
125125
QueryResult<WebHook> result = await PermissionState.SuppressAsync(() => WebHookStorage.QueryAsync(new WebHookQuery()));
126126

127-
await ctx.SetProgressMaxValueAsync(result.TotalCount);
127+
int counter = 0;
128128

129129
foreach (WebHook item in result.Items)
130130
{
@@ -155,7 +155,7 @@ public async Task OnUnpublishedAsync(ContentItem contentItem)
155155
Logger.LogError("Failed to send webhook for unpublished content: {schema}/{id} HTTP-Status:{status}", contentItem.Schema.Name, contentItem.Id, response.StatusCode);
156156
}
157157

158-
await ctx.IncrementProgressValueAsync();
158+
await ctx.UpdateAsync(item.Name, counter++, result.TotalCount);
159159
}
160160
});
161161
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) usercode
2+
// https://github.com/usercode/DragonFly
3+
// MIT License
4+
5+
namespace DragonFly.Client;
6+
7+
public static class CssExtensions
8+
{
9+
public static string ToCss(this BackgroundTaskInfo taskInfo)
10+
{
11+
return taskInfo.State switch
12+
{
13+
BackgroundTaskState.Awaiting => null,
14+
BackgroundTaskState.Completed => "list-group-item-success",
15+
BackgroundTaskState.Failed => "list-group-item-danger",
16+
BackgroundTaskState.Canceled => "list-group-item-warning",
17+
BackgroundTaskState.Canceling => "list-group-item-warning",
18+
_ => "list-group-item-light"
19+
};
20+
}
21+
}

0 commit comments

Comments
 (0)