Compare commits

...

1 Commits

Author SHA1 Message Date
henry b1b1b44e0f wip - trying to move expensive alculation to background 2025-05-16 21:21:07 -04:00
5 changed files with 71 additions and 7 deletions

View File

@ -26,6 +26,19 @@ builder.Services.AddHostedService<AccessQueueBackgroundService>();
var app = builder.Build(); var app = builder.Build();
// Configure TakeANumberAccessQueueRepo active users cache
using (var scope = app.Services.CreateScope())
{
var config = scope.ServiceProvider.GetRequiredService<IConfiguration>();
var repo = scope.ServiceProvider.GetRequiredService<IAccessQueueRepo>() as TakeANumberAccessQueueRepo;
if (repo != null)
{
int activeSeconds = config.GetValue<int>("AccessQueue:ActivitySeconds", 2);
int updateInterval = config.GetValue<int>("AccessQueue:ActiveUsersUpdateIntervalSeconds", 2);
repo.ConfigureActiveUsersCache(activeSeconds, updateInterval);
}
}
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment()) if (!app.Environment.IsDevelopment())
{ {

View File

@ -6,13 +6,14 @@
} }
}, },
"AccessQueue": { "AccessQueue": {
"CapacityLimit": 3, // Maximum number of active users "CapacityLimit": 3,
"ActivitySeconds": 2, // Time since last access before a user is considered inactive "ActivitySeconds": 2,
"ExpirationSeconds": 10, // 12 hours - Time before a user access is revoked "ExpirationSeconds": 10,
"RollingExpiration": true // Whether to extend expiration time on access "RollingExpiration": true,
"ActiveUsersUpdateIntervalSeconds": 2
}, },
"AccessQueuePlayground": { "AccessQueuePlayground": {
"RefreshRateMilliseconds": 200 // How often to re-request access and update the UI "RefreshRateMilliseconds": 200
}, },
"AllowedHosts": "*" "AllowedHosts": "*"
} }

View File

@ -1,5 +1,6 @@
using AccessQueueService.Models; using AccessQueueService.Models;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using System.Threading;
namespace AccessQueueService.Data namespace AccessQueueService.Data
{ {
@ -12,6 +13,12 @@ namespace AccessQueueService.Data
private ulong _nowServing = 0; private ulong _nowServing = 0;
private ulong _nextUnusedTicket = 0; private ulong _nextUnusedTicket = 0;
private int _cachedActiveUsers = 0;
private int _activeSeconds = 60; // default, can be set from config
private Timer? _activeUsersUpdateTimer;
private int _activeUsersUpdateIntervalSeconds = 10; // default, can be set from config
private readonly object _activeUsersLock = new();
public int GetUnexpiredTicketsCount() => _accessTickets.Count(t => t.Value.ExpiresOn > DateTime.UtcNow); public int GetUnexpiredTicketsCount() => _accessTickets.Count(t => t.Value.ExpiresOn > DateTime.UtcNow);
public int GetActiveTicketsCount(DateTime activeCutoff) => _accessTickets public int GetActiveTicketsCount(DateTime activeCutoff) => _accessTickets
.Count(t => t.Value.ExpiresOn > DateTime.UtcNow && t.Value.LastActive > activeCutoff); .Count(t => t.Value.ExpiresOn > DateTime.UtcNow && t.Value.LastActive > activeCutoff);
@ -54,7 +61,8 @@ namespace AccessQueueService.Data
{ {
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
var activeCutoff = now.AddSeconds(-activeSeconds); var activeCutoff = now.AddSeconds(-activeSeconds);
var numberOfActiveUsers = _accessTickets.Count(t => t.Value.ExpiresOn > now && t.Value.LastActive > activeCutoff); // Use cached value instead of recalculating
var numberOfActiveUsers = GetCachedActiveUsers();
var openSpots = capacityLimit - numberOfActiveUsers; var openSpots = capacityLimit - numberOfActiveUsers;
if(openSpots <= 0) if(openSpots <= 0)
{ {
@ -104,5 +112,33 @@ namespace AccessQueueService.Data
} }
return _accessTickets.Remove(userId); return _accessTickets.Remove(userId);
} }
public void ConfigureActiveUsersCache(int activeSeconds, int updateIntervalSeconds)
{
_activeSeconds = activeSeconds;
_activeUsersUpdateIntervalSeconds = updateIntervalSeconds;
_activeUsersUpdateTimer?.Dispose();
_activeUsersUpdateTimer = new Timer(_ => UpdateActiveUsersCache(), null, 0, _activeUsersUpdateIntervalSeconds * 1000);
}
private void UpdateActiveUsersCache()
{
var now = DateTime.UtcNow;
var activeCutoff = now.AddSeconds(-_activeSeconds);
int count = 0;
lock (_activeUsersLock)
{
count = _accessTickets.Count(t => t.Value.ExpiresOn > now && t.Value.LastActive > activeCutoff);
_cachedActiveUsers = count;
}
}
public int GetCachedActiveUsers()
{
lock (_activeUsersLock)
{
return _cachedActiveUsers;
}
}
} }
} }

View File

@ -22,6 +22,19 @@ builder.Services.AddHostedService<AccessCleanupBackgroundService>();
var app = builder.Build(); var app = builder.Build();
// Configure TakeANumberAccessQueueRepo active users cache
using (var scope = app.Services.CreateScope())
{
var config = scope.ServiceProvider.GetRequiredService<IConfiguration>();
var repo = scope.ServiceProvider.GetRequiredService<IAccessQueueRepo>() as TakeANumberAccessQueueRepo;
if (repo != null)
{
int activeSeconds = config.GetValue<int>("AccessQueue:ActivitySeconds", 900);
int updateInterval = config.GetValue<int>("AccessQueue:ActiveUsersUpdateIntervalSeconds", 10);
repo.ConfigureActiveUsersCache(activeSeconds, updateInterval);
}
}
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {
app.UseSwagger(); app.UseSwagger();

View File

@ -28,7 +28,8 @@
"ActivitySeconds": 900, "ActivitySeconds": 900,
"ExpirationSeconds": 43200, "ExpirationSeconds": 43200,
"RollingExpiration": true, "RollingExpiration": true,
"CleanupIntervalSeconds": 60 "CleanupIntervalSeconds": 60,
"ActiveUsersUpdateIntervalSeconds": 10
}, },
"AllowedHosts": "*" "AllowedHosts": "*"
} }