Adjust load tester. Made AddOrUpdateDevice async.

This commit is contained in:
Jared Goodwin 2023-06-02 13:31:01 -07:00
parent 382a9a6c32
commit 934bf9a14a
5 changed files with 145 additions and 110 deletions

View File

@ -87,13 +87,13 @@ namespace Remotely.Server.Hubs
}
}
public Task<bool> DeviceCameOnline(Device device)
public async Task<bool> DeviceCameOnline(Device device)
{
try
{
if (CheckForDeviceBan(device.ID, device.DeviceName))
{
return Task.FromResult(false);
return false;
}
var ip = Context.GetHttpContext()?.Connection?.RemoteIpAddress;
@ -105,12 +105,13 @@ namespace Remotely.Server.Hubs
if (CheckForDeviceBan(device.PublicIP))
{
return Task.FromResult(false);
return false;
}
if (_dataService.AddOrUpdateDevice(device, out var updatedDevice))
var result = await _dataService.AddOrUpdateDevice(device);
if (result.IsSuccess)
{
Device = updatedDevice;
Device = result.Value;
_serviceSessionCache.AddOrUpdateByConnectionId(Context.ConnectionId, Device);
@ -124,14 +125,14 @@ namespace Remotely.Server.Hubs
foreach (var connection in connections)
{
connection.InvokeCircuitEvent(CircuitEventName.DeviceUpdate, Device);
await connection.InvokeCircuitEvent(CircuitEventName.DeviceUpdate, Device);
}
return Task.FromResult(true);
return true;
}
else
{
// Organization wasn't found.
return Task.FromResult(false);
return false;
}
}
catch (Exception ex)
@ -140,7 +141,7 @@ namespace Remotely.Server.Hubs
}
Context.Abort();
return Task.FromResult(false);
return false;
}
public async Task DeviceHeartbeat(Device device)
@ -163,8 +164,14 @@ namespace Remotely.Server.Hubs
}
_dataService.AddOrUpdateDevice(device, out var updatedDevice);
Device = updatedDevice;
var result = await _dataService.AddOrUpdateDevice(device);
if (result.IsSuccess)
{
return;
}
Device = result.Value;
_serviceSessionCache.AddOrUpdateByConnectionId(Context.ConnectionId, Device);

View File

@ -1,4 +1,5 @@
using Immense.RemoteControl.Shared.Models;
using Immense.RemoteControl.Shared;
using Immense.RemoteControl.Shared.Models;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
@ -29,7 +30,7 @@ namespace Remotely.Server.Services
InviteLink AddInvite(string orgID, InviteViewModel invite);
bool AddOrUpdateDevice(Device device, out Device updatedDevice);
Task<Result<Device>> AddOrUpdateDevice(Device device);
Task AddOrUpdateSavedScript(SavedScript script, string userId);
@ -322,45 +323,44 @@ namespace Remotely.Server.Services
return inviteLink;
}
public bool AddOrUpdateDevice(Device device, out Device updatedDevice)
public async Task<Result<Device>> AddOrUpdateDevice(Device device)
{
using var dbContext = _appDbFactory.GetContext();
var existingDevice = dbContext.Devices.Find(device.ID);
if (existingDevice != null)
var resultDevice = await dbContext.Devices.FindAsync(device.ID);
if (resultDevice != null)
{
existingDevice.CurrentUser = device.CurrentUser;
existingDevice.DeviceName = device.DeviceName;
existingDevice.Drives = device.Drives;
existingDevice.CpuUtilization = device.CpuUtilization;
existingDevice.UsedMemory = device.UsedMemory;
existingDevice.UsedStorage = device.UsedStorage;
existingDevice.Is64Bit = device.Is64Bit;
existingDevice.IsOnline = true;
existingDevice.OSArchitecture = device.OSArchitecture;
existingDevice.OSDescription = device.OSDescription;
existingDevice.Platform = device.Platform;
existingDevice.ProcessorCount = device.ProcessorCount;
existingDevice.PublicIP = device.PublicIP;
existingDevice.TotalMemory = device.TotalMemory;
existingDevice.TotalStorage = device.TotalStorage;
existingDevice.AgentVersion = device.AgentVersion;
existingDevice.LastOnline = DateTimeOffset.Now;
updatedDevice = existingDevice;
resultDevice.CurrentUser = device.CurrentUser;
resultDevice.DeviceName = device.DeviceName;
resultDevice.Drives = device.Drives;
resultDevice.CpuUtilization = device.CpuUtilization;
resultDevice.UsedMemory = device.UsedMemory;
resultDevice.UsedStorage = device.UsedStorage;
resultDevice.Is64Bit = device.Is64Bit;
resultDevice.IsOnline = true;
resultDevice.OSArchitecture = device.OSArchitecture;
resultDevice.OSDescription = device.OSDescription;
resultDevice.Platform = device.Platform;
resultDevice.ProcessorCount = device.ProcessorCount;
resultDevice.PublicIP = device.PublicIP;
resultDevice.TotalMemory = device.TotalMemory;
resultDevice.TotalStorage = device.TotalStorage;
resultDevice.AgentVersion = device.AgentVersion;
resultDevice.LastOnline = DateTimeOffset.Now;
}
else
{
device.LastOnline = DateTimeOffset.Now;
if (_hostEnvironment.IsDevelopment() && dbContext.Organizations.Any())
{
var org = dbContext.Organizations.FirstOrDefault();
var org = await dbContext.Organizations.FirstOrDefaultAsync();
device.Organization = org;
device.OrganizationID = org?.ID;
}
updatedDevice = device;
resultDevice = device;
if (!dbContext.Organizations.Any(x => x.ID == device.OrganizationID))
if (!await dbContext.Organizations.AnyAsync(x => x.ID == device.OrganizationID))
{
_logger.LogInformation(
"Unable to add device {deviceName} because organization {organizationID}" +
@ -369,12 +369,12 @@ namespace Remotely.Server.Services
device.OrganizationID,
device.ID);
return false;
return Result.Fail<Device>("Organization does not exist.");
}
dbContext.Devices.Add(device);
await dbContext.Devices.AddAsync(device);
}
dbContext.SaveChanges();
return true;
await dbContext.SaveChangesAsync();
return Result.Ok(resultDevice);
}
public async Task AddOrUpdateSavedScript(SavedScript script, string userId)

