Skip to content

Commit f306592

Browse files
[+] Added MethodTimer
[~] Report generaction is now threadsafe and runs parallel async CactuseSecurity#3055
1 parent 42d9e26 commit f306592

File tree

5 files changed

+78
-26
lines changed

5 files changed

+78
-26
lines changed

roles/middleware/files/FWO.Middleware.Server/FWO.Middleware.Server.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
</PropertyGroup>
99

1010
<ItemGroup>
11+
<PackageReference Include="MethodTimer.Fody" Version="3.2.3" />
1112
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.14" />
1213
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="4.0.0" />
1314
<PackageReference Include="PuppeteerSharp" Version="20.1.3" />
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
2+
<MethodTimer />
3+
</Weavers>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3+
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
4+
<xs:element name="Weavers">
5+
<xs:complexType>
6+
<xs:all>
7+
<xs:element name="MethodTimer" minOccurs="0" maxOccurs="1" type="xs:anyType" />
8+
</xs:all>
9+
<xs:attribute name="VerifyAssembly" type="xs:boolean">
10+
<xs:annotation>
11+
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
12+
</xs:annotation>
13+
</xs:attribute>
14+
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
15+
<xs:annotation>
16+
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
17+
</xs:annotation>
18+
</xs:attribute>
19+
<xs:attribute name="GenerateXsd" type="xs:boolean">
20+
<xs:annotation>
21+
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
22+
</xs:annotation>
23+
</xs:attribute>
24+
</xs:complexType>
25+
</xs:element>
26+
</xs:schema>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Reflection;
2+
3+
namespace FWO.Middleware.Server
4+
{
5+
public static class MethodTimeLogger
6+
{
7+
public static void Log(MethodBase methodBase, TimeSpan timeSpan, string message)
8+
{
9+
string additionalInfo = $" Additional Info: {message}";
10+
string duration = $" Runtime: {timeSpan:mm}m {timeSpan:ss}s {timeSpan:fff}ms";
11+
12+
Type type = methodBase.DeclaringType;
13+
Type? @interface = type!.GetInterfaces()
14+
.FirstOrDefault(i => type.GetInterfaceMap(i).TargetMethods.Any(m => m.DeclaringType == type));
15+
16+
string info = "Executed ";
17+
string logs = $"{DateTime.Now} | " + info + $"{methodBase.DeclaringType!.Name}.{methodBase.Name}.{duration}";
18+
19+
Logging.Log.WriteInfo("", logs);
20+
}
21+
}
22+
}
23+
24+

roles/middleware/files/FWO.Middleware.Server/ReportScheduler.cs

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using FWO.Basics;
1+
using FWO.Basics;
22
using FWO.Data.Report;
33
using FWO.Api.Client;
44
using FWO.Api.Client.Queries;
@@ -8,6 +8,10 @@
88
using FWO.Report;
99
using System.Timers;
1010
using FWO.Config.File;
11+
using System.Collections.Concurrent;
12+
using System;
13+
using MethodTimer;
14+
using System.Reflection;
1115

