feat(dir): ️ add in-memory caching

This commit is contained in:
Collin Barrett 2024-06-10 09:57:56 -05:00
parent 2cede3e539
commit 89f15c99f0
10 changed files with 95 additions and 27 deletions

View file

@ -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<Response>;
[UsedImplicitly]
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, Response>
private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IStreamRequestHandler<Request, Response>
{
public async IAsyncEnumerable<Response> Handle(Request request,
[EnumeratorCancellation] CancellationToken ct)
public async IAsyncEnumerable<Response> 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;
}
}

View file

@ -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<Response>;
[UsedImplicitly]
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, Response>
private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IStreamRequestHandler<Request, Response>
{
public async IAsyncEnumerable<Response> 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;
}
}

View file

@ -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<Response?>;
[UsedImplicitly]
private sealed class Handler(QueryDbContext ctx) : IRequestHandler<Request, Response?>
private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IRequestHandler<Request, Response?>
{
public Task<Response?> Handle(Request request, CancellationToken _)
{
return Query(ctx, request.Id);
return cache.GetOrCreateAsync($"{nameof(GetListDetails)}_{request.Id}", entry => Query(ctx, request.Id));
}
}

View file

@ -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<Response>;
[UsedImplicitly]
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, Response>
private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IStreamRequestHandler<Request, Response>
{
public async IAsyncEnumerable<Response> 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;
}
}

View file

@ -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<Response>;
[UsedImplicitly]
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, Response>
private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IStreamRequestHandler<Request, Response>
{
public async IAsyncEnumerable<Response> Handle(Request request,
[EnumeratorCancellation] CancellationToken ct)
public async IAsyncEnumerable<Response> 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;
}
}

View file

@ -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<Response>;
[UsedImplicitly]
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, Response>
private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IStreamRequestHandler<Request, Response>
{
public async IAsyncEnumerable<Response> Handle(Request request,
[EnumeratorCancellation] CancellationToken ct)
public async IAsyncEnumerable<Response> 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;
}
}

View file

@ -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<Response>;
[UsedImplicitly]
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, Response>
private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IStreamRequestHandler<Request, Response>
{
public async IAsyncEnumerable<Response> Handle(Request request,
[EnumeratorCancellation] CancellationToken ct)
public async IAsyncEnumerable<Response> 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;
}
}

View file

@ -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<Response>;
[UsedImplicitly]
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, Response>
private sealed class Handler(QueryDbContext ctx, IMemoryCache cache) : IStreamRequestHandler<Request, Response>
{
public async IAsyncEnumerable<Response> Handle(Request request,
[EnumeratorCancellation] CancellationToken ct)
public async IAsyncEnumerable<Response> 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;
}
}

View file

@ -20,7 +20,7 @@ public static void AddInfrastructure(this IHostApplicationBuilder builder)
so.EnableRetryOnFailure([0])
.MigrationsAssembly(MigrationsAssembly))
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));
builder.Services.AddHostedService<MigrationService>();
builder.Services.AddMemoryCache();
}
}

View file

@ -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<TResponse> GetOrCreateAsyncEnumerable<TResponse>(
this IMemoryCache cache,
object key,
ConfiguredCancelableAsyncEnumerable<TResponse> query,
MemoryCacheEntryOptions? options = null) where TResponse : class
{
if (cache.TryGetValue(key, out List<TResponse>? cachedElements))
{
foreach (var element in cachedElements ?? []) yield return element;
}
else
{
var elementsToCache = new List<TResponse>();
await foreach (var element in query)
{
elementsToCache.Add(element);
yield return element;
}
cache.Set(key, elementsToCache, options);
}
}
}