Skip to content

Commit 0980da5

Browse files
committed
Increase max parallel DNS requests
Increase max parallel HTTP getInfo requests Decrease sever refresh timeout Add aggregation service to get both IW4M and HMW master server lists Remove server data list fetching (unused)
1 parent ddff61e commit 0980da5

File tree

14 files changed

+211
-180
lines changed

14 files changed

+211
-180
lines changed

H2MLauncher.Core/Networking/CachedIpv6EndpointResolver.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public sealed class CachedIpv6EndpointResolver(ILogger<CachedIpv6EndpointResolve
1313
private readonly ILogger<CachedIpv6EndpointResolver> _logger = logger;
1414
private readonly IMemoryCache _memoryCache = memoryCache;
1515

16+
private const int MaxParallelDnsRequests = 200;
17+
1618
private record struct IpEndpointCacheKey(string IpOrHostName, int Port) { }
1719

1820
/// <summary>
@@ -91,7 +93,7 @@ await Parallel.ForEachAsync(
9193
new ParallelOptions()
9294
{
9395
CancellationToken = cancellationToken,
94-
MaxDegreeOfParallelism = 20
96+
MaxDegreeOfParallelism = MaxParallelDnsRequests
9597
},
9698
async (server, token) =>
9799
{

H2MLauncher.Core/Networking/GameServer/HMW/HttpGameServerInfoService.cs

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Concurrent;
2+
using System.Diagnostics;
23
using System.Net;
34
using System.Reactive.Disposables;
45
using System.Runtime.CompilerServices;
@@ -16,7 +17,7 @@ public sealed class HttpGameServerInfoService<TServer> : IGameServerInfoService<
1617
{
1718
private readonly HttpClient _httpClient;
1819
private readonly ILogger<HttpGameServerInfoService<TServer>> _logger;
19-
private const int MAX_PARALLEL_REQUESTS = 15;
20+
private const int MAX_PARALLEL_REQUESTS = 50;
2021

2122
public HttpGameServerInfoService(ILogger<HttpGameServerInfoService<TServer>> logger, HttpClient httpClient)
2223
{
@@ -44,8 +45,23 @@ public HttpGameServerInfoService(ILogger<HttpGameServerInfoService<TServer>> log
4445
int requestTimeoutInMs = 10000,
4546
CancellationToken cancellationToken = default)
4647
{
48+
if (servers.TryGetNonEnumeratedCount(out int count))
49+
{
50+
_logger.LogDebug(
51+
"Requesting HTTP server info for {numServers} servers (sendSynchronously: {sendSync}, requestTimeout: {requestTimeout})",
52+
count, sendSynchronously, requestTimeoutInMs);
53+
}
54+
else
55+
{
56+
_logger.LogDebug(
57+
"Requesting HTTP server info (sendSynchronously: {sendSync}, requestTimeout: {requestTimeout})",
58+
sendSynchronously, requestTimeoutInMs);
59+
}
60+
61+
Activity activity = new("HttpGetInfoAsync");
4762
Channel<(TServer server, GameServerInfo? info)> channel = Channel.CreateUnbounded<(TServer, GameServerInfo?)>();
4863
ConcurrentBag<Task> continuations = [];
64+
int responseCount = 0;
4965

5066
Task requestTask = Parallel.ForEachAsync(
5167
servers,
@@ -67,6 +83,7 @@ public HttpGameServerInfoService(ILogger<HttpGameServerInfoService<TServer>> log
6783
if (t.IsCompletedSuccessfully)
6884
{
6985
channel.Writer.TryWrite((server, t.Result));
86+
Interlocked.Increment(ref responseCount);
7087
}
7188
},
7289
CancellationToken.None,
@@ -86,7 +103,16 @@ public HttpGameServerInfoService(ILogger<HttpGameServerInfoService<TServer>> log
86103
}).ContinueWith((task) =>
87104
{
88105
return Task.WhenAll(continuations)
89-
.ContinueWith(t => channel.Writer.TryComplete(t.Exception));
106+
.ContinueWith(t =>
107+
{
108+
channel.Writer.TryComplete(t.Exception);
109+
110+
_logger.LogDebug(
111+
"Received HTTP server info from {numServersResponded} servers.",
112+
responseCount);
113+
114+
activity?.Dispose();
115+
});
90116
});
91117

92118
if (sendSynchronously)
@@ -100,10 +126,27 @@ public HttpGameServerInfoService(ILogger<HttpGameServerInfoService<TServer>> log
100126
public async Task GetInfoAsync(IEnumerable<TServer> servers, Action<ServerInfoEventArgs<TServer, GameServerInfo>> onInfoResponse,
101127
int timeoutInMs = 10000, CancellationToken cancellationToken = default)
102128
{
129+
using Activity activity = new("HttpGetInfoAsync");
130+
131+
if (servers.TryGetNonEnumeratedCount(out int count))
132+
{
133+
_logger.LogDebug(
134+
"Requesting HTTP server info for {numServers} servers (requestTimeout: {requestTimeout})",
135+
count, timeoutInMs);
136+
}
137+
else
138+
{
139+
_logger.LogDebug(
140+
"Requesting HTTP server info (requestTimeout: {requestTimeout})",
141+
timeoutInMs);
142+
}
143+
103144
using CancellationTokenSource timeoutCancellation = new(timeoutInMs);
104145
using CancellationTokenSource linkedCancellation = CancellationTokenSource.CreateLinkedTokenSource(timeoutCancellation.Token, cancellationToken);
105146
try
106147
{
148+
int responseCount = 0;
149+
107150
await Parallel.ForEachAsync(
108151
servers,
109152
new ParallelOptions()
@@ -113,24 +156,29 @@ await Parallel.ForEachAsync(
113156
},
114157
async (server, ct) =>
115158
{
116-
117159
try
118160
{
119161
GameServerInfo? info = await GetInfoAsync(server, linkedCancellation.Token);
120162
if (info is not null)
121163
{
122164
onInfoResponse(new() { Server = server, ServerInfo = info });
165+
Interlocked.Increment(ref responseCount);
123166
}
124167
}
125168
catch (OperationCanceledException)
126169
{
127170
// canceled
128171
}
129172
}).ConfigureAwait(false);
173+
174+
_logger.LogDebug(
175+
"Received HTTP server info from {numServersResponded} servers.",
176+
responseCount);
130177
}
131178
catch (OperationCanceledException)
132179
{
133180
// canceled
181+
_logger.LogDebug("Requesting HTTP server info canceled.");
134182
}
135183
finally
136184
{
@@ -263,14 +311,7 @@ public async Task<IDisposable> SendGetInfoAsync(IEnumerable<TServer> servers, Ac
263311
return ReadInfoFromResponseAsync(response, server, timings, cancellationToken);
264312
}
265313
catch (OperationCanceledException) { return Task.FromResult<GameServerInfo?>(null); }
266-
catch (Exception ex)
267-
{
268-
_logger.LogError(ex, "Error while requesting server info from {server}", server);
269-
270-
return Task.FromResult<GameServerInfo?>(null);
271-
}
314+
catch { return Task.FromResult<GameServerInfo?>(null); }
272315
}
273-
274-
275316
}
276317
}

H2MLauncher.Core/Networking/GameServer/TcpUdpDynamicGameServerInfoService.cs

Lines changed: 0 additions & 113 deletions
This file was deleted.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using H2MLauncher.Core.Models;
2+
using H2MLauncher.Core.Utilities;
3+
4+
using Microsoft.Extensions.DependencyInjection;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace H2MLauncher.Core.Services
8+
{
9+
public class AggregatedMasterServerService : IMasterServerService
10+
{
11+
private readonly IMasterServerService _hmwMasterServerService;
12+
private readonly IMasterServerService _iw4mMasterServerService;
13+
private readonly ILogger<AggregatedMasterServerService> _logger;
14+
15+
public AggregatedMasterServerService(
16+
[FromKeyedServices("HMW")] IMasterServerService hmwMasterServerService,
17+
[FromKeyedServices("H2M")] IMasterServerService iw4mMasterServerService,
18+
ILogger<AggregatedMasterServerService> logger)
19+
{
20+
_hmwMasterServerService = hmwMasterServerService;
21+
_iw4mMasterServerService = iw4mMasterServerService;
22+
_logger = logger;
23+
}
24+
25+
public IAsyncEnumerable<ServerConnectionDetails> FetchServersAsync(CancellationToken cancellationToken)
26+
{
27+
IEnumerable<IAsyncEnumerable<ServerConnectionDetails>> sources = [
28+
_iw4mMasterServerService.FetchServersAsync(cancellationToken),
29+
_hmwMasterServerService.FetchServersAsync(cancellationToken),
30+
];
31+
32+
return sources.Interleave().Distinct();
33+
}
34+
35+
public IAsyncEnumerable<ServerConnectionDetails> GetServersAsync(CancellationToken cancellationToken)
36+
{
37+
IEnumerable<IAsyncEnumerable<ServerConnectionDetails>> sources = [
38+
_iw4mMasterServerService.GetServersAsync(cancellationToken),
39+
_hmwMasterServerService.GetServersAsync(cancellationToken),
40+
];
41+
42+
return sources.Interleave().Distinct();
43+
}
44+
}
45+
}
Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using H2MLauncher.Core.Models;
1+
using System.Runtime.CompilerServices;
2+
3+
using H2MLauncher.Core.Models;
24

35
using Microsoft.Extensions.Caching.Memory;
46

@@ -9,17 +11,32 @@ public abstract class CachedMasterServerService(IMemoryCache memoryCache, string
911
protected IMemoryCache Cache { get; } = memoryCache;
1012
protected string CacheKey { get; } = cacheKey;
1113

12-
public abstract Task<IReadOnlySet<ServerConnectionDetails>> FetchServersAsync(CancellationToken cancellationToken);
14+
public async IAsyncEnumerable<ServerConnectionDetails> FetchServersAsync([EnumeratorCancellation] CancellationToken cancellationToken)
15+
{
16+
var servers = await FetchServersCoreAsync(cancellationToken);
17+
18+
foreach (var server in servers)
19+
{
20+
if (cancellationToken.IsCancellationRequested)
21+
{
22+
yield break;
23+
}
1324

14-
public Task<IReadOnlySet<ServerConnectionDetails>> GetServersAsync(CancellationToken cancellationToken)
25+
yield return server;
26+
}
27+
}
28+
29+
public IAsyncEnumerable<ServerConnectionDetails> GetServersAsync(CancellationToken cancellationToken)
1530
{
1631
if (Cache.TryGetValue<IReadOnlySet<ServerConnectionDetails>>(CacheKey, out var cachedServers)
1732
&& cachedServers is not null)
1833
{
19-
return Task.FromResult(cachedServers);
34+
return cachedServers.ToAsyncEnumerable();
2035
}
2136

2237
return FetchServersAsync(cancellationToken);
2338
}
39+
40+
protected abstract Task<IReadOnlySet<ServerConnectionDetails>> FetchServersCoreAsync(CancellationToken cancellationToken);
2441
}
2542
}

H2MLauncher.Core/Services/H2MServersService.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System.Collections.Generic;
2-
using System.Net;
1+
using System.Net;
32

43
using H2MLauncher.Core.IW4MAdmin;
54
using H2MLauncher.Core.IW4MAdmin.Models;
@@ -21,7 +20,7 @@ public class H2MServersService(
2120
private readonly IServiceScopeFactory _serviceScopeFactory = serviceScopeFactory;
2221
private readonly IEndpointResolver _endpointResolver = endpointResolver;
2322

24-
public override async Task<IReadOnlySet<ServerConnectionDetails>> FetchServersAsync(CancellationToken cancellationToken)
23+
protected override async Task<IReadOnlySet<ServerConnectionDetails>> FetchServersCoreAsync(CancellationToken cancellationToken)
2524
{
2625
IServiceScope scope = _serviceScopeFactory.CreateScope();
2726
try
@@ -51,7 +50,7 @@ key.AddressFamily is System.Net.Sockets.AddressFamily.InterNetwork ||
5150
}
5251
catch (Exception ex)
5352
{
54-
_errorHandlingService.HandleException(ex, "Unable to fetch the servers details at this time. Please try again later.");
53+
_errorHandlingService.HandleException(ex, "Unable to fetch the servers details at this time. Please try again later.");
5554
}
5655
finally
5756
{

H2MLauncher.Core/Services/HMWMasterService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class HMWMasterService(IErrorHandlingService errorHandlingService, IHttpC
1212
private readonly IErrorHandlingService _errorHandlingService = errorHandlingService;
1313
private readonly IHttpClientFactory _httpClientFactory = httpClientFactory;
1414

15-
public override async Task<IReadOnlySet<ServerConnectionDetails>> FetchServersAsync(CancellationToken cancellationToken)
15+
protected override async Task<IReadOnlySet<ServerConnectionDetails>> FetchServersCoreAsync(CancellationToken cancellationToken)
1616
{
1717
HttpClient httpClient = _httpClientFactory.CreateClient(nameof(HMWMasterService));
1818
HashSet<ServerConnectionDetails> servers = [];

0 commit comments

Comments
 (0)