Skip to content

Commit 360f84c

Browse files
alespourbednar
andauthored
feat: add gzip support (#33)
* feat: add support for compressing request payload Co-authored-by: Jakub Bednář <[email protected]> --------- Co-authored-by: Jakub Bednář <[email protected]>
1 parent d4b18b6 commit 360f84c

File tree

7 files changed

+178
-15
lines changed

7 files changed

+178
-15
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
## 0.2.0 [unreleased]
22

3+
### Features
4+
5+
1. [#32](https://github.com/InfluxCommunity/influxdb3-csharp/pull/33): Add GZIP support
6+
37
## 0.1.0 [2023-06-09]
48

59
- initial release of new client version

Client.Test.Integration/QueryWriteTest.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
using InfluxDB3.Client.Write;
99
using NUnit.Framework;
1010

11+
using WriteOptions = InfluxDB3.Client.Config.WriteOptions;
12+
1113
namespace InfluxDB3.Client.Test.Integration;
1214

1315
public class QueryWriteTest
@@ -106,4 +108,22 @@ public async Task CanDisableCertificateValidation()
106108

107109
await client.WritePointAsync(PointData.Measurement("cpu").AddTag("tag", "c"));
108110
}
111+
112+
113+
[Test]
114+
public async Task WriteDataGzipped()
115+
{
116+
using var client = new InfluxDBClient(new InfluxDBClientConfigs
117+
{
118+
HostUrl = _hostUrl,
119+
Database = _database,
120+
AuthToken = _authToken,
121+
WriteOptions = new WriteOptions
122+
{
123+
GzipThreshold = 1
124+
}
125+
});
126+
127+
await client.WritePointAsync(PointData.Measurement("cpu").AddTag("tag", "c").AddField("user", 14.34));
128+
}
109129
}

Client.Test/InfluxDBClientWriteTest.cs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Threading.Tasks;
44
using InfluxDB3.Client.Config;
55
using InfluxDB3.Client.Write;
6+
using WireMock.Matchers;
67
using WireMock.RequestBuilders;
78
using WireMock.ResponseBuilders;
89

@@ -74,6 +75,43 @@ public async Task BodyNull()
7475
Assert.That(requests[0].RequestMessage.BodyData?.BodyAsString, Is.EqualTo("mem,tag=a field=1"));
7576
}
7677

78+
[Test]
79+
public async Task BodyNonDefaultGzipped()
80+
{
81+
MockServer
82+
.Given(Request.Create().WithPath("/api/v2/write").WithHeader("Content-Encoding", "gzip").UsingPost())
83+
.RespondWith(Response.Create().WithStatusCode(204));
84+
85+
_client = new InfluxDBClient(new InfluxDBClientConfigs
86+
{
87+
HostUrl = MockServerUrl,
88+
Organization = "org",
89+
Database = "database",
90+
WriteOptions = new WriteOptions
91+
{
92+
GzipThreshold = 1
93+
}
94+
});
95+
96+
await _client.WriteRecordAsync("mem,tag=a field=1");
97+
var requests = MockServer.LogEntries.ToList();
98+
Assert.That(requests[0].RequestMessage.BodyData?.BodyAsString, Is.EqualTo("mem,tag=a field=1"));
99+
}
100+
101+
[Test]
102+
public async Task BodyDefaultNotGzipped()
103+
{
104+
MockServer
105+
.Given(Request.Create().WithPath("/api/v2/write").WithHeader("Content-Encoding", ".*", MatchBehaviour.RejectOnMatch).UsingPost())
106+
.RespondWith(Response.Create().WithStatusCode(204));
107+
108+
_client = new InfluxDBClient(MockServerUrl, null, "org", "database");
109+
110+
await _client.WriteRecordAsync("mem,tag=a field=1");
111+
var requests = MockServer.LogEntries.ToList();
112+
Assert.That(requests[0].RequestMessage.BodyData?.BodyAsString, Is.EqualTo("mem,tag=a field=1"));
113+
}
114+
77115
[Test]
78116
public void AlreadyDisposed()
79117
{
@@ -167,7 +205,10 @@ public async Task PrecisionOptions()
167205
HostUrl = MockServerUrl,
168206
Organization = "org",
169207
Database = "database",
170-
WritePrecision = WritePrecision.Ms
208+
WriteOptions = new WriteOptions
209+
{
210+
Precision = WritePrecision.Ms
211+
}
171212
});
172213
MockServer
173214
.Given(Request.Create().WithPath("/api/v2/write").UsingPost())