View File

@ -1,4 +1,5 @@
using Castle.Core.Logging;
using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Logging;
using Moq;
@ -6,85 +7,93 @@ using Remotely.Agent.Services;
using Remotely.Agent.Services.Windows;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace Remotely.Tests.LoadTester
namespace Remotely.Tests.LoadTester;
internal class Program
{
internal class Program
private static readonly double _heartbeatMs = TimeSpan.FromMinutes(1).TotalMilliseconds;
private static int _agentCount;
private static string _organizationId;
private static string _serverurl;
private static Mock<ICpuUtilizationSampler> _cpuSampler;
private static Mock<ILogger<DeviceInfoGeneratorWin>> _logger;
private static DeviceInfoGeneratorWin _deviceInfo;
private static Stopwatch _stopwatch;
private static int _connectedCount;
private static void Main(string[] args)
{
private static readonly SemaphoreSlim _lock = new(10, 10);
private static readonly double _heartbeatMs = TimeSpan.FromMinutes(1).TotalMilliseconds;
private static int _agentCount;
private static string _organizationId;
private static string _serverurl;
private static Mock<ICpuUtilizationSampler> _cpuSampler;
private static Mock<ILogger<DeviceInfoGeneratorWin>> _logger;
private static DeviceInfoGeneratorWin _deviceInfo;
_cpuSampler = new Mock<ICpuUtilizationSampler>();
_cpuSampler.Setup(x => x.CurrentUtilization).Returns(0);
_logger = new Mock<ILogger<DeviceInfoGeneratorWin>>();
private static void Main(string[] args)
_deviceInfo = new DeviceInfoGeneratorWin(_cpuSampler.Object, _logger.Object);
_stopwatch = Stopwatch.StartNew();
ConnectAgents();
Console.Write("Press Enter to exit...");
Console.ReadLine();
}
private static void ConnectAgents()
{
try
{
_cpuSampler = new Mock<ICpuUtilizationSampler>();
_cpuSampler.Setup(x => x.CurrentUtilization).Returns(0);
_logger = new Mock<ILogger<DeviceInfoGeneratorWin>>();
_deviceInfo = new DeviceInfoGeneratorWin(_cpuSampler.Object, _logger.Object);
ConnectAgents();
Console.Write("Press Enter to exit...");
Console.ReadLine();
}
private static void ConnectAgents()
{
try
if (!CommandLineParser.CommandLineArgs.ContainsKey("serverurl") ||
!CommandLineParser.CommandLineArgs.ContainsKey("organizationid") ||
!CommandLineParser.CommandLineArgs.ContainsKey("agentcount"))
{
if (!CommandLineParser.CommandLineArgs.ContainsKey("serverurl") ||
!CommandLineParser.CommandLineArgs.ContainsKey("organizationid") ||
!CommandLineParser.CommandLineArgs.ContainsKey("agentcount"))
{
Console.WriteLine("Command line arguments must include all of the following: ");
Console.WriteLine();
Console.WriteLine("-serverurl [full URL of the Remotely server]");
Console.WriteLine();
Console.WriteLine("-organizationid [organization ID that the device will belong to]");
Console.WriteLine();
Console.WriteLine("-agentcount [the number of agent connections to simulate]");
Console.WriteLine();
Console.WriteLine("Press Enter to exit...");
Environment.Exit(0);
}
_agentCount = int.Parse(CommandLineParser.CommandLineArgs["agentcount"]);
_organizationId = CommandLineParser.CommandLineArgs["organizationid"];
_serverurl = CommandLineParser.CommandLineArgs["serverurl"];
for (var i = 0; i < _agentCount; i++)
{
_ = StartAgent(i);
}
}
catch (Exception ex)
{
Console.WriteLine("An error occurred. Check your syntex. Error: ");
Console.WriteLine("Command line arguments must include all of the following: ");
Console.WriteLine();
Console.WriteLine(ex.Message);
Console.WriteLine("-serverurl [full URL of the Remotely server]");
Console.WriteLine();
Console.WriteLine("-organizationid [organization ID that the device will belong to]");
Console.WriteLine();
Console.WriteLine("-agentcount [the number of agent connections to simulate]");
Console.WriteLine();
Console.WriteLine("Press Enter to exit...");
Environment.Exit(0);
}
_agentCount = int.Parse(CommandLineParser.CommandLineArgs["agentcount"]);
_organizationId = CommandLineParser.CommandLineArgs["organizationid"];
_serverurl = CommandLineParser.CommandLineArgs["serverurl"];
for (var i = 0; i < _agentCount; i++)
{
_ = StartAgent(i);
}
}
catch (Exception ex)
{
Console.WriteLine("An error occurred. Check your syntex. Error: ");
Console.WriteLine();
Console.WriteLine(ex.Message);
}
}
private static async Task StartAgent(int i)
private static async Task StartAgent(int i)
{
while (true)
{
try
{
await _lock.WaitAsync();
var waitSeconds = Math.Max(3, Random.Shared.NextDouble() * 10);
await Task.Delay(TimeSpan.FromSeconds(waitSeconds));
var deviceId = Guid.NewGuid().ToString();
var hubConnection = new HubConnectionBuilder()
.WithUrl(_serverurl + "/hubs/service")
.WithAutomaticReconnect(new RetryPolicy())
.Build();
Console.WriteLine("Connecting device number " + i.ToString());
Console.WriteLine($"Connecting device number {i}");
await hubConnection.StartAsync();
var device = await _deviceInfo.CreateDevice(deviceId, _organizationId);
@ -98,29 +107,47 @@ namespace Remotely.Tests.LoadTester
return;
}
_ = Task.Run(async () =>
{
await Task.Delay(new Random().Next(1, (int)_heartbeatMs));
var heartbeatTimer = new System.Timers.Timer(_heartbeatMs);
heartbeatTimer.Elapsed += async (sender, args) =>
{
var currentInfo = await _deviceInfo.CreateDevice(device.ID, _organizationId);
currentInfo.DeviceName = device.DeviceName;
await hubConnection.SendAsync("DeviceHeartbeat", currentInfo);
try
{
var currentInfo = await _deviceInfo.CreateDevice(device.ID, _organizationId);
currentInfo.DeviceName = device.DeviceName;
await hubConnection.SendAsync("DeviceHeartbeat", currentInfo);
}
catch { }
};
heartbeatTimer.Start();
});
Console.WriteLine($"Connected device number {i}");
Interlocked.Increment(ref _connectedCount);
if (_connectedCount == _agentCount)
{
Console.WriteLine($"Finished connecting all devices. Elapsed: {_stopwatch.Elapsed}");
}
break;
}
catch (Exception ex)
{
Console.WriteLine($"Device {i} failed to connect.");
Console.WriteLine(ex.Message);
}
finally
{
_lock.Release();
}
}
}
private class RetryPolicy : IRetryPolicy
{
public TimeSpan? NextRetryDelay(RetryContext retryContext)
{
return TimeSpan.FromSeconds(3);
}
}
}

