From f958ba5ddd9a2ddf7c805ffe47525e4e1e93f884 Mon Sep 17 00:00:00 2001 From: henry Date: Fri, 4 Jul 2025 12:07:22 -0400 Subject: [PATCH] Modify repo to use concurrent dictionaries for thread safety during backup --- .../Data/TakeANumberAccessQueueRepo.cs | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/AccessQueueService/Data/TakeANumberAccessQueueRepo.cs b/AccessQueueService/Data/TakeANumberAccessQueueRepo.cs index ad45a83..1cf3b1b 100644 --- a/AccessQueueService/Data/TakeANumberAccessQueueRepo.cs +++ b/AccessQueueService/Data/TakeANumberAccessQueueRepo.cs @@ -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 _accessTickets = []; - private Dictionary _queueNumbers = []; - private Dictionary _accessQueue = []; + private ConcurrentDictionary _accessTickets = new(); + private ConcurrentDictionary _queueNumbers = new(); + private ConcurrentDictionary _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(); - var newQueueNumbers = new Dictionary(); + var newQueue = new ConcurrentDictionary(); + var newQueueNumbers = new ConcurrentDictionary(); 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(_accessTickets), + AccessQueue = new Dictionary(_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(state.AccessTickets); + var _accessQueue = new ConcurrentDictionary(state.AccessQueue); var _nextUnusedTicket = 0ul; var _nowServing = ulong.MaxValue; - Dictionary _queueNumbers = []; + var _queueNumbers = new ConcurrentDictionary(); 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); }