Skip to content

Commit 5c5bffd

Browse files
committed
Networking and Perfomrance Improvments
1 parent 3211a94 commit 5c5bffd

File tree

6 files changed

+93
-25
lines changed

6 files changed

+93
-25
lines changed

TreblleCore/ServiceCollectionExtensions.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ namespace Treblle.Net.Core;
1414

1515
public static class ServiceCollectionExtensions
1616
{
17-
private static readonly Uri DefaultApiUri = new("https://rocknrolla.treblle.com");
1817
private static readonly Dictionary<string, string> maskingMap = new()
1918
{
2019
{ "password", "DefaultStringMasker" },
@@ -153,8 +152,14 @@ private static IServiceCollection AddTreblleWithAutoConfiguration(
153152
services.AddHttpClient("Treblle", (serviceProvider, httpClient) =>
154153
{
155154
var options = serviceProvider.GetRequiredService<IOptions<TreblleOptions>>().Value;
156-
httpClient.BaseAddress = DefaultApiUri;
157155
httpClient.DefaultRequestHeaders.Add("x-api-key", options.SdkToken);
156+
httpClient.Timeout = TimeSpan.FromSeconds(10);
157+
})
158+
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
159+
{
160+
UseCookies = false, // Treblle API doesn't use cookies - disabling saves memory and processing
161+
UseProxy = false, // Skip proxy detection for better performance
162+
EnableMultipleHttp2Connections = true // Enable HTTP/2 multiplexing for better throughput
158163
});
159164

160165
// Register masker types individually (replacement for keyed services)
@@ -264,11 +269,6 @@ private static IServiceCollection AddTreblle(
264269
// Apply additional configuration if provided
265270
configureOptions?.Invoke(o);
266271
});
267-
services.AddHttpClient("Treblle", httpClient =>
268-
{
269-
httpClient.BaseAddress = DefaultApiUri;
270-
httpClient.DefaultRequestHeaders.Add("x-api-key", sdkToken);
271-
});
272272

273273
// Register masker types individually (replacement for keyed services)
274274
services.TryAddTransient<DefaultStringMasker>();

TreblleCore/Treblle.Net.Core.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<PropertyGroup>
44
<TargetFramework>net6.0</TargetFramework>
55
<Authors>Treblle</Authors>
6-
<Version>2.0.0</Version>
6+
<Version>2.0.1-beta.2</Version>
77
<PackageId>Treblle.Net.Core</PackageId>
88
<Product>Treblle .NET Core</Product>
99
<Description>Treblle makes it super easy to understand what's going on with your APIs and the apps that use them.</Description>

TreblleCore/TreblleJsonContext.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Collections.Generic;
2+
using System.Text.Json;
3+
using System.Text.Json.Serialization;
4+
5+
namespace Treblle.Net.Core;
6+
7+
/// <summary>
8+
/// Source-generated JSON serialization context for Treblle payload types.
9+
/// Provides compile-time serialization code generation for better performance.
10+
/// </summary>
11+
[JsonSerializable(typeof(TrebllePayload))]
12+
[JsonSerializable(typeof(JsonElement))]
13+
[JsonSerializable(typeof(object))] // For dynamic request/response bodies
14+
[JsonSerializable(typeof(string))] // For dictionary values
15+
[JsonSerializable(typeof(Dictionary<string, object>))] // For headers
16+
[JsonSourceGenerationOptions(
17+
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
18+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
19+
WriteIndented = false
20+
)]
21+
internal partial class TreblleJsonContext : JsonSerializerContext
22+
{
23+
}

TreblleCore/TreblleMiddleware.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ public TreblleMiddleware(
3535
_logger = logger;
3636
_options = options.Value;
3737

38-
// Bounded channel prevents unbounded memory growth
39-
var channelOptions = new BoundedChannelOptions(1000)
38+
// Bounded channel prevents unbounded memory growth - increased capacity for high-volume scenarios
39+
var channelOptions = new BoundedChannelOptions(3000)
4040
{
4141
FullMode = BoundedChannelFullMode.DropOldest,
4242
SingleReader = true,

TreblleCore/TrebllePayloadFactory.cs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ internal sealed class TrebllePayloadFactory
2828
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
2929
};
3030

31+
// Cache static system information to avoid repeated allocations
32+
private static readonly string CachedOSName = Environment.OSVersion.ToString();
33+
private static readonly string CachedOSVersion = Environment.OSVersion.Version.ToString();
34+
private static readonly string CachedArchitecture = RuntimeInformation.ProcessArchitecture.ToString();
35+
private static readonly string CachedTimezone = (!string.IsNullOrEmpty(TimeZoneInfo.Local.StandardName))
36+
? TimeZoneInfo.Local.StandardName
37+
: "UTC";
38+
3139
private readonly TreblleOptions _treblleOptions;
3240
private readonly ILogger<TrebllePayloadFactory> _logger;
3341

@@ -90,16 +98,14 @@ private static void AddLanguage(TrebllePayload payload)
9098
private static void AddServer(HttpContext httpContext, TrebllePayload payload)
9199
{
92100
payload.Data.Server.Ip = httpContext.Connection.LocalIpAddress?.MapToIPv4()?.ToString() ?? "bogon";
93-
payload.Data.Server.Timezone = (!string.IsNullOrEmpty(TimeZoneInfo.Local.StandardName))
94-
? TimeZoneInfo.Local.StandardName
95-
: "UTC";
101+
payload.Data.Server.Timezone = CachedTimezone;
96102
payload.Data.Server.Software = httpContext.GetServerVariable("SERVER_SOFTWARE");
97103
payload.Data.Server.Signature = null;
98104
payload.Data.Server.Protocol = httpContext.Request.Protocol;
99105

100-
payload.Data.Server.Os.Name = Environment.OSVersion.ToString();
101-
payload.Data.Server.Os.Release = Environment.OSVersion.Version.ToString();
102-
payload.Data.Server.Os.Architecture = RuntimeInformation.ProcessArchitecture.ToString();
106+
payload.Data.Server.Os.Name = CachedOSName;
107+
payload.Data.Server.Os.Release = CachedOSVersion;
108+
payload.Data.Server.Os.Architecture = CachedArchitecture;
103109
}
104110

105111
private async Task AddRequest(HttpContext httpContext, TrebllePayload payload)
@@ -233,7 +239,7 @@ private async Task TryAddRequestBody(HttpContext httpContext, TrebllePayload pay
233239
}
234240
else if (IsValidJson(bodyData))
235241
{
236-
payload.Data.Request.Body = JsonSerializer.Deserialize<JsonElement>(bodyData, JsonOptions);
242+
payload.Data.Request.Body = JsonSerializer.Deserialize<JsonElement>(bodyData, TreblleJsonContext.Default.JsonElement);
237243
}
238244
else
239245
{
@@ -250,8 +256,8 @@ private async Task TryAddRequestBody(HttpContext httpContext, TrebllePayload pay
250256
else if (contentType.Contains("application/xml", StringComparison.OrdinalIgnoreCase))
251257
{
252258
var doc = XDocument.Parse(bodyData);
253-
var jsonText = JsonSerializer.Serialize(ConvertXDocumentToObject(doc), JsonOptions);
254-
payload.Data.Request.Body = JsonSerializer.Deserialize<JsonElement>(jsonText, JsonOptions);
259+
var jsonText = JsonSerializer.Serialize(ConvertXDocumentToObject(doc), TreblleJsonContext.Default.Object);
260+
payload.Data.Request.Body = JsonSerializer.Deserialize<JsonElement>(jsonText, TreblleJsonContext.Default.JsonElement);
255261
}
256262
else
257263
{
@@ -318,7 +324,7 @@ private async Task TryAddResponse(HttpContext httpContext, MemoryStream? respons
318324
}
319325
else if (IsValidJson(responseContent))
320326
{
321-
payload.Data.Response.Body = JsonSerializer.Deserialize<JsonElement>(responseContent, JsonOptions);
327+
payload.Data.Response.Body = JsonSerializer.Deserialize<JsonElement>(responseContent, TreblleJsonContext.Default.JsonElement);
322328
}
323329
else
324330
{
@@ -421,7 +427,7 @@ private bool IsValidJson(string str)
421427
{
422428
try
423429
{
424-
JsonSerializer.Deserialize<JsonElement>(str, JsonOptions);
430+
JsonSerializer.Deserialize<JsonElement>(str, TreblleJsonContext.Default.JsonElement);
425431
return true;
426432
}
427433
catch (JsonException)

TreblleCore/TreblleService.cs

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.IO;
4+
using System.IO.Compression;
35
using System.Net.Http;
46
using System.Text;
57
using System.Threading.Tasks;
@@ -22,6 +24,15 @@ internal sealed class TreblleService
2224
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping // Faster encoding
2325
};
2426

27+
private static readonly string[] TreblleEndpoints = new[]
28+
{
29+
"https://rocknrolla.treblle.com",
30+
"https://punisher.treblle.com",
31+
"https://sicario.treblle.com"
32+
};
33+
34+
private static readonly Random Random = new();
35+
2536
private readonly Dictionary<string, string> _maskingMap;
2637
private readonly HttpClient _httpClient;
2738
private readonly ILogger<TreblleService> _logger;
@@ -49,7 +60,7 @@ public TreblleService(
4960
{
5061
try
5162
{
52-
var jsonPayload = JsonSerializer.Serialize(payload, JsonOptions);
63+
var jsonPayload = JsonSerializer.Serialize(payload, TreblleJsonContext.Default.TrebllePayload);
5364

5465
// Check if payload exceeds 5MB limit
5566
const int maxPayloadSizeBytes = 5 * 1024 * 1024; // 5MB
@@ -88,15 +99,28 @@ public TreblleService(
8899
}
89100
};
90101

91-
jsonPayload = JsonSerializer.Serialize(reducedPayload, JsonOptions);
102+
jsonPayload = JsonSerializer.Serialize(reducedPayload, TreblleJsonContext.Default.TrebllePayload);
92103
}
93104

94105
var finalJsonPayload = _disableMasking
95106
? jsonPayload
96107
: jsonPayload.Mask(_maskingMap, _serviceProvider, _logger);
97108

98-
using HttpContent content = new StringContent(finalJsonPayload ?? string.Empty, Encoding.UTF8, "application/json");
99-
using var httpResponseMessage = await _httpClient.PostAsync(string.Empty, content);
109+
var randomEndpoint = TreblleEndpoints[Random.Next(TreblleEndpoints.Length)];
110+
111+
var jsonBytes = Encoding.UTF8.GetBytes(finalJsonPayload ?? string.Empty);
112+
var compressedBytes = CompressData(jsonBytes);
113+
114+
using HttpContent content = new ByteArrayContent(compressedBytes);
115+
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
116+
117+
// Only add gzip header if we actually compressed the data
118+
if (compressedBytes.Length < jsonBytes.Length)
119+
{
120+
content.Headers.ContentEncoding.Add("gzip");
121+
}
122+
123+
using var httpResponseMessage = await _httpClient.PostAsync(randomEndpoint, content);
100124
return httpResponseMessage;
101125
}
102126
catch (Exception ex)
@@ -109,4 +133,19 @@ public TreblleService(
109133
return null;
110134
}
111135
}
136+
137+
private static byte[] CompressData(byte[] data)
138+
{
139+
// Skip compression for small payloads - not worth the overhead
140+
if (data.Length < 1024)
141+
return data;
142+
143+
// Pre-size output stream to avoid buffer reallocations during compression
144+
using var output = new MemoryStream(data.Length / 3);
145+
using (var gzipStream = new GZipStream(output, CompressionLevel.Fastest))
146+
{
147+
gzipStream.Write(data, 0, data.Length);
148+
}
149+
return output.ToArray();
150+
}
112151
}

0 commit comments

Comments
 (0)