diff options
Diffstat (limited to 'ModAS.Server/Services/RoomStateCacheService.cs')
-rw-r--r-- | ModAS.Server/Services/RoomStateCacheService.cs | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/ModAS.Server/Services/RoomStateCacheService.cs b/ModAS.Server/Services/RoomStateCacheService.cs new file mode 100644 index 0000000..3fdf29a --- /dev/null +++ b/ModAS.Server/Services/RoomStateCacheService.cs @@ -0,0 +1,66 @@ +using System.Collections.Frozen; +using ArcaneLibs.Extensions; +using Elastic.Apm; +using Elastic.Apm.Api; +using LibMatrix; +using LibMatrix.RoomTypes; + +namespace ModAS.Server.Services; + +public class RoomStateCacheService(RoomContextService roomContextService) { + public FrozenDictionary<string, FrozenSet<StateEventResponse>> RoomStateCache { get; private set; } = FrozenDictionary<string, FrozenSet<StateEventResponse>>.Empty; + private SemaphoreSlim updateLock = new(1, 1); + public async Task<FrozenSet<StateEventResponse>> GetRoomState(string roomId, GenericRoom? roomReference = null) { + if (RoomStateCache.TryGetValue(roomId, out var roomState)) return roomState; + return await InvalidateRoomState(roomId, roomReference); + } + + public async Task<FrozenSet<StateEventResponse>> InvalidateRoomState(string roomId, GenericRoom? roomReference = null) { + var invalidateSpan = currentTransaction.StartSpan($"invalidateRoomState - {roomId}", ApiConstants.TypeApp); + var getRoomReferenceSpan = currentTransaction.StartSpan($"getRoomReference - {roomId}", ApiConstants.TypeApp); + if (roomReference is null) { + var rc = await roomContextService.GetRoomContext(roomId); + if (rc is null) return FrozenSet<StateEventResponse>.Empty; + roomReference = await roomContextService.GetRoomReferenceById(roomId); + } + + if (roomReference is null) { + currentTransaction.CaptureException(new Exception("Could not get room reference for room state invalidation"), roomId, true); + return FrozenSet<StateEventResponse>.Empty; + } + + getRoomReferenceSpan.End(); + + var updateSpan = currentTransaction.StartSpan($"updateRoomState - {roomId}", ApiConstants.TypeApp); + await updateLock.WaitAsync(); + var unfrozen = RoomStateCache.ToDictionary(); + unfrozen[roomId] = (await roomReference.GetFullStateAsListAsync()).ToFrozenSet(); + RoomStateCache = unfrozen.ToFrozenDictionary(); + updateSpan.End(); + updateLock.Release(); + + invalidateSpan.End(); + if (!RoomStateCache.ContainsKey(roomId)) { + currentTransaction.CaptureException(new Exception("Room state cache does not contain room after invalidation"), roomId, false); + if (!unfrozen.ContainsKey(roomId)) + currentTransaction.CaptureException(new Exception("Unfrozen room state cache does not contain room after invalidation either..."), roomId, false); + } + + return RoomStateCache[roomId]; + } + + public async Task EnsureCachedFromRoomList(IEnumerable<GenericRoom> rooms) { + await updateLock.WaitAsync(); + var unfrozen = RoomStateCache.ToDictionary(); + + var tasks = rooms.Select(async room => { + if (RoomStateCache.ContainsKey(room.RoomId)) return; + unfrozen[room.RoomId] = (await room.GetFullStateAsListAsync()).ToFrozenSet(); + }).ToList(); + await Task.WhenAll(tasks); + RoomStateCache = unfrozen.ToFrozenDictionary(); + updateLock.Release(); + } + + private static ITransaction currentTransaction => Agent.Tracer.CurrentTransaction; +} \ No newline at end of file |