Skip to content

Commit 937c6e1

Browse files
Tyrie VellaCopilot
andcommitted
Increase HTTP connection pool minimum to 64
The connection pool was previously sized to Environment.ProcessorCount (e.g., 8 on an 8-core machine). HTTP object downloads are I/O-bound, not CPU-bound, so CPU count is a poor proxy for optimal connection concurrency. Under burst workloads like git checkout, 8 connections saturate almost instantly. Set a floor of 64 connections (or ProcessorCount if higher). This provides more headroom before pool exhaustion during burst object download scenarios. Work item: 60167591 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 44b6d6b commit 937c6e1

2 files changed

Lines changed: 44 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: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ namespace GVFS.Common.Http
1717
{
1818
public abstract class HttpRequestor : IDisposable
1919
{
20+
private const int MinConnectionLimit = 64;
21+
2022
private static long requestCount = 0;
2123
private static SemaphoreSlim availableConnections;
24+
private static int connectionLimitConfigured = 0;
2225

2326
private readonly ProductInfoHeaderValue userAgentHeader;
2427

@@ -34,8 +37,13 @@ static HttpRequestor()
3437
using (var machineConfigLock = GetMachineConfigLock())
3538
{
3639
ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol | SecurityProtocolType.Tls12;
37-
ServicePointManager.DefaultConnectionLimit = Environment.ProcessorCount;
38-
availableConnections = new SemaphoreSlim(ServicePointManager.DefaultConnectionLimit);
40+
41+
// HTTP downloads are I/O-bound, not CPU-bound, so ProcessorCount is too low
42+
// for burst workloads (e.g., checkout triggering many object downloads).
43+
// This default can be overridden via git config gvfs.max-http-connections.
44+
int connectionLimit = Math.Max(Environment.ProcessorCount, MinConnectionLimit);
45+
ServicePointManager.DefaultConnectionLimit = connectionLimit;
46+
availableConnections = new SemaphoreSlim(connectionLimit);
3947
}
4048
}
4149

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

4856
this.Tracer = tracer;
4957

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+
5065
HttpClientHandler httpClientHandler = new HttpClientHandler() { UseDefaultCredentials = true };
5166

5267
this.authentication.ConfigureHttpClientHandlerSslIfNeeded(this.Tracer, httpClientHandler, enlistment.CreateGitProcess());
@@ -337,6 +352,31 @@ private static bool TryGetResponseMessageFromHttpRequestException(HttpRequestExc
337352

338353
}
339354

355+
private static void TryApplyConnectionLimitFromConfig(ITracer tracer, Enlistment enlistment)
356+
{
357+
try
358+
{
359+
GitProcess.ConfigResult result = enlistment.CreateGitProcess().GetFromConfig(GVFSConstants.GitConfig.MaxHttpConnectionsConfig);
360+
string error;
361+
int configuredLimit;
362+
if (result.TryParseAsInt(0, MinConnectionLimit, out configuredLimit, out error) && configuredLimit > 0)
363+
{
364+
ServicePointManager.DefaultConnectionLimit = configuredLimit;
365+
availableConnections = new SemaphoreSlim(configuredLimit);
366+
367+
EventMetadata metadata = new EventMetadata();
368+
metadata.Add("configuredLimit", configuredLimit);
369+
tracer.RelatedEvent(EventLevel.Informational, "HttpRequestor_ConnectionLimitConfigured", metadata);
370+
}
371+
}
372+
catch (Exception e)
373+
{
374+
EventMetadata metadata = new EventMetadata();
375+
metadata.Add("Exception", e.ToString());
376+
tracer.RelatedWarning(metadata, "HttpRequestor: Failed to read gvfs.max-http-connections config, using default");
377+
}
378+
}
379+
340380
private static FileStream GetMachineConfigLock()
341381
{
342382
var machineConfigLocation = RuntimeEnvironment.SystemConfigurationFile;

0 commit comments

Comments
 (0)