perf(backend): cache YouTube searches and shrink the thumbnail URLs

Repeated searches and song lookups now reuse a single-flight memory cache, so
concurrent or debounced queries hit YouTube once instead of on every keystroke.
Thumbnails are resolved at a configurable size instead of the largest variant,
which cuts the image payload the browser pulls from Google.
This commit is contained in:
2026-07-20 17:29:28 +02:00
parent 7c62f8a8e8
commit dd3150e3d4
5 changed files with 144 additions and 9 deletions
@@ -0,0 +1,41 @@
using System.Collections.Concurrent;
using ErrorOr;
using Microsoft.Extensions.Caching.Memory;
namespace Musify.Infrastructure.Services
{
public sealed class SingleFlightCache(IMemoryCache cache)
{
private readonly ConcurrentDictionary<string, object> inFlight = new();
public async Task<ErrorOr<TValue>> GetOrCreateAsync<TValue>(
string key,
TimeSpan duration,
Func<Task<ErrorOr<TValue>>> factory)
{
if (duration <= TimeSpan.Zero)
return await factory();
if (cache.TryGetValue(key, out TValue? cached) && cached is not null)
return cached;
var pending = (Lazy<Task<ErrorOr<TValue>>>)inFlight.GetOrAdd(
key,
_ => new Lazy<Task<ErrorOr<TValue>>>(factory));
try
{
var result = await pending.Value;
if (!result.IsError)
cache.Set(key, result.Value, duration);
return result;
}
finally
{
inFlight.TryRemove(key, out _);
}
}
}
}