Basic redis object cache implementation
This commit is contained in:
parent
a0425aaf4c
commit
3e4410f52c
2 changed files with 48 additions and 4 deletions
|
@ -0,0 +1,38 @@
|
|||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
|
||||
namespace Iceshrimp.Backend.Core.Extensions;
|
||||
|
||||
public static class DistributedCacheExtensions {
|
||||
//TODO: named caches, CacheService?
|
||||
//TODO: thread-safe locks to prevent fetching data more than once
|
||||
|
||||
public static async Task<T?> Get<T>(this IDistributedCache cache, string key) {
|
||||
var buffer = await cache.GetAsync(key);
|
||||
if (buffer == null) return default;
|
||||
|
||||
var stream = new MemoryStream(buffer);
|
||||
var data = await JsonSerializer.DeserializeAsync<T>(stream);
|
||||
|
||||
return data != null ? (T)data : default;
|
||||
}
|
||||
|
||||
public static async Task<T> Fetch<T>(this IDistributedCache cache, string key, TimeSpan ttl,
|
||||
Func<Task<T>> fetcher) {
|
||||
var hit = await cache.Get<T>(key);
|
||||
if (hit != null) return hit;
|
||||
|
||||
var fetched = await fetcher();
|
||||
await cache.Set(key, fetched, ttl);
|
||||
return fetched;
|
||||
}
|
||||
|
||||
public static async Task Set<T>(this IDistributedCache cache, string key, T data, TimeSpan ttl) {
|
||||
using var ms = new MemoryStream();
|
||||
await JsonSerializer.SerializeAsync(ms, data);
|
||||
var buffer = new Memory<byte>();
|
||||
_ = await ms.ReadAsync(buffer);
|
||||
var options = new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = ttl };
|
||||
await cache.SetAsync(key, buffer.ToArray(), options);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ using Iceshrimp.Backend.Core.Federation.ActivityPub;
|
|||
using Iceshrimp.Backend.Core.Helpers;
|
||||
using Iceshrimp.Backend.Core.Middleware;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Iceshrimp.Backend.Core.Services;
|
||||
|
@ -19,7 +20,9 @@ public class UserService(
|
|||
IOptions<Config.InstanceSection> instance,
|
||||
ILogger<UserService> logger,
|
||||
DatabaseContext db,
|
||||
ActivityFetcherService fetchSvc) {
|
||||
ActivityFetcherService fetchSvc,
|
||||
IDistributedCache cache
|
||||
) {
|
||||
private (string Username, string? Host) AcctToTuple(string acct) {
|
||||
if (!acct.StartsWith("acct:")) throw new GracefulException(HttpStatusCode.BadRequest, "Invalid query");
|
||||
|
||||
|
@ -161,10 +164,13 @@ public class UserService(
|
|||
return await GetOrCreateSystemUser("relay.actor");
|
||||
}
|
||||
|
||||
//TODO: cache in redis
|
||||
private async Task<User> GetOrCreateSystemUser(string username) {
|
||||
return await db.Users.FirstOrDefaultAsync(p => p.UsernameLower == username && p.Host == null) ??
|
||||
return await cache.Fetch($"systemUser:{username}", TimeSpan.FromHours(24), async () => {
|
||||
logger.LogTrace("GetOrCreateSystemUser delegate method called for user {username}", username);
|
||||
return await db.Users.FirstOrDefaultAsync(p => p.UsernameLower == username &&
|
||||
p.Host == null) ??
|
||||
await CreateSystemUser(username);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<User> CreateSystemUser(string username) {
|
||||
|
|
Loading…
Add table
Reference in a new issue