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"));
+ }
}
}