From 96bfc6cbb6dda5993f5bce85b6f271edc32a8a23 Mon Sep 17 00:00:00 2001 From: Simon <63975668+Simyon264@users.noreply.github.com> Date: Mon, 24 Jun 2024 19:40:07 +0200 Subject: [PATCH] Add cache admin stats and move some shit internally --- .../Helpers/MemoryCacheExtensions.cs | 59 +++++++++++++ .../Pages/{Account => Admin}/Admin.razor | 0 ReplayBrowser/Pages/Admin/AdminStats.razor | 87 +++++++++++++++++++ .../Pages/{Account => Admin}/Logs.razor | 0 .../Pages/{Account => Admin}/Notices.razor | 0 ReplayBrowser/Startup.cs | 9 +- 6 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 ReplayBrowser/Helpers/MemoryCacheExtensions.cs rename ReplayBrowser/Pages/{Account => Admin}/Admin.razor (100%) create mode 100644 ReplayBrowser/Pages/Admin/AdminStats.razor rename ReplayBrowser/Pages/{Account => Admin}/Logs.razor (100%) rename ReplayBrowser/Pages/{Account => Admin}/Notices.razor (100%) diff --git a/ReplayBrowser/Helpers/MemoryCacheExtensions.cs b/ReplayBrowser/Helpers/MemoryCacheExtensions.cs new file mode 100644 index 0000000..b4fbcf1 --- /dev/null +++ b/ReplayBrowser/Helpers/MemoryCacheExtensions.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using Microsoft.Extensions.Caching.Memory; + +namespace ReplayBrowser.Helpers; + +public static class MemoryCacheExtensions +{ +#region Microsoft.Extensions.Caching.Memory_6_OR_OLDER + + private static readonly Lazy> GetEntries6 = + new Lazy>(() => (Func)Delegate.CreateDelegate( + typeof(Func), + typeof(MemoryCache).GetProperty("EntriesCollection", BindingFlags.NonPublic | BindingFlags.Instance).GetGetMethod(true), + throwOnBindFailure: true)); + +#endregion + +#region Microsoft.Extensions.Caching.Memory_7_OR_NEWER + + private static readonly Lazy> GetCoherentState = + new Lazy>(() => + CreateGetter(typeof(MemoryCache) + .GetField("_coherentState", BindingFlags.NonPublic | BindingFlags.Instance))); + + private static readonly Lazy> GetEntries7 = + new Lazy>(() => + CreateGetter(typeof(MemoryCache) + .GetNestedType("CoherentState", BindingFlags.NonPublic) + .GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance))); + + private static Func CreateGetter(FieldInfo field) + { + var methodName = $"{field.ReflectedType.FullName}.get_{field.Name}"; + var method = new DynamicMethod(methodName, typeof(TReturn), new[] { typeof(TParam) }, typeof(TParam), true); + var ilGen = method.GetILGenerator(); + ilGen.Emit(OpCodes.Ldarg_0); + ilGen.Emit(OpCodes.Ldfld, field); + ilGen.Emit(OpCodes.Ret); + return (Func)method.CreateDelegate(typeof(Func)); + } + +#endregion + + private static readonly Func GetEntries = + Assembly.GetAssembly(typeof(MemoryCache)).GetName().Version.Major < 7 + ? (Func)(cache => (IDictionary)GetEntries6.Value(cache)) + : cache => GetEntries7.Value(GetCoherentState.Value(cache)); + + public static ICollection GetKeys(this IMemoryCache memoryCache) => + GetEntries((MemoryCache)memoryCache).Keys; + + public static IEnumerable GetKeys(this IMemoryCache memoryCache) => + memoryCache.GetKeys().OfType(); +} \ No newline at end of file diff --git a/ReplayBrowser/Pages/Account/Admin.razor b/ReplayBrowser/Pages/Admin/Admin.razor similarity index 100% rename from ReplayBrowser/Pages/Account/Admin.razor rename to ReplayBrowser/Pages/Admin/Admin.razor diff --git a/ReplayBrowser/Pages/Admin/AdminStats.razor b/ReplayBrowser/Pages/Admin/AdminStats.razor new file mode 100644 index 0000000..1dc9873 --- /dev/null +++ b/ReplayBrowser/Pages/Admin/AdminStats.razor @@ -0,0 +1,87 @@ +@page "/account/admin/stats" +@using System.Text.Json +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Authorization +@using Microsoft.Extensions.Caching.Memory +@using ReplayBrowser.Helpers +@using ReplayBrowser.Services +@using Microsoft.AspNetCore.Components.Web +@attribute [Authorize] +@inject IMemoryCache MemoryCache +@inject AuthenticationStateProvider AuthenticationStateProvider +@inject AccountService AccountService +@inject NavigationManager NavigationManager + + + + + +Cache Stats + +

AdminStats

+@if(!_authed) +{ + +} +else +{ + var stats = MemoryCache.GetCurrentStatistics(); + if (stats == null) + { + + } + else + { +
+
+

Total hits: @stats.TotalHits

+

Total misses: @stats.TotalMisses

+

Total entries: @stats.CurrentEntryCount

+

Estimated size: @stats.CurrentEstimatedSize

+
+
+ } + + var keys = MemoryCache.GetKeys(); + foreach (var key in keys) + { + var entry = MemoryCache.Get(key); + +
+
+

+ Key: @key
+ Value: +

@JsonSerializer.Serialize(entry, new JsonSerializerOptions() { WriteIndented = true })

+

+
+
+ } + + +} +@code { + private bool _authed = false; + + protected override async Task OnInitializedAsync() + { + var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + var user = await AccountService.GetAccount(authState); + + if (user == null || !user.IsAdmin) + { + NavigationManager.NavigateTo("/"); + return; + } + + _authed = true; + } +} \ No newline at end of file diff --git a/ReplayBrowser/Pages/Account/Logs.razor b/ReplayBrowser/Pages/Admin/Logs.razor similarity index 100% rename from ReplayBrowser/Pages/Account/Logs.razor rename to ReplayBrowser/Pages/Admin/Logs.razor diff --git a/ReplayBrowser/Pages/Account/Notices.razor b/ReplayBrowser/Pages/Admin/Notices.razor similarity index 100% rename from ReplayBrowser/Pages/Account/Notices.razor rename to ReplayBrowser/Pages/Admin/Notices.razor diff --git a/ReplayBrowser/Startup.cs b/ReplayBrowser/Startup.cs index 49bc31d..eca8f6b 100644 --- a/ReplayBrowser/Startup.cs +++ b/ReplayBrowser/Startup.cs @@ -1,8 +1,10 @@ using System.IdentityModel.Tokens.Jwt; using System.Net; +using System.Reflection; using System.Text.Json.Serialization; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using OpenTelemetry.Metrics; using ReplayBrowser.Data; @@ -27,6 +29,11 @@ public Startup(IConfiguration configuration) public void ConfigureServices(IServiceCollection services) { + services.AddMemoryCache(opts => + { + opts.TrackStatistics = true; + }); + services.AddControllersWithViews().AddJsonOptions(options => { options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; @@ -95,8 +102,6 @@ public void ConfigureServices(IServiceCollection services) }); }); - services.AddMemoryCache(); - // Endpoint logging services.Configure(options => {