From 89f15c99f01a2a803cccf0f6c6915f5d6ba1e371 Mon Sep 17 00:00:00 2001 From: Collin Barrett Date: Mon, 10 Jun 2024 09:57:56 -0500 Subject: [PATCH] =?UTF-8?q?feat(dir):=20=E2=9C=A8=E2=9A=A1=EF=B8=8F=20add?= =?UTF-8?q?=20in-memory=20caching?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Queries/GetLanguages.cs | 13 +++++--- .../Queries/GetLicenses.cs | 10 +++++-- .../Queries/GetListDetails.cs | 5 ++-- .../Queries/GetLists.cs | 10 +++++-- .../Queries/GetMaintainers.cs | 13 +++++--- .../Queries/GetSoftware.cs | 13 +++++--- .../Queries/GetSyntaxes.cs | 13 +++++--- .../Queries/GetTags.cs | 13 +++++--- .../ConfigurationExtensions.cs | 2 +- .../MemoryCacheExtension.cs | 30 +++++++++++++++++++ 10 files changed, 95 insertions(+), 27 deletions(-) create mode 100644 services/Directory/FilterLists.Directory.Infrastructure/MemoryCacheExtension.cs diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetLanguages.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetLanguages.cs index ecfef00fd..8e2ad52b6 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetLanguages.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetLanguages.cs @@ -1,8 +1,10 @@ using System.Runtime.CompilerServices; +using FilterLists.Directory.Infrastructure; using FilterLists.Directory.Infrastructure.Persistence.Queries.Context; using JetBrains.Annotations; using MediatR; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; namespace FilterLists.Directory.Application.Queries; @@ -27,12 +29,15 @@ public static class GetLanguages public sealed record Request : IStreamRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler + private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IStreamRequestHandler { - public async IAsyncEnumerable Handle(Request request, - [EnumeratorCancellation] CancellationToken ct) + public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) { - await foreach (var language in Query(ctx).WithCancellation(ct)) yield return language; + await foreach (var language in cache.GetOrCreateAsyncEnumerable( + nameof(GetLanguages), + Query(ctx).WithCancellation(ct)) + .WithCancellation(ct)) + yield return language; } } diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetLicenses.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetLicenses.cs index 5d4c9ad50..15d49deff 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetLicenses.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetLicenses.cs @@ -1,8 +1,10 @@ using System.Runtime.CompilerServices; +using FilterLists.Directory.Infrastructure; using FilterLists.Directory.Infrastructure.Persistence.Queries.Context; using JetBrains.Annotations; using MediatR; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; namespace FilterLists.Directory.Application.Queries; @@ -30,11 +32,15 @@ public static class GetLicenses public sealed record Request : IStreamRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler + private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IStreamRequestHandler { public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) { - await foreach (var license in Query(ctx).WithCancellation(ct)) yield return license; + await foreach (var license in cache.GetOrCreateAsyncEnumerable( + nameof(GetLicenses), + Query(ctx).WithCancellation(ct)) + .WithCancellation(ct)) + yield return license; } } diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetListDetails.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetListDetails.cs index 3c20fcd29..5b0a42aa5 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetListDetails.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetListDetails.cs @@ -2,6 +2,7 @@ using JetBrains.Annotations; using MediatR; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; namespace FilterLists.Directory.Application.Queries; @@ -71,11 +72,11 @@ public static class GetListDetails public sealed record Request(int Id) : IRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IRequestHandler + private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IRequestHandler { public Task Handle(Request request, CancellationToken _) { - return Query(ctx, request.Id); + return cache.GetOrCreateAsync($"{nameof(GetListDetails)}_{request.Id}", entry => Query(ctx, request.Id)); } } diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetLists.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetLists.cs index 303e27f76..39ac6155c 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetLists.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetLists.cs @@ -1,8 +1,10 @@ using System.Runtime.CompilerServices; +using FilterLists.Directory.Infrastructure; using FilterLists.Directory.Infrastructure.Persistence.Queries.Context; using JetBrains.Annotations; using MediatR; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; namespace FilterLists.Directory.Application.Queries; @@ -42,11 +44,15 @@ public static class GetLists public sealed record Request : IStreamRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler + private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IStreamRequestHandler { public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) { - await foreach (var list in Query(ctx).WithCancellation(ct)) yield return list; + await foreach (var list in cache.GetOrCreateAsyncEnumerable( + nameof(GetLists), + Query(ctx).WithCancellation(ct)) + .WithCancellation(ct)) + yield return list; } } diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetMaintainers.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetMaintainers.cs index df7be0b43..9ad2b79cb 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetMaintainers.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetMaintainers.cs @@ -1,8 +1,10 @@ using System.Runtime.CompilerServices; +using FilterLists.Directory.Infrastructure; using FilterLists.Directory.Infrastructure.Persistence.Queries.Context; using JetBrains.Annotations; using MediatR; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; namespace FilterLists.Directory.Application.Queries; @@ -29,12 +31,15 @@ public static class GetMaintainers public sealed record Request : IStreamRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler + private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IStreamRequestHandler { - public async IAsyncEnumerable Handle(Request request, - [EnumeratorCancellation] CancellationToken ct) + public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) { - await foreach (var maintainer in Query(ctx).WithCancellation(ct)) yield return maintainer; + await foreach (var maintainer in cache.GetOrCreateAsyncEnumerable( + nameof(GetMaintainers), + Query(ctx).WithCancellation(ct)) + .WithCancellation(ct)) + yield return maintainer; } } diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetSoftware.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetSoftware.cs index 30121f1b8..72b87477e 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetSoftware.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetSoftware.cs @@ -1,8 +1,10 @@ using System.Runtime.CompilerServices; +using FilterLists.Directory.Infrastructure; using FilterLists.Directory.Infrastructure.Persistence.Queries.Context; using JetBrains.Annotations; using MediatR; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; namespace FilterLists.Directory.Application.Queries; @@ -32,12 +34,15 @@ public static class GetSoftware public sealed record Request : IStreamRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler + private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IStreamRequestHandler { - public async IAsyncEnumerable Handle(Request request, - [EnumeratorCancellation] CancellationToken ct) + public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) { - await foreach (var software in Query(ctx).WithCancellation(ct)) yield return software; + await foreach (var software in cache.GetOrCreateAsyncEnumerable( + nameof(GetSoftware), + Query(ctx).WithCancellation(ct)) + .WithCancellation(ct)) + yield return software; } } diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetSyntaxes.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetSyntaxes.cs index 48aac9731..ca40e397d 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetSyntaxes.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetSyntaxes.cs @@ -1,8 +1,10 @@ using System.Runtime.CompilerServices; +using FilterLists.Directory.Infrastructure; using FilterLists.Directory.Infrastructure.Persistence.Queries.Context; using JetBrains.Annotations; using MediatR; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; namespace FilterLists.Directory.Application.Queries; @@ -32,12 +34,15 @@ public static class GetSyntaxes public sealed record Request : IStreamRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler + private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IStreamRequestHandler { - public async IAsyncEnumerable Handle(Request request, - [EnumeratorCancellation] CancellationToken ct) + public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) { - await foreach (var syntax in Query(ctx).WithCancellation(ct)) yield return syntax; + await foreach (var syntax in cache.GetOrCreateAsyncEnumerable( + nameof(GetSyntaxes), + Query(ctx).WithCancellation(ct)) + .WithCancellation(ct)) + yield return syntax; } } diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetTags.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetTags.cs index a20f6d660..ef8e69c50 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetTags.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetTags.cs @@ -1,8 +1,10 @@ using System.Runtime.CompilerServices; +using FilterLists.Directory.Infrastructure; using FilterLists.Directory.Infrastructure.Persistence.Queries.Context; using JetBrains.Annotations; using MediatR; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; namespace FilterLists.Directory.Application.Queries; @@ -28,12 +30,15 @@ public static class GetTags public sealed record Request : IStreamRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler + private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IStreamRequestHandler { - public async IAsyncEnumerable Handle(Request request, - [EnumeratorCancellation] CancellationToken ct) + public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) { - await foreach (var tag in Query(ctx).WithCancellation(ct)) yield return tag; + await foreach (var tag in cache.GetOrCreateAsyncEnumerable( + nameof(GetTags), + Query(ctx).WithCancellation(ct)) + .WithCancellation(ct)) + yield return tag; } } diff --git a/services/Directory/FilterLists.Directory.Infrastructure/ConfigurationExtensions.cs b/services/Directory/FilterLists.Directory.Infrastructure/ConfigurationExtensions.cs index 0e31648d5..a3f86d2f3 100644 --- a/services/Directory/FilterLists.Directory.Infrastructure/ConfigurationExtensions.cs +++ b/services/Directory/FilterLists.Directory.Infrastructure/ConfigurationExtensions.cs @@ -20,7 +20,7 @@ public static void AddInfrastructure(this IHostApplicationBuilder builder) so.EnableRetryOnFailure([0]) .MigrationsAssembly(MigrationsAssembly)) .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)); - builder.Services.AddHostedService(); + builder.Services.AddMemoryCache(); } } \ No newline at end of file diff --git a/services/Directory/FilterLists.Directory.Infrastructure/MemoryCacheExtension.cs b/services/Directory/FilterLists.Directory.Infrastructure/MemoryCacheExtension.cs new file mode 100644 index 000000000..4527aff1c --- /dev/null +++ b/services/Directory/FilterLists.Directory.Infrastructure/MemoryCacheExtension.cs @@ -0,0 +1,30 @@ +using System.Runtime.CompilerServices; +using Microsoft.Extensions.Caching.Memory; + +namespace FilterLists.Directory.Infrastructure; + +public static class MemoryCacheExtension +{ + public static async IAsyncEnumerable GetOrCreateAsyncEnumerable( + this IMemoryCache cache, + object key, + ConfiguredCancelableAsyncEnumerable query, + MemoryCacheEntryOptions? options = null) where TResponse : class + { + if (cache.TryGetValue(key, out List? cachedElements)) + { + foreach (var element in cachedElements ?? []) yield return element; + } + else + { + var elementsToCache = new List(); + await foreach (var element in query) + { + elementsToCache.Add(element); + yield return element; + } + + cache.Set(key, elementsToCache, options); + } + } +} \ No newline at end of file