Modify repo to use concurrent dictionaries for thread safety during backup

This commit is contained in:
henry 2025-07-04 12:07:22 -04:00
parent e8a7cc588f
commit f958ba5ddd
1 changed files with 26 additions and 25 deletions

View File

@ -1,4 +1,5 @@
using System.Runtime.Serialization;
using System.Collections.Concurrent;
using System.Runtime.Serialization;
using System.Text.Json;
using AccessQueueService.Models;
using Microsoft.Extensions.Configuration;
@ -7,9 +8,9 @@ namespace AccessQueueService.Data
{
public class TakeANumberAccessQueueRepo : IAccessQueueRepo
{
private Dictionary<string, AccessTicket> _accessTickets = [];
private Dictionary<string, ulong> _queueNumbers = [];
private Dictionary<ulong, AccessTicket> _accessQueue = [];
private ConcurrentDictionary<string, AccessTicket> _accessTickets = new();
private ConcurrentDictionary<string, ulong> _queueNumbers = new();
private ConcurrentDictionary<ulong, AccessTicket> _accessQueue = new();
internal ulong _nowServing = 0;
internal ulong _nextUnusedTicket = 0;
@ -47,12 +48,12 @@ namespace AccessQueueService.Data
public int DeleteExpiredTickets()
{
var cutoff = DateTime.UtcNow;
var expiredTickets = _accessTickets.Where(t => t.Value.ExpiresOn < cutoff);
var expiredTickets = _accessTickets.Where(t => t.Value.ExpiresOn < cutoff).ToList();
int count = 0;
foreach (var ticket in expiredTickets)
{
count++;
_accessTickets.Remove(ticket.Key);
_accessTickets.TryRemove(ticket.Key, out _);
}
return count;
}
@ -70,13 +71,13 @@ namespace AccessQueueService.Data
int filledSpots = 0;
while (filledSpots < openSpots && _nowServing < _nextUnusedTicket)
{
if (_accessQueue.TryGetValue(_nowServing, out var nextUser))
if (_accessQueue.TryRemove(_nowServing, out var nextUser))
{
_accessQueue.Remove(_nowServing);
_queueNumbers.Remove(nextUser.UserId);
_queueNumbers.TryRemove(nextUser.UserId, out _);
if (nextUser.LastActive < activeCutoff)
{
// User is inactive, throw away their ticket
_nowServing++;
continue;
}
_accessTickets[nextUser.UserId] = new AccessTicket
@ -104,24 +105,25 @@ namespace AccessQueueService.Data
public bool RemoveUser(string userId)
{
if (_queueNumbers.TryGetValue(userId, out var queueNumber))
if (_queueNumbers.TryRemove(userId, out var queueNumber))
{
_accessQueue.Remove(queueNumber);
_queueNumbers.Remove(userId);
_accessQueue.TryRemove(queueNumber, out _);
}
return _accessTickets.Remove(userId);
return _accessTickets.TryRemove(userId, out _);
}
internal void Optimize()
{
var newQueue = new Dictionary<ulong, AccessTicket>();
var newQueueNumbers = new Dictionary<string, ulong>();
var newQueue = new ConcurrentDictionary<ulong, AccessTicket>();
var newQueueNumbers = new ConcurrentDictionary<string, ulong>();
ulong newIndex = 0;
for (ulong i = _nowServing; i < _nextUnusedTicket; i++)
{
var user = _accessQueue[i];
newQueue[newIndex] = user;
newQueueNumbers[user.UserId] = newIndex++;
if (_accessQueue.TryGetValue(i, out var user))
{
newQueue[newIndex] = user;
newQueueNumbers[user.UserId] = newIndex++;
}
}
_accessQueue = newQueue;
_queueNumbers = newQueueNumbers;
@ -133,10 +135,9 @@ namespace AccessQueueService.Data
{
var state = new TakeANumberAccessQueueRepoState
{
AccessTickets = _accessTickets,
AccessQueue = _accessQueue,
AccessTickets = new Dictionary<string, AccessTicket>(_accessTickets),
AccessQueue = new Dictionary<ulong, AccessTicket>(_accessQueue),
};
return JsonSerializer.Serialize(state);
}
@ -148,14 +149,14 @@ namespace AccessQueueService.Data
return new();
}
var _accessTickets = state.AccessTickets;
var _accessQueue = state.AccessQueue;
var _accessTickets = new ConcurrentDictionary<string, AccessTicket>(state.AccessTickets);
var _accessQueue = new ConcurrentDictionary<ulong, AccessTicket>(state.AccessQueue);
var _nextUnusedTicket = 0ul;
var _nowServing = ulong.MaxValue;
Dictionary<string, ulong> _queueNumbers = [];
var _queueNumbers = new ConcurrentDictionary<string, ulong>();
foreach (var queueItem in state.AccessQueue)
{
_queueNumbers.Add(queueItem.Value.UserId, queueItem.Key);
_queueNumbers[queueItem.Value.UserId] = queueItem.Key;
_nextUnusedTicket = Math.Max(_nextUnusedTicket, queueItem.Key + 1);
_nowServing = Math.Min(_nowServing, queueItem.Key);
}