Client/Config/InfluxDBClientConfigs.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,6 @@ public string HostUrl
3838
/// </summary>
3939
public string? Database { get; set; }
4040

41-
/// <summary>
42-
/// The default precision to use for the timestamp of points if no precision is specified in the write API call.
43-
/// </summary>
44-
public WritePrecision? WritePrecision { get; set; }
45-
4641
/// <summary>
4742
/// Timeout to wait before the HTTP request times out. Default to '10 seconds'.
4843
/// </summary>
@@ -58,11 +53,21 @@ public string HostUrl
5853
/// </summary>
5954
public bool DisableServerCertificateValidation { get; set; }
6055

56+
/// <summary>
57+
/// Write options.
58+
/// </summary>
59+
public WriteOptions? WriteOptions { get; set; }
60+
6161
internal void Validate()
6262
{
6363
if (string.IsNullOrEmpty(HostUrl))
6464
{
6565
throw new ArgumentException("The hostname or IP address of the InfluxDB server has to be defined.");
6666
}
6767
}
68+
69+
internal WritePrecision WritePrecision
70+
{
71+
get => WriteOptions != null ? WriteOptions.Precision ?? WritePrecision.Ns : WritePrecision.Ns;
72+
}
6873
}

Client/Config/WriteOptions.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using InfluxDB3.Client.Write;
2+
3+
namespace InfluxDB3.Client.Config;
4+
5+
/// <summary>
6+
/// The WriteOptions class holds the configuration for writing data to InfluxDB.
7+
///
8+
/// You can configure following options:
9+
/// - Precision: The default precision to use for the timestamp of points if no precision is specified in the write API call.
10+
/// - GzipThreshold: The threshold in bytes for gzipping the body. The default value is 1000.
11+
///
12+
/// If you want create client with custom options, you can use the following code:
13+
/// <code>
14+
/// using var client = new InfluxDBClient(new InfluxDBClientConfigs{
15+
/// HostUrl = "https://us-east-1-1.aws.cloud2.influxdata.com",
16+
/// Organization = "my-org",
17+
/// Database = "my-database",
18+
/// WriteOptions = new WriteOptions
19+
/// {
20+
/// Precision = WritePrecision.S,
21+
/// GzipThreshold = 4096
22+
/// }
23+
/// });
24+
/// </code>
25+
/// </summary>
26+
public class WriteOptions
27+
{
28+
/// <summary>
29+
/// The default precision to use for the timestamp of points if no precision is specified in the write API call.
30+
/// </summary>
31+
public WritePrecision? Precision { get; set; }
32+
33+
/// <summary>
34+
/// The threshold in bytes for gzipping the body.
35+
/// </summary>
36+
public int GzipThreshold { get; set; }
37+
38+
internal static readonly WriteOptions DefaultOptions = new()
39+
{
40+
Precision = WritePrecision.Ns,
41+
GzipThreshold = 1000
42+
};
43+
}

Client/InfluxDBClient.cs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ public class InfluxDBClient : IInfluxDBClient
9090
private readonly HttpClient _httpClient;
9191
private readonly FlightSqlClient _flightSqlClient;
9292
private readonly RestClient _restClient;
93+
private readonly GzipHandler _gzipHandler;
9394