View File

@ -29,7 +29,7 @@ namespace Remotely.Tests
}
[TestMethod]
public void AddOrUpdateDevice()
public async Task AddOrUpdateDevice()
{
var storedDevice = _dataService.GetDevice(_newDeviceID);
@ -43,7 +43,8 @@ namespace Remotely.Tests
Is64Bit = Environment.Is64BitOperatingSystem
};
Assert.IsTrue(_dataService.AddOrUpdateDevice(newDevice, out _));
var result = await _dataService.AddOrUpdateDevice(newDevice);
Assert.IsTrue(result.IsSuccess);
storedDevice = _dataService.GetDevice(_newDeviceID);

View File

@ -94,9 +94,9 @@ namespace Remotely.Tests
User2 = dataService.GetUserByNameWithOrg("testuser2@test.com");
Device1.OrganizationID = Admin1.OrganizationID;
dataService.AddOrUpdateDevice(Device1, out _);
await dataService.AddOrUpdateDevice(Device1); ;
Device2.OrganizationID = Admin1.OrganizationID;
dataService.AddOrUpdateDevice(Device2, out _);
await dataService.AddOrUpdateDevice(Device2);
dataService.AddDeviceGroup(Admin1.OrganizationID, Group1, out _, out _);
dataService.AddDeviceGroup(Admin1.OrganizationID, Group2, out _, out _);