Skip to content

Commit 42cbc3c

Browse files
authored
Merge pull request #1928 from tyrielv/tyrielv/oom-connection-pool-size
Increase HTTP connection pool default to 2xCPU count and allow configuration override
2 parents a1f7ff4 + 43ff0ed commit 42cbc3c

2 files changed

Lines changed: 68 additions & 2 deletions

File tree

GVFS/GVFS.Common/GVFSConstants.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public static class GitConfig
4646

4747
public const string ShowHydrationStatus = GVFSPrefix + "show-hydration-status";
4848
public const bool ShowHydrationStatusDefault = false;
49+
50+
public const string MaxHttpConnectionsConfig = GVFSPrefix + "max-http-connections";
4951
}
5052

5153
public static class LocalGVFSConfig

GVFS/GVFS.Common/Http/HttpRequestor.cs

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public abstract class HttpRequestor : IDisposable
2222

2323
private static long requestCount = 0;
2424
private static SemaphoreSlim availableConnections;
25+
private static int connectionLimitConfigured = 0;
2526

2627
private readonly ProductInfoHeaderValue userAgentHeader;
2728

@@ -37,8 +38,12 @@ static HttpRequestor()
3738
using (var machineConfigLock = GetMachineConfigLock())
3839
{
3940
ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol | SecurityProtocolType.Tls12;
40-
ServicePointManager.DefaultConnectionLimit = Environment.ProcessorCount;
41-
availableConnections = new SemaphoreSlim(ServicePointManager.DefaultConnectionLimit);
41+
42+
// HTTP downloads are I/O-bound, not CPU-bound, so we default to
43+
// 2x ProcessorCount. Can be overridden via gvfs.max-http-connections.
44+
int connectionLimit = 2 * Environment.ProcessorCount;
45+
ServicePointManager.DefaultConnectionLimit = connectionLimit;
46+
availableConnections = new SemaphoreSlim(connectionLimit);
4247
}
4348
}
4449

@@ -50,6 +55,13 @@ protected HttpRequestor(ITracer tracer, RetryConfig retryConfig, Enlistment enli
5055

5156
this.Tracer = tracer;
5257

58+
// On first instantiation, check git config for a custom connection limit.
59+
// This runs before any requests are made (during mount initialization).
60+
if (Interlocked.CompareExchange(ref connectionLimitConfigured, 1, 0) == 0)
61+
{
62+
TryApplyConnectionLimitFromConfig(tracer, enlistment);
63+
}
64+
5365
HttpClientHandler httpClientHandler = new HttpClientHandler() { UseDefaultCredentials = true };
5466

5567
this.authentication.ConfigureHttpClientHandlerSslIfNeeded(this.Tracer, httpClientHandler, enlistment.CreateGitProcess());
@@ -362,6 +374,58 @@ private static bool TryGetResponseMessageFromHttpRequestException(HttpRequestExc
362374

363375
}
364376

377+
private static void TryApplyConnectionLimitFromConfig(ITracer tracer, Enlistment enlistment)
378+
{
379+
try
380+
{
381+
GitProcess.ConfigResult result = enlistment.CreateGitProcess().GetFromConfig(GVFSConstants.GitConfig.MaxHttpConnectionsConfig);
382+
string error;
383+
int configuredLimit;
384+
if (!result.TryParseAsInt(0, 1, out configuredLimit, out error))
385+
{
386+
EventMetadata metadata = new EventMetadata();
387+
metadata.Add("error", error);
388+
tracer.RelatedWarning(metadata, "HttpRequestor: Invalid gvfs.max-http-connections config value, using default");
389+
return;
390+
}
391+
392+
if (configuredLimit > 0)
393+
{
394+
int currentLimit = ServicePointManager.DefaultConnectionLimit;
395+
ServicePointManager.DefaultConnectionLimit = configuredLimit;
396+
397+
// Adjust the existing semaphore rather than replacing it, so any
398+
// in-flight waiters release permits to the correct instance.
399+
int delta = configuredLimit - currentLimit;
400+
if (delta > 0)
401+
{
402+
for (int i = 0; i < delta; i++)
403+
{
404+
availableConnections.Release();
405+
}
406+
}
407+
else if (delta < 0)
408+
{
409+
for (int i = 0; i < -delta; i++)
410+
{
411+
availableConnections.Wait();
412+
}
413+
}
414+
415+
EventMetadata metadata = new EventMetadata();
416+
metadata.Add("configuredLimit", configuredLimit);
417+
metadata.Add("previousLimit", currentLimit);
418+
tracer.RelatedEvent(EventLevel.Informational, "HttpRequestor_ConnectionLimitConfigured", metadata);
419+
}
420+
}
421+
catch (Exception e)
422+
{
423+
EventMetadata metadata = new EventMetadata();
424+
metadata.Add("Exception", e.ToString());
425+
tracer.RelatedWarning(metadata, "HttpRequestor: Failed to read gvfs.max-http-connections config, using default");
426+
}
427+
}
428+
365429
private static FileStream GetMachineConfigLock()
366430
{
367431
var machineConfigLocation = RuntimeEnvironment.SystemConfigurationFile;

0 commit comments

Comments
 (0)