9495
/// <summary>
9596
/// This class provides an interface for interacting with an InfluxDB server,
@@ -107,6 +108,7 @@ public InfluxDBClient(string hostUrl, string? authToken = null, string? organiza
107108
Organization = organization,
108109
Database = database,
109110
AuthToken = authToken,
111+
WriteOptions = WriteOptions.DefaultOptions
110112
})
111113
{
112114
}
@@ -129,6 +131,7 @@ public InfluxDBClient(InfluxDBClientConfigs configs)
129131
_httpClient = CreateAndConfigureHttpClient(_configs);
130132
_flightSqlClient = new FlightSqlClient(configs: _configs, httpClient: _httpClient);
131133
_restClient = new RestClient(configs: _configs, httpClient: _httpClient);
134+
_gzipHandler = new GzipHandler(configs.WriteOptions != null ? configs.WriteOptions.GzipThreshold : 0);
132135
}
133136

134137
/// <summary>
@@ -247,15 +250,16 @@ private async Task WriteData(IEnumerable<object> data, string? organization = nu
247250
throw new ObjectDisposedException(nameof(InfluxDBClient));
248251
}
249252

250-
var precisionNotNull = precision ?? _configs.WritePrecision ?? WritePrecision.Ns;
253+
var precisionNotNull = precision ?? _configs.WritePrecision;
251254
var sb = ToLineProtocolBody(data, precisionNotNull);
252255
if (sb.Length == 0)
253256
{
254257
Trace.WriteLine($"The writes: {data} doesn't contains any Line Protocol, skipping");
255258
return;
256259
}
257260

258-
var content = new StringContent(sb.ToString(), Encoding.UTF8, "text/plain");
261+
var body = sb.ToString();
262+
var content = _gzipHandler.Process(body) ?? new StringContent(body, Encoding.UTF8, "text/plain");
259263
var queryParams = new Dictionary<string, string?>()
260264
{
261265
{
@@ -314,19 +318,24 @@ private static StringBuilder ToLineProtocolBody(IEnumerable<object?> data, Write
314318

315319
internal static HttpClient CreateAndConfigureHttpClient(InfluxDBClientConfigs configs)
316320
{
317-
var handler = new HttpClientHandler
321+
var handler = new HttpClientHandler();
322+
if (handler.SupportsRedirectConfiguration)
318323
{
319-
AllowAutoRedirect = configs.AllowHttpRedirects
320-
};
321-
324+
handler.AllowAutoRedirect = configs.AllowHttpRedirects;
325+
}
326+
if (handler.SupportsAutomaticDecompression)
327+
{
328+
handler.AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate;
329+
}
322330
if (configs.DisableServerCertificateValidation)
323331
{
324332
handler.ServerCertificateCustomValidationCallback = (_, _, _, _) => true;
325333
}
326334

327-
var client = new HttpClient(handler);
328-
329-
client.Timeout = configs.Timeout;
335+
var client = new HttpClient(handler)
336+
{
337+
Timeout = configs.Timeout
338+
};
330339
client.DefaultRequestHeaders.UserAgent.ParseAdd($"influxdb3-csharp/{AssemblyHelper.GetVersion()}");
331340
if (!string.IsNullOrEmpty(configs.AuthToken))
332341
{

Client/Internal/GzipHandler.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using System.IO;
3+
using System.IO.Compression;
4+
using System.Linq;
5+
using System.Net.Http;
6+
using System.Text;
7+
8+
namespace InfluxDB3.Client.Internal;
9+
10+
internal class GzipHandler
11+
{
12+
private readonly int _threshold;
13+
14+
public GzipHandler(int threshold)
15+
{
16+
_threshold = threshold;
17+
}
18+
19+
public HttpContent? Process(string body)
20+
{
21+
if (_threshold > 0 && body.Length < _threshold)
22+
{
23+
return null;
24+
}
25+
26+
using (var msi = new MemoryStream(Encoding.UTF8.GetBytes(body)))
27+
using (var mso = new MemoryStream())
28+
{
29+
using (var gs = new GZipStream(mso, CompressionMode.Compress))
30+
{
31+
msi.CopyTo(gs);
32+
gs.Flush();
33+
}
34+
35+
var content = new ByteArrayContent(mso.ToArray());
36+
content.Headers.Add("Content-Type", "text/plain; charset=utf-8");
37+
content.Headers.Add("Content-Encoding", "gzip");
38+
return content;
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)