From d2f21d5ca23c27ab05a1817915390ffff7e4ce7c Mon Sep 17 00:00:00 2001 From: henry Date: Wed, 2 Jul 2025 00:22:34 -0400 Subject: [PATCH 1/2] added first draft at method to re-organize queue --- .../Data/TakeANumberAccessQueueRepo.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/AccessQueueService/Data/TakeANumberAccessQueueRepo.cs b/AccessQueueService/Data/TakeANumberAccessQueueRepo.cs index f05c660..5b35204 100644 --- a/AccessQueueService/Data/TakeANumberAccessQueueRepo.cs +++ b/AccessQueueService/Data/TakeANumberAccessQueueRepo.cs @@ -104,5 +104,20 @@ namespace AccessQueueService.Data } return _accessTickets.Remove(userId); } + + public void Optimize() + { + DeleteExpiredTickets(); + + var newQueue = new Dictionary(); + var newQueueNumbers = new Dictionary(); + ulong newIndex = 0; + for (ulong i = _nowServing; i < _nextUnusedTicket; i++) + { + var user = _accessQueue[i]; + newQueue[newIndex] = user; + newQueueNumbers[user.UserId] = newIndex; + } + } } } -- 2.40.1 From fa749689f3d974f4f72ce1a57cc8086b04c77ca1 Mon Sep 17 00:00:00 2001 From: henry Date: Thu, 3 Jul 2025 01:25:54 -0400 Subject: [PATCH 2/2] Finalized optimization method, added trigger and tests --- AccessQueueService/AccessQueueService.csproj | 28 +++++++----- .../Data/TakeANumberAccessQueueRepo.cs | 23 ++++++---- .../AccessQueueRepoTests.cs | 45 ++++++++++++++++++- 3 files changed, 75 insertions(+), 21 deletions(-) diff --git a/AccessQueueService/AccessQueueService.csproj b/AccessQueueService/AccessQueueService.csproj index 9c50d6d..dac5a84 100644 --- a/AccessQueueService/AccessQueueService.csproj +++ b/AccessQueueService/AccessQueueService.csproj @@ -1,17 +1,21 @@ - - net8.0 - enable - enable - + + net8.0 + enable + enable + - - - - - - - + + + + + + + + + + + diff --git a/AccessQueueService/Data/TakeANumberAccessQueueRepo.cs b/AccessQueueService/Data/TakeANumberAccessQueueRepo.cs index 5b35204..7289845 100644 --- a/AccessQueueService/Data/TakeANumberAccessQueueRepo.cs +++ b/AccessQueueService/Data/TakeANumberAccessQueueRepo.cs @@ -6,11 +6,11 @@ namespace AccessQueueService.Data public class TakeANumberAccessQueueRepo : IAccessQueueRepo { private readonly Dictionary _accessTickets = []; - private readonly Dictionary _queueNumbers = []; - private readonly Dictionary _accessQueue = []; + private Dictionary _queueNumbers = []; + private Dictionary _accessQueue = []; - private ulong _nowServing = 0; - private ulong _nextUnusedTicket = 0; + internal ulong _nowServing = 0; + internal ulong _nextUnusedTicket = 0; public int GetUnexpiredTicketsCount() => _accessTickets.Count(t => t.Value.ExpiresOn > DateTime.UtcNow); public int GetActiveTicketsCount(DateTime activeCutoff) => _accessTickets @@ -32,6 +32,11 @@ namespace AccessQueueService.Data public void Enqueue(AccessTicket ticket) { + if(_nextUnusedTicket >= long.MaxValue) + { + // Prevent overflow + Optimize(); + } _queueNumbers[ticket.UserId] = _nextUnusedTicket; _accessQueue[_nextUnusedTicket] = ticket; _nextUnusedTicket++; @@ -105,10 +110,8 @@ namespace AccessQueueService.Data return _accessTickets.Remove(userId); } - public void Optimize() + internal void Optimize() { - DeleteExpiredTickets(); - var newQueue = new Dictionary(); var newQueueNumbers = new Dictionary(); ulong newIndex = 0; @@ -116,8 +119,12 @@ namespace AccessQueueService.Data { var user = _accessQueue[i]; newQueue[newIndex] = user; - newQueueNumbers[user.UserId] = newIndex; + newQueueNumbers[user.UserId] = newIndex++; } + _accessQueue = newQueue; + _queueNumbers = newQueueNumbers; + _nowServing = 0; + _nextUnusedTicket = newIndex; } } } diff --git a/AccessQueueServiceTests/AccessQueueRepoTests.cs b/AccessQueueServiceTests/AccessQueueRepoTests.cs index a24fa1e..f80a9a3 100644 --- a/AccessQueueServiceTests/AccessQueueRepoTests.cs +++ b/AccessQueueServiceTests/AccessQueueRepoTests.cs @@ -22,7 +22,6 @@ namespace AccessQueueServiceTests [Fact] public void GetUnexpiredTicketsCount_ReturnsCorrectCount() { - _repo.UpsertTicket(new AccessTicket { UserId = "a", ExpiresOn = DateTime.UtcNow.AddMinutes(1), LastActive = DateTime.UtcNow }); _repo.UpsertTicket(new AccessTicket { UserId = "b", ExpiresOn = DateTime.UtcNow.AddMinutes(-1), LastActive = DateTime.UtcNow }); Assert.Equal(1, _repo.GetUnexpiredTicketsCount()); @@ -192,5 +191,49 @@ namespace AccessQueueServiceTests Assert.Null(_repo.GetTicket("second")); Assert.Null(_repo.GetTicket("third")); } + + [Fact] + public void Optimize_MaintainsQueueOrder() + { + var ticket1 = new AccessTicket { UserId = "first", ExpiresOn = DateTime.UtcNow.AddMinutes(1), LastActive = DateTime.UtcNow }; + var ticket2 = new AccessTicket { UserId = "second", ExpiresOn = DateTime.UtcNow.AddMinutes(1), LastActive = DateTime.UtcNow }; + var ticket3 = new AccessTicket { UserId = "third", ExpiresOn = DateTime.UtcNow.AddMinutes(1), LastActive = DateTime.UtcNow }; + _repo.Enqueue(ticket1); + _repo.Enqueue(ticket2); + _repo.Enqueue(ticket3); + + _repo.DidDequeueUntilFull(60 * 60, 60, 1); + _repo.Optimize(); + + Assert.NotNull(_repo.GetTicket("first")); + Assert.Equal(0, _repo.GetRequestsAhead("second")); + Assert.Equal(1, _repo.GetRequestsAhead("third")); + Assert.Equal(0ul, _repo._nowServing); + + _repo.DidDequeueUntilFull(60 * 60, 60, 2); + Assert.NotNull(_repo.GetTicket("second")); + Assert.Equal(0, _repo.GetRequestsAhead("third")); + } + + [Fact] + public void Enqueue_MaintainsQueueOrder_WhenMaxValueExceeded() + { + var ticket1 = new AccessTicket { UserId = "first", ExpiresOn = DateTime.UtcNow.AddMinutes(1), LastActive = DateTime.UtcNow }; + var ticket2 = new AccessTicket { UserId = "second", ExpiresOn = DateTime.UtcNow.AddMinutes(1), LastActive = DateTime.UtcNow }; + var ticket3 = new AccessTicket { UserId = "third", ExpiresOn = DateTime.UtcNow.AddMinutes(1), LastActive = DateTime.UtcNow }; + + _repo._nowServing = long.MaxValue - 1; + _repo._nextUnusedTicket = long.MaxValue - 1; + + _repo.Enqueue(ticket1); + _repo.Enqueue(ticket2); + _repo.Enqueue(ticket3); + + Assert.Equal(0ul, _repo._nowServing); + Assert.Equal(3ul, _repo._nextUnusedTicket); + + Assert.Equal(0, _repo.GetRequestsAhead("first")); + Assert.Equal(2, _repo.GetRequestsAhead("third")); + } } } -- 2.40.1