diff --git a/LibMatrix/Extensions/MatrixHttpClient.Single.cs b/LibMatrix/Extensions/MatrixHttpClient.Single.cs
index cd82071..ae18b2d 100644
--- a/LibMatrix/Extensions/MatrixHttpClient.Single.cs
+++ b/LibMatrix/Extensions/MatrixHttpClient.Single.cs
@@ -1,5 +1,6 @@
#define SINGLE_HTTPCLIENT // Use a single HttpClient instance for all MatrixHttpClient instances
// #define SYNC_HTTPCLIENT // Only allow one request as a time, for debugging
+using System.Collections.Concurrent;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Net;
@@ -70,7 +71,7 @@ public class MatrixHttpClient {
public int MaxRetryIntervalMs { get; set; } = DefaultMaxRetryIntervalMs;
public int MaxRetries { get; set; } = DefaultMaxRetries;
- private Dictionary<HttpRequestMessage, int> _retries = [];
+ private readonly ConcurrentDictionary<HttpRequestMessage, int> _retries = [];
// default headers, not bound to client
public HttpRequestHeaders DefaultRequestHeaders { get; set; } =
@@ -224,7 +225,12 @@ public class MatrixHttpClient {
}
if (responseMessage.IsSuccessStatusCode) {
- _retries.Remove(request);
+ while (!_retries.TryRemove(request, out _)) {
+ Console.WriteLine("[MatrixHttpClient] Race - failed to remove retries entry, retrying...");
+ // ReSharper disable once MethodSupportsCancellation - this shouldn't be cancellable as it would be a memory leak
+ await Task.Delay(5); // hopefully helps resolve contention?
+ }
+
return responseMessage;
}
diff --git a/LibMatrix/Helpers/RoomBuilder.cs b/LibMatrix/Helpers/RoomBuilder.cs
index 1e33bb5..ed47eb2 100644
--- a/LibMatrix/Helpers/RoomBuilder.cs
+++ b/LibMatrix/Helpers/RoomBuilder.cs
@@ -207,7 +207,7 @@ public class RoomBuilder {
private async Task SetStatesAsync(GenericRoom room, List<MatrixEvent> state) {
if (state.Count == 0) return;
Console.WriteLine($"Setting {state.Count} state events for {room.RoomId}...");
- await room.BulkSendEventsAsync(state);
+ // await room.BulkSendEventsAsync(state);
// We chunk this up to try to avoid hitting reverse proxy timeouts
// foreach (var group in state.Chunk(chunkSize)) {
// var sw = Stopwatch.StartNew();
@@ -217,27 +217,31 @@ public class RoomBuilder {
// Console.WriteLine($"Warning: Sending {group.Length} state events took {sw.ElapsedMilliseconds}ms, which is quite long. Reducing chunk size to {chunkSize}.");
// }
// }
- // int chunkSize = 50;
- // for (int i = 0; i < state.Count; i += chunkSize) {
- // var chunk = state.Skip(i).Take(chunkSize).ToList();
- // if (chunk.Count == 0) continue;
- //
- // var sw = Stopwatch.StartNew();
- // await room.BulkSendEventsAsync(chunk, forceSyncInterval: chunk.Count + 1);
- // Console.WriteLine($"Sent {chunk.Count} state events in {sw.ElapsedMilliseconds}ms. {state.Count - (i + chunk.Count)} remaining.");
- // // if (sw.ElapsedMilliseconds > 45000) {
- // // chunkSize = Math.Max(chunkSize / 3, 1);
- // // Console.WriteLine($"Warning: Sending {chunk.Count} state events took {sw.ElapsedMilliseconds}ms, which is dangerously long. Reducing chunk size to {chunkSize}.");
- // // }
- // // else if (sw.ElapsedMilliseconds > 30000) {
- // // chunkSize = Math.Max(chunkSize / 2, 1);
- // // Console.WriteLine($"Warning: Sending {chunk.Count} state events took {sw.ElapsedMilliseconds}ms, which is quite long. Reducing chunk size to {chunkSize}.");
- // // }
- // // else if (sw.ElapsedMilliseconds < 10000) {
- // // chunkSize = Math.Min((int)(chunkSize * 1.2), 1000);
- // // Console.WriteLine($"Info: Sending {chunk.Count} state events took {sw.ElapsedMilliseconds}ms, increasing chunk size to {chunkSize}.");
- // // }
- // }
+ int chunkSize = 767;
+ for (int i = 0; i < state.Count; i += chunkSize) {
+ var chunk = state.Skip(i).Take(chunkSize).ToList();
+ if (chunk.Count == 0) continue;
+
+ var sw = Stopwatch.StartNew();
+ await room.BulkSendEventsAsync(chunk, forceSyncInterval: chunk.Count + 1);
+ Console.WriteLine($"Sent {chunk.Count} state events in {sw.ElapsedMilliseconds}ms. {state.Count - (i + chunk.Count)} remaining.");
+ if (sw.ElapsedMilliseconds > 50000) {
+ chunkSize = Math.Max((int)(chunkSize / 1.2), 1);
+ Console.WriteLine($"Warning: Sending {chunk.Count} state events took {sw.ElapsedMilliseconds}ms, which is dangerously long. Reducing chunk size to {chunkSize}.");
+ }
+ // else if (sw.ElapsedMilliseconds > 30000) {
+ // chunkSize = Math.Max(chunkSize / 2, 1);
+ // Console.WriteLine($"Warning: Sending {chunk.Count} state events took {sw.ElapsedMilliseconds}ms, which is quite long. Reducing chunk size to {chunkSize}.");
+ // }
+ else if (sw.ElapsedMilliseconds < 5000) {
+ chunkSize = Math.Min((int)(chunkSize * 1.5), 1000);
+ Console.WriteLine($"Info: Sending {chunk.Count} state events took {sw.ElapsedMilliseconds}ms, increasing chunk size to {chunkSize}.");
+ }
+ else if (sw.ElapsedMilliseconds < 10000) {
+ chunkSize = Math.Min((int)(chunkSize * 1.2), 1000);
+ Console.WriteLine($"Info: Sending {chunk.Count} state events took {sw.ElapsedMilliseconds}ms, increasing chunk size to {chunkSize}.");
+ }
+ }
}
private async Task SetBasicRoomInfoAsync(GenericRoom room) {
diff --git a/LibMatrix/Responses/Federation/SignedObject.cs b/LibMatrix/Responses/Federation/SignedObject.cs
index 3f6ffd6..517bb1f 100644
--- a/LibMatrix/Responses/Federation/SignedObject.cs
+++ b/LibMatrix/Responses/Federation/SignedObject.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
@@ -19,7 +20,7 @@ public class SignedObject<T> {
}
[JsonExtensionData]
- public required JsonObject Content { get; set; }
+ public JsonObject Content { get; set; } = null!;
[JsonIgnore]
public T TypedContent {
diff --git a/LibMatrix/Services/ServiceInstaller.cs b/LibMatrix/Services/ServiceInstaller.cs
index 5ffd43a..7f15cd2 100644
--- a/LibMatrix/Services/ServiceInstaller.cs
+++ b/LibMatrix/Services/ServiceInstaller.cs
@@ -13,6 +13,7 @@ public static class ServiceInstaller {
services.AddSingleton<ClientWellKnownResolver>();
services.AddSingleton<ServerWellKnownResolver>();
services.AddSingleton<SupportWellKnownResolver>();
+ services.AddSingleton<PolicyServerWellKnownResolver>();
if (!services.Any(x => x.ServiceType == typeof(WellKnownResolverConfiguration)))
services.AddSingleton<WellKnownResolverConfiguration>();
services.AddSingleton<WellKnownResolverService>();
|