diff --git a/modules/AgonesAllocator/Project/AgonesAllocator.cs b/modules/AgonesAllocator/Project/AgonesAllocator.cs index dcc3c80..83460c5 100644 --- a/modules/AgonesAllocator/Project/AgonesAllocator.cs +++ b/modules/AgonesAllocator/Project/AgonesAllocator.cs @@ -26,18 +26,24 @@ public class ModuleConfig : ICloudCodeSetup public void Setup(ICloudCodeConfig config) { - config.Dependencies.AddScoped(_ => + config.Dependencies.AddSingleton(_ => { // TODO: Replace with required auth of your service var authProvider = new AnonymousAuthenticationProvider(); - var handler = new HttpClientHandler + var handler = new SocketsHttpHandler { + PooledConnectionLifetime = TimeSpan.FromMinutes(5), + PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2), + MaxConnectionsPerServer = 300, // TODO: Implement MTLS or other cert validation here - // ServerCertificateCustomValidationCallback = (_, _, _, _) => throw new NotImplementedException() + // SslOptions = new SslClientAuthenticationOptions { RemoteCertificateValidationCallback = (_, _, _, _) => throw new NotImplementedException() }, }; - - return new HttpClientRequestAdapter(authProvider, httpClient: new HttpClient(handler)) + + // Cloud Code cancels an invocation at 15s; fail with budget left to return an error. + var httpClient = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(10) }; + + return new HttpClientRequestAdapter(authProvider, httpClient: httpClient) { BaseUrl = AllocatorServiceBaseUrl }; diff --git a/modules/EdgegapAllocator/Project/Client/EdgegapClient.cs b/modules/EdgegapAllocator/Project/Client/EdgegapClient.cs index 847d07a..e229b61 100644 --- a/modules/EdgegapAllocator/Project/Client/EdgegapClient.cs +++ b/modules/EdgegapAllocator/Project/Client/EdgegapClient.cs @@ -1,3 +1,4 @@ +using System; using System.Net.Http; namespace EdgegapAllocatorModule.Client; @@ -9,9 +10,21 @@ public interface IEdgegapHttpClientFactory public class EdgegapHttpClientFactory : IEdgegapHttpClientFactory { + // Static so the connection pool outlives a single invocation; a per-call handler re-handshakes every request. + private static readonly SocketsHttpHandler SharedHandler = new() + { + PooledConnectionLifetime = TimeSpan.FromMinutes(5), + PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2), + MaxConnectionsPerServer = 300 + }; + public HttpClient Create(string apiToken) { - var client = new HttpClient(); + // Cloud Code cancels an invocation at 15s; fail with budget left to return an error. + var client = new HttpClient(SharedHandler, disposeHandler: false) + { + Timeout = TimeSpan.FromSeconds(10) + }; client.DefaultRequestHeaders.Add("Authorization", $"{apiToken}"); return client; } diff --git a/modules/EdgegapAllocator/Project/EdgegapAllocator.cs b/modules/EdgegapAllocator/Project/EdgegapAllocator.cs index 92238f0..94960b2 100644 --- a/modules/EdgegapAllocator/Project/EdgegapAllocator.cs +++ b/modules/EdgegapAllocator/Project/EdgegapAllocator.cs @@ -28,7 +28,7 @@ public class ModuleConfig : ICloudCodeSetup public void Setup(ICloudCodeConfig config) { config.Dependencies.AddSingleton(GameApiClient.Create()); - config.Dependencies.AddScoped(); + config.Dependencies.AddSingleton(); } } @@ -143,8 +143,8 @@ public async Task Poll(IExecutionContext context, PollRequest requ try { Secret edgegapApiToken = await gameApiClient.SecretManager.GetSecret(context, EdgegapApiTokenSecretName); - HttpClient client = httpClientFactory.Create(edgegapApiToken.Value); - HttpResponseMessage response = await client.GetAsync($"{EdgegapApiUrl}/v1/status/{requestId}"); + using HttpClient client = httpClientFactory.Create(edgegapApiToken.Value); + using HttpResponseMessage response = await client.GetAsync($"{EdgegapApiUrl}/v1/status/{requestId}"); string responseContent = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) diff --git a/modules/GameyeAllocator/Project/Client/GameyeClient.cs b/modules/GameyeAllocator/Project/Client/GameyeClient.cs index 3bef28c..6f93a3b 100644 --- a/modules/GameyeAllocator/Project/Client/GameyeClient.cs +++ b/modules/GameyeAllocator/Project/Client/GameyeClient.cs @@ -1,3 +1,4 @@ +using System; using System.Net.Http; namespace GameyeAllocatorModule.Client; @@ -9,9 +10,21 @@ public interface IGameyeHttpClientFactory public class GameyeHttpClientFactory : IGameyeHttpClientFactory { + // Static so the connection pool outlives a single invocation; a per-call handler re-handshakes every request. + private static readonly SocketsHttpHandler SharedHandler = new() + { + PooledConnectionLifetime = TimeSpan.FromMinutes(5), + PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2), + MaxConnectionsPerServer = 300 + }; + public HttpClient Create(string apiToken) { - var client = new HttpClient(); + // Cloud Code cancels an invocation at 15s; fail with budget left to return an error. + var client = new HttpClient(SharedHandler, disposeHandler: false) + { + Timeout = TimeSpan.FromSeconds(10) + }; client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiToken}"); return client; } diff --git a/modules/GameyeAllocator/Project/GameyeAllocator.cs b/modules/GameyeAllocator/Project/GameyeAllocator.cs index 22bef46..909dc7c 100644 --- a/modules/GameyeAllocator/Project/GameyeAllocator.cs +++ b/modules/GameyeAllocator/Project/GameyeAllocator.cs @@ -26,7 +26,7 @@ public class ModuleConfig : ICloudCodeSetup public void Setup(ICloudCodeConfig config) { config.Dependencies.AddSingleton(GameApiClient.Create()); - config.Dependencies.AddScoped(); + config.Dependencies.AddSingleton(); // ────────────────────────────────────────────────────────────── // Gameye allocator configuration — edit the values below. diff --git a/modules/RocketScienceAllocator/Project/RocketScienceAllocator.cs b/modules/RocketScienceAllocator/Project/RocketScienceAllocator.cs index 80d738c..c4c09a6 100644 --- a/modules/RocketScienceAllocator/Project/RocketScienceAllocator.cs +++ b/modules/RocketScienceAllocator/Project/RocketScienceAllocator.cs @@ -18,7 +18,7 @@ public class ModuleConfig : ICloudCodeSetup public void Setup(ICloudCodeConfig config) { config.Dependencies.AddSingleton(GameApiClient.Create()); - config.Dependencies.AddScoped(); + config.Dependencies.AddSingleton(); } } @@ -173,9 +173,21 @@ public interface IRocketScienceHttpClientFactory public class RocketScienceHttpClientFactory : IRocketScienceHttpClientFactory { + // Static so the connection pool outlives a single invocation; a per-call handler re-handshakes every request. + private static readonly SocketsHttpHandler SharedHandler = new() + { + PooledConnectionLifetime = TimeSpan.FromMinutes(5), + PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2), + MaxConnectionsPerServer = 300 + }; + public HttpClient Create(string apiKey) { - var client = new HttpClient(); + // Cloud Code cancels an invocation at 15s; fail with budget left to return an error. + var client = new HttpClient(SharedHandler, disposeHandler: false) + { + Timeout = TimeSpan.FromSeconds(10) + }; client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey); return client; }