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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
8fecf7de2d
commit
a4ced664cf
1 file changed
+12
-5
@@ -55,7 +55,8 @@ namespace PrivaPub.Domain.Media
|
||||
readonly IMediaService _media;
|
||||
readonly IOptionsMonitor<MediaOptions> _options;
|
||||
readonly Federation.Moderation.IDomainBlocks _domainBlocks;
|
||||
readonly ConcurrentDictionary<string, Task<(ProxyOutcome, string, string)>> _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<string, Lazy<Task<(ProxyOutcome, string, string)>>> _inFlight = new();
|
||||
readonly ConcurrentDictionary<string, (ProxyOutcome Outcome, DateTime Until)> _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<Task<(ProxyOutcome, string, string)>>(() => DownloadOnce(key)));
|
||||
try
|
||||
{
|
||||
return await download.WaitAsync(token);
|
||||
return await shared.Value.WaitAsync(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (download.IsCompleted)
|
||||
_inFlight.TryRemove(new KeyValuePair<string, Task<(ProxyOutcome, string, string)>>(url, download));
|
||||
if (shared.Value.IsCompleted)
|
||||
_inFlight.TryRemove(new KeyValuePair<string, Lazy<Task<(ProxyOutcome, string, string)>>>(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
|
||||
|
||||
Reference in new issue
Block a user