1216
namespace FWO.Middleware.Server
1317
{
@@ -16,8 +20,7 @@ namespace FWO.Middleware.Server
1620
/// </summary>
1721
public class ReportScheduler
1822
{
19-
private readonly object scheduledReportsLock = new ();
20-
private List<ReportSchedule> scheduledReports = [];
23+
private ConcurrentBag<ReportSchedule> scheduledReports = [];
2124
private readonly TimeSpan CheckScheduleInterval = TimeSpan.FromMinutes(1);
2225

2326
private readonly string apiServerUri;
@@ -27,7 +30,6 @@ public class ReportScheduler
2730
private readonly GraphQlApiSubscription<ReportSchedule[]> scheduledReportsSubscription;
2831
private readonly JwtWriter jwtWriter;
2932

30-
private readonly object ldapLock = new ();
3133
private List<Ldap> connectedLdaps;
3234

3335
/// <summary>
@@ -54,18 +56,12 @@ public ReportScheduler(ApiConnection apiConnection, JwtWriter jwtWriter, GraphQl
5456

5557
private void OnLdapUpdate(List<Ldap> connectedLdaps)
5658
{
57-
lock(ldapLock)
58-
{
59-
this.connectedLdaps = connectedLdaps;
60-
}
59+
this.connectedLdaps = connectedLdaps;
6160
}
6261

6362
private void OnScheduleUpdate(ReportSchedule[] scheduledReports)
6463
{
65-
lock (scheduledReportsLock)
66-
{
67-
this.scheduledReports = [.. scheduledReports];
68-
}
64+
this.scheduledReports = [.. scheduledReports];
6965
}
7066

7167
private void ApiExceptionHandler(Exception exception)
@@ -74,22 +70,22 @@ private void ApiExceptionHandler(Exception exception)
7470
// Subscription will be restored if no exception is thrown here
7571
}
7672

73+
[Time]
7774
private async void CheckSchedule(object? _, ElapsedEventArgs __)
7875
{
7976
List<Task> reportGeneratorTasks = [];
8077

8178
DateTime dateTimeNowRounded = RoundDown(DateTime.Now, CheckScheduleInterval);
8279

83-
lock (scheduledReports)
84-
{
85-
foreach (ReportSchedule reportSchedule in scheduledReports)
80+
await Parallel.ForEachAsync(scheduledReports, new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount },
81+
async (reportSchedule, ct) =>
8682
{
8783
try
8884
{
89-
if (reportSchedule.Active)
85+
if(reportSchedule.Active)
9086
{
9187
// Add schedule interval as long as schedule time is smaller then current time
92-
while (RoundDown(reportSchedule.StartTime, CheckScheduleInterval) < dateTimeNowRounded)
88+
while(RoundDown(reportSchedule.StartTime, CheckScheduleInterval) < dateTimeNowRounded)
9389
{
9490
reportSchedule.StartTime = reportSchedule.RepeatInterval switch
9591
{
@@ -102,26 +98,24 @@ private async void CheckSchedule(object? _, ElapsedEventArgs __)
10298
};
10399
}
104100

105-
if (RoundDown(reportSchedule.StartTime, CheckScheduleInterval) == dateTimeNowRounded)
101+
if(RoundDown(reportSchedule.StartTime, CheckScheduleInterval) == dateTimeNowRounded)
106102
{
107-
reportGeneratorTasks.Add(GenerateReport(reportSchedule, dateTimeNowRounded));
103+
await GenerateReport(reportSchedule, dateTimeNowRounded, ct);
108104
}
109105
}
110106
}
111-
catch (Exception exception)
107+
catch(Exception exception)
112108
{
113109
Log.WriteError("Report Scheduling", "Checking scheduled reports lead to exception.", exception);
114110
}
115-
}
116-
}
111+
});
117112

118-
await Task.WhenAll(reportGeneratorTasks);
119113
}
120114

121-
private Task GenerateReport(ReportSchedule reportSchedule, DateTime dateTimeNowRounded)
115+
[Time]
116+
private async Task GenerateReport(ReportSchedule reportSchedule, DateTime dateTimeNowRounded, CancellationToken token)
122117
{
123-
CancellationToken token = new ();
124-
return Task.Run(async () =>
118+
await Task.Run(async () =>
125119
{
126120
try
127121
{
@@ -157,6 +151,10 @@ private Task GenerateReport(ReportSchedule reportSchedule, DateTime dateTimeNowR
157151
Log.WriteInfo("Report Scheduling", $"Scheduled report \"{reportSchedule.Name}\" with id \"{reportSchedule.Id}\" for user \"{reportSchedule.ScheduleOwningUser.Name}\" with id \"{reportSchedule.ScheduleOwningUser.DbId}\" was empty.");
158152
}
159153
}
154+
catch(TaskCanceledException)
155+
{
156+
Log.WriteWarning("Report Scheduling", $"Generating scheduled report was cancelled");
157+
}
160158
catch (Exception exception)
161159
{
162160
Log.WriteError("Report Scheduling", $"Generating scheduled report \"{reportSchedule.Name}\" with id \"{reportSchedule.Id}\" lead to exception.", exception);

0 commit comments

Comments
 (0)