From a4ced664cfb8dfec4fc45c0954e80bb842728886 Mon Sep 17 00:00:00 2001 From: thepra Date: Wed, 7 Oct 2026 11:00:04 +0200 Subject: [PATCH] The proxy's shared download is started once ConcurrentDictionary.GetOrAdd may run its factory more than once when asked at the same time, and each run started a download: five clients asking at once for a new file made three upstream requests in one run of Clients_asking_at_once_share_one_download. The factory now makes a Lazy, of which only the one kept is ever started, and a download that begins just after another cached the file uses it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw --- PrivaPub/Domain/Media/MediaProxy.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/PrivaPub/Domain/Media/MediaProxy.cs b/PrivaPub/Domain/Media/MediaProxy.cs index 7d26f19..263c3b5 100644 --- a/PrivaPub/Domain/Media/MediaProxy.cs +++ b/PrivaPub/Domain/Media/MediaProxy.cs @@ -55,7 +55,8 @@ namespace PrivaPub.Domain.Media readonly IMediaService _media; readonly IOptionsMonitor _options; readonly Federation.Moderation.IDomainBlocks _domainBlocks; - readonly ConcurrentDictionary> _inFlight = new(); + // Lazy: GetOrAdd may run its factory more than once when asked at the same time, but only one Lazy is ever evaluated + readonly ConcurrentDictionary>> _inFlight = new(); readonly ConcurrentDictionary _refused = new(); readonly SemaphoreSlim _downloads = new(Downloads); readonly object _keyLock = new(); @@ -153,15 +154,15 @@ namespace PrivaPub.Domain.Media _refused.TryRemove(url, out _); } // shared by everyone asking for it now; the download itself isn't cancelled when one of them leaves - var download = _inFlight.GetOrAdd(url, key => DownloadOnce(key)); + var shared = _inFlight.GetOrAdd(url, key => new Lazy>(() => DownloadOnce(key))); try { - return await download.WaitAsync(token); + return await shared.Value.WaitAsync(token); } finally { - if (download.IsCompleted) - _inFlight.TryRemove(new KeyValuePair>(url, download)); + if (shared.Value.IsCompleted) + _inFlight.TryRemove(new KeyValuePair>>(url, shared)); } } @@ -170,6 +171,12 @@ namespace PrivaPub.Domain.Media await Task.Yield(); if (!await _downloads.WaitAsync(TimeSpan.FromSeconds(30))) return (ProxyOutcome.Failed, default, default);//too busy: the client tries again + // cached by a download that ended just before this one began + if (Cached(url) is { Path: not null } done) + { + _downloads.Release(); + return (ProxyOutcome.Cached, done.Path, done.ContentType); + } var (path, typePath) = CachePaths(url); var part = $"{path}.{Guid.NewGuid():N}.part"; try