From 6f3f08ed340e59a62a2d0428a5c32f99551ef1ce Mon Sep 17 00:00:00 2001 From: Rory& Date: Tue, 3 Jun 2025 01:01:40 +0200 Subject: Fix performance issues, add fake user bot to test client, more testing --- testFrontend/SafeNSound.FakeUser/MonitorService.cs | 52 ++++++++++++ testFrontend/SafeNSound.FakeUser/Program.cs | 22 +++++ .../SafeNSound.FakeUser/RandomAlarmService.cs | 35 ++++++++ .../SafeNSound.FakeUser/SafeNSound.FakeUser.csproj | 10 ++- testFrontend/SafeNSound.FakeUser/UserStore.cs | 97 ++++++++++++++++++++++ testFrontend/SafeNSound.FakeUser/appsettings.json | 13 +++ 6 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 testFrontend/SafeNSound.FakeUser/MonitorService.cs create mode 100644 testFrontend/SafeNSound.FakeUser/RandomAlarmService.cs create mode 100644 testFrontend/SafeNSound.FakeUser/UserStore.cs create mode 100644 testFrontend/SafeNSound.FakeUser/appsettings.json (limited to 'testFrontend/SafeNSound.FakeUser') diff --git a/testFrontend/SafeNSound.FakeUser/MonitorService.cs b/testFrontend/SafeNSound.FakeUser/MonitorService.cs new file mode 100644 index 0000000..57d90a5 --- /dev/null +++ b/testFrontend/SafeNSound.FakeUser/MonitorService.cs @@ -0,0 +1,52 @@ +using ArcaneLibs.Extensions; + +namespace SafeNSound.FakeUser; + +public class MonitorService(ILogger logger, UserStore userStore): IHostedService { + private Task? _getAllAlarmsTask, _assignBudgetTask; + private readonly CancellationTokenSource _cts = new(); + + public async Task StartAsync(CancellationToken cancellationToken) { + _getAllAlarmsTask = GetAllAlarms(_cts.Token); + _assignBudgetTask = AssignBudget(_cts.Token); + } + + private async Task GetAllAlarms(CancellationToken cancellationToken) { + while (!cancellationToken.IsCancellationRequested) { + try { + var user = userStore.GetRandomMonitor(); + var alarms = await user.Client!.GetAllAlarms(); + if(alarms.Count > 0) + logger.LogInformation("Monitor {UserId} has outstanding alarms: {Alarm}", user.Auth.Username, alarms.ToJson(indent: false)); + // else + // logger.LogInformation("Monitor {UserId} found no alarms to query", user.Auth.Username); + await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); + } catch (Exception ex) { + logger.LogError(ex, "Error querying alarm"); + } + } + } + + private async Task AssignBudget(CancellationToken cancellationToken) { + while (!cancellationToken.IsCancellationRequested) { + try { + var user = userStore.GetRandomMonitor(); + // var alarms = await user.Client!.GetAllAlarms(); + // if(alarms.Count > 0) + // logger.LogInformation("Monitor {UserId} has outstanding alarms: {Alarm}", user.Auth.Username, alarms.ToJson(indent: false)); + // else + // logger.LogInformation("Monitor {UserId} found no alarms to query", user.Auth.Username); + await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); + + } catch (Exception ex) { + logger.LogError(ex, "Error querying alarm"); + } + + } + } + + public async Task StopAsync(CancellationToken cancellationToken) { + await _cts.CancelAsync(); + await _getAllAlarmsTask!; + } +} \ No newline at end of file diff --git a/testFrontend/SafeNSound.FakeUser/Program.cs b/testFrontend/SafeNSound.FakeUser/Program.cs index 3751555..7c3eaff 100644 --- a/testFrontend/SafeNSound.FakeUser/Program.cs +++ b/testFrontend/SafeNSound.FakeUser/Program.cs @@ -1,2 +1,24 @@ // See https://aka.ms/new-console-template for more information + +using SafeNSound.FakeUser; +using SafeNSound.Sdk; + Console.WriteLine("Hello, World!"); + +var builder = Host.CreateApplicationBuilder(args); +// longer shutdown timeout +builder.Services.Configure(options => { + options.ShutdownTimeout = TimeSpan.FromSeconds(120); +}); + +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddHostedService(sp => sp.GetRequiredService()); +builder.Services.AddHostedService(); +builder.Services.AddHostedService(); + +// WrappedHttpClient.LogRequests = false; + +var host = builder.Build(); +host.Run(); \ No newline at end of file diff --git a/testFrontend/SafeNSound.FakeUser/RandomAlarmService.cs b/testFrontend/SafeNSound.FakeUser/RandomAlarmService.cs new file mode 100644 index 0000000..7835f89 --- /dev/null +++ b/testFrontend/SafeNSound.FakeUser/RandomAlarmService.cs @@ -0,0 +1,35 @@ +namespace SafeNSound.FakeUser; + +public class RandomAlarmService(UserStore userStore): IHostedService { + private Task? _listenerTask; + private readonly CancellationTokenSource _cts = new(); + + public async Task StartAsync(CancellationToken cancellationToken) { + _listenerTask = Run(_cts.Token); + } + + private async Task Run(CancellationToken cancellationToken) { + while (!cancellationToken.IsCancellationRequested) { + try { + var user = userStore.GetRandomUser(); + if (Random.Shared.Next(100) > 90) { + await user.Client!.SetAlarm(new Sdk.AlarmDto { + Reason = "fall" + }); + } + else { + await user.Client!.DeleteAlarm(); + } + } + catch (Exception ex) { + Console.WriteLine($"Error setting/deleting alarm: {ex.Message}"); + } + + await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken); + } + } + + public async Task StopAsync(CancellationToken cancellationToken) { + await _cts.CancelAsync(); + } +} \ No newline at end of file diff --git a/testFrontend/SafeNSound.FakeUser/SafeNSound.FakeUser.csproj b/testFrontend/SafeNSound.FakeUser/SafeNSound.FakeUser.csproj index fd4bd08..830faaa 100644 --- a/testFrontend/SafeNSound.FakeUser/SafeNSound.FakeUser.csproj +++ b/testFrontend/SafeNSound.FakeUser/SafeNSound.FakeUser.csproj @@ -1,4 +1,4 @@ - + Exe @@ -7,4 +7,12 @@ enable + + + + + + + + diff --git a/testFrontend/SafeNSound.FakeUser/UserStore.cs b/testFrontend/SafeNSound.FakeUser/UserStore.cs new file mode 100644 index 0000000..9b04efb --- /dev/null +++ b/testFrontend/SafeNSound.FakeUser/UserStore.cs @@ -0,0 +1,97 @@ +using SafeNSound.Sdk; + +namespace SafeNSound.FakeUser; + +public class UserStore(SafeNSoundAuthentication authService, SafeNSoundConfiguration config) : IHostedService { + public List Admins { get; } = Enumerable.Range(0, 1).Select(_ => new ClientContainer()).ToList(); + public List Monitors { get; } = Enumerable.Range(0, 5).Select(_ => new ClientContainer()).ToList(); + public List Users { get; } = Enumerable.Range(0, 150000).Select(_ => new ClientContainer()).ToList(); + public List AllUsers => [.. Users, .. Monitors, .. Admins]; + + public ClientContainer GetRandomUser() { + ClientContainer user; + do { + user = Users[new Random().Next(Users.Count)]; + } while (user.Client == null); + + return user; + } + + public ClientContainer GetRandomMonitor() { + ClientContainer user; + do { + user = Monitors[new Random().Next(Monitors.Count)]; + } while (user.Client == null); + + return user; + } + + public ClientContainer GetRandomAdmin() { + ClientContainer user; + do { + user = Admins[new Random().Next(Admins.Count)]; + } while (user.Client == null); + + return user; + } + + public ClientContainer GetRandomUserOfAnyType() { + ClientContainer user; + do { + user = AllUsers[new Random().Next(AllUsers.Count)]; + } while (user.Client == null); + + return user; + } + + public async Task StartAsync(CancellationToken cancellationToken) { + Admins.ForEach(x => x.Auth.UserType = "admin"); + Monitors.ForEach(x => x.Auth.UserType = "monitor"); + var ss = new SemaphoreSlim(256, 256); + var tasks = ((ClientContainer[]) [..Users, ..Monitors, ..Admins]).Select(async container => { + await ss.WaitAsync(); + await authService.Register(container.Auth); + // container.Client = new SafeNSoundClient(config, (await authService.Login(container.Auth)).AccessToken); + // container.WhoAmI = await container.Client.WhoAmI(); + ss.Release(); + }).ToList(); + await Task.WhenAll(tasks); + + var users = Users.ToArray(); + tasks = Monitors.Select(async container => { + var items = Random.Shared.GetItems(users, Users.Count / Monitors.Count).DistinctBy(x=>x.WhoAmI!.UserId); + foreach (var user in items) { + await container.Client!.AddAssignedUser(user.WhoAmI!.UserId); + } + }).ToList(); + await Task.WhenAll(tasks); + } + + public async Task StopAsync(CancellationToken cancellationToken) { + await Task.WhenAll(Users.Select(Cleanup).ToList()); + await Task.WhenAll(Monitors.Select(Cleanup).ToList()); + await Task.WhenAll(Admins.Select(Cleanup).ToList()); + } + + private async Task Cleanup(ClientContainer container) { + if (container.Client == null) return; + try { + await container.Client.DeleteAccount(container.Auth); + } + catch { + Console.WriteLine("Failed to delete account for user: " + container.Auth.Username); + } + } + + public class ClientContainer { + public RegisterDto Auth { get; set; } = new() { + Email = $"{Guid.NewGuid()}@example.com", + Username = $"user-{Guid.NewGuid()}", + Password = Guid.NewGuid().ToString(), + UserType = "user" + }; + + public SafeNSoundClient? Client { get; set; } + public WhoAmI? WhoAmI { get; set; } + } +} \ No newline at end of file diff --git a/testFrontend/SafeNSound.FakeUser/appsettings.json b/testFrontend/SafeNSound.FakeUser/appsettings.json new file mode 100644 index 0000000..f77ef07 --- /dev/null +++ b/testFrontend/SafeNSound.FakeUser/appsettings.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "System": "Information", + "Microsoft": "Information", + "ArcaneLibs.Blazor.Components.AuthorizedImage": "Information" + } + }, + "SafeNSound": { + "BaseUrl": "http://localhost:3000" + } +} -- cgit 1.5.1