Skip to content

Commit 3c8accd

Browse files
feat: simplify timeout settings (#175)
1 parent b0dbbd0 commit 3c8accd

File tree

9 files changed

+236
-93
lines changed

9 files changed

+236
-93
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### Features
44

55
1. [#174](https://github.com/InfluxCommunity/influxdb3-csharp/pull/174): Support passing HttpClient to InfluxDBClient.
6+
1. [#175](https://github.com/InfluxCommunity/influxdb3-csharp/pull/175): Add QueryTimeout and WriteTimeout to ClientConfig.
67

78
### CI
89

Client.Test.Integration/QueryWriteTest.cs

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,22 +169,92 @@ public async Task MaxReceiveMessageSize()
169169
}
170170

171171
[Test]
172-
public void GrpcDeadline()
172+
public async Task TimeoutExceededByDeadline()
173173
{
174174
using var client = new InfluxDBClient(new ClientConfig
175175
{
176176
Host = Host,
177177
Token = Token,
178178
Database = Database,
179+
WriteTimeout = TimeSpan.FromSeconds(11),
180+
QueryTimeout = TimeSpan.FromSeconds(11),
179181
QueryOptions = new QueryOptions()
180182
{
181-
Deadline = DateTime.UtcNow.AddMicroseconds(1)
183+
Deadline = DateTime.UtcNow.AddTicks(1) // Deadline will have a higher priority than QueryTimeout
182184
}
183185
});
186+
await client.WriteRecordAsync("mem,tag=a field=1");
187+
TestQuery(client);
188+
TestQueryBatches(client);
189+
TestQueryPoints(client);
190+
}
191+
192+
[Test]
193+
public async Task TimeoutExceededByQueryTimeout()
194+
{
195+
using var client = new InfluxDBClient(new ClientConfig
196+
{
197+
Host = Host,
198+
Token = Token,
199+
Database = Database,
200+
WriteTimeout = TimeSpan.FromSeconds(11),
201+
QueryTimeout = TimeSpan.FromTicks(1)
202+
});
203+
await client.WriteRecordAsync("mem,tag=a field=1");
204+
TestQuery(client);
205+
TestQueryBatches(client);
206+
TestQueryPoints(client);
207+
}
208+
209+
[Test]
210+
public async Task TimeoutExceeded()
211+
{
212+
using var client = new InfluxDBClient(new ClientConfig
213+
{
214+
Host = Host,
215+
Token = Token,
216+
Database = Database,
217+
WriteTimeout = TimeSpan.FromSeconds(11),
218+
QueryTimeout = TimeSpan.FromSeconds(11),
219+
QueryOptions =
220+
{
221+
Deadline = DateTime.UtcNow.AddSeconds(11),
222+
}
223+
});
224+
await client.WriteRecordAsync("mem,tag=a field=1");
225+
var timeout = TimeSpan.FromTicks(1);
226+
TestQuery(client, timeout);
227+
TestQueryBatches(client, timeout);
228+
TestQueryPoints(client, timeout);
229+
}
230+
231+
private static void TestQuery(InfluxDBClient client, TimeSpan? timeout = null)
232+
{
233+
var ex = Assert.ThrowsAsync<RpcException>(async () =>
234+
{
235+
await foreach (var _ in client.Query("SELECT * FROM mem", timeout: timeout))
236+
{
237+
}
238+
});
239+
Assert.That(ex.StatusCode, Is.EqualTo(StatusCode.DeadlineExceeded));
240+
}
184241

242+
private static void TestQueryBatches(InfluxDBClient client, TimeSpan? timeout = null)
243+
{
244+
var ex = Assert.ThrowsAsync<RpcException>(async () =>
245+
{
246+
await foreach (var _ in client.QueryBatches("SELECT * FROM mem", timeout: timeout))
247+
{
248+
}
249+
});
250+
Assert.That(ex.StatusCode, Is.EqualTo(StatusCode.DeadlineExceeded));
251+
}
252+
253+
private static void TestQueryPoints(InfluxDBClient client, TimeSpan? timeout = null)
254+
{
185255
var ex = Assert.ThrowsAsync<RpcException>(async () =>
186256
{
187-
await foreach (var _ in client.Query("SELECT value FROM stat"))
257+
await foreach (var _ in client.QueryPoints("SELECT * FROM mem", timeout: timeout))
188258
{
189259
}
190260
});

Client.Test.Integration/WriteTest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public async Task WriteWithError()
3333
Assert.That(iaex.Message,
3434
Does.Contain("Found trailing content")
3535
.Or.Contain("partial write of line protocol occurred")
36+
.Or.Contain("write buffer error: parsing for line protocol failed")
3637
);
3738
Assert.That(iaex.StatusCode.ToString(), Is.EqualTo("BadRequest"));
3839
Assert.That(iaex.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));

Client.Test/InfluxDBClientQueryTest.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public async Task PassNamedParametersToFlightClient()
5454
var mockFlightSqlClient = new Mock<IFlightSqlClient>();
5555
mockFlightSqlClient
5656
.Setup(m => m.Execute(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<QueryType>(),
57-
It.IsAny<Dictionary<string, object>>(), It.IsAny<Dictionary<string, string>>()))
57+
It.IsAny<Dictionary<string, object>>(), It.IsAny<Dictionary<string, string>>(), It.IsAny<TimeSpan>()))
5858
.Returns(new List<RecordBatch>().ToAsyncEnumerable());
5959

6060
//
@@ -74,9 +74,9 @@ public async Task PassNamedParametersToFlightClient()
7474
{ "max-frequency", 3.5 }
7575
};
7676

77-
_ = await _client.QueryPoints(query, database: "my-db", queryType: queryType, namedParameters: namedParameters)
77+
_ = await _client.QueryPoints(query, database: "my-db", queryType: queryType, namedParameters: namedParameters, timeout: TimeSpan.MaxValue)
7878
.ToListAsync();
79-
mockFlightSqlClient.Verify(m => m.Execute(query, "my-db", queryType, namedParameters, new Dictionary<string, string>()), Times.Exactly(1));
79+
mockFlightSqlClient.Verify(m => m.Execute(query, "my-db", queryType, namedParameters, new Dictionary<string, string>(), TimeSpan.MaxValue), Times.Exactly(1));
8080
}
8181

8282
[Test]
@@ -109,7 +109,7 @@ public async Task PassHeadersToFlightClient()
109109
var mockFlightSqlClient = new Mock<IFlightSqlClient>();
110110
mockFlightSqlClient
111111
.Setup(m => m.Execute(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<QueryType>(),
112-
It.IsAny<Dictionary<string, object>>(), It.IsAny<Dictionary<string, string>>()))
112+
It.IsAny<Dictionary<string, object>>(), It.IsAny<Dictionary<string, string>>(), It.IsAny<TimeSpan?>()))
113113
.Returns(new List<RecordBatch>().ToAsyncEnumerable());
114114

115115
//
@@ -128,6 +128,6 @@ public async Task PassHeadersToFlightClient()
128128
}};
129129
_ = await _client.QueryPoints(query, database: "my-db", queryType: queryType, headers: headers)
130130
.ToListAsync();
131-
mockFlightSqlClient.Verify(m => m.Execute(query, "my-db", queryType, new Dictionary<string, object>(), headers), Times.Exactly(1));
131+
mockFlightSqlClient.Verify(m => m.Execute(query, "my-db", queryType, new Dictionary<string, object>(), headers, null), Times.Exactly(1));
132132
}
133133
}

Client.Test/InfluxDBClientWriteTest.cs

Lines changed: 85 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Net;
5-
using System.Net.Http;
5+
using System.Threading;
66
using System.Threading.Tasks;
77
using InfluxDB3.Client.Config;
88
using InfluxDB3.Client.Write;
99
using WireMock.Matchers;
1010
using WireMock.RequestBuilders;
1111
using WireMock.ResponseBuilders;
12+
using WriteOptions = InfluxDB3.Client.Config.WriteOptions;
1213

1314
namespace InfluxDB3.Client.Test;
1415

@@ -493,55 +494,113 @@ public void WriteNoSyncTrueNotSupported()
493494
}
494495

495496
[Test]
496-
public async Task TestSetHttpClient()
497+
public void TimeoutExceededByTimeout()
497498
{
498499
MockServer
499500
.Given(Request.Create().WithPath("/api/v2/write").UsingPost())
500-
.RespondWith(Response.Create().WithStatusCode(HttpStatusCode.OK));
501-
502-
var httpClient = new HttpClient();
503-
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("my-user-agent");
504-
httpClient.DefaultRequestHeaders.Add("X-Client-ID", "123");
501+
.RespondWith(Response.Create().WithStatusCode(204).WithDelay(TimeSpan.FromSeconds(2)));
505502

506503
_client = new InfluxDBClient(new ClientConfig
507504
{
508505
Host = MockServerUrl,
509506
Token = "my-token",
510507
Database = "my-database",
511-
HttpClient = httpClient
508+
Timeout = TimeSpan.FromTicks(1)
512509
});
510+
TestWriteRecordAsync(_client);
511+
TestWriteRecordsAsync(_client);
512+
TestWritePointAsync(_client);
513+
TestWritePointsAsync(_client);
514+
}
513515

514-
await _client.WriteRecordAsync("mem,tag=a field=1");
515-
var requests = MockServer.LogEntries.ToList();
516-
using (Assert.EnterMultipleScope())
516+
[Test]
517+
public void TimeoutExceededByWriteTimeout()
518+
{
519+
MockServer
520+
.Given(Request.Create().WithPath("/api/v2/write").UsingPost())
521+
.RespondWith(Response.Create().WithStatusCode(204).WithDelay(TimeSpan.FromSeconds(2)));
522+
523+
_client = new InfluxDBClient(new ClientConfig
517524
{
518-
Assert.That(requests[0].RequestMessage.Headers?["User-Agent"].First(), Is.EqualTo("my-user-agent"));
519-
Assert.That(requests[0].RequestMessage.Headers["X-Client-ID"].First(), Is.EqualTo("123"));
520-
}
521-
Assert.Pass();
525+
Host = MockServerUrl,
526+
Token = "my-token",
527+
Database = "my-database",
528+
QueryTimeout = TimeSpan.FromSeconds(11),
529+
Timeout = TimeSpan.FromSeconds(11),
530+
WriteTimeout = TimeSpan.FromTicks(1) // WriteTimeout has a higher priority than Timeout
531+
});
532+
TestWriteRecordAsync(_client);
533+
TestWriteRecordsAsync(_client);
534+
TestWritePointAsync(_client);
535+
TestWritePointsAsync(_client);
536+
522537
}
523538

524539
[Test]
525-
public void TestCheckHttpClientStillOpen()
540+
public void TimeoutExceededByToken()
526541
{
527542
MockServer
528-
.Given(Request.Create().WithPath("/test").UsingGet())
529-
.RespondWith(
530-
Response.Create()
531-
.WithStatusCode(HttpStatusCode.OK)
532-
.WithBody("Still ok"));
543+
.Given(Request.Create().WithPath("/api/v2/write").UsingPost())
544+
.RespondWith(Response.Create().WithStatusCode(204).WithDelay(TimeSpan.FromSeconds(2)));
533545

534-
var httpClient = new HttpClient(new HttpClientHandler());
535546
_client = new InfluxDBClient(new ClientConfig
536547
{
537548
Host = MockServerUrl,
538549
Token = "my-token",
539550
Database = "my-database",
540-
HttpClient = httpClient
551+
QueryTimeout = TimeSpan.FromSeconds(11),
552+
Timeout = TimeSpan.FromSeconds(11),
553+
WriteTimeout = TimeSpan.FromSeconds(11)
541554
});
542-
_client.Dispose();
555+
var cancellationToken = new CancellationTokenSource(TimeSpan.FromTicks(1)).Token;
556+
TestWriteRecordAsync(_client, cancellationToken);
557+
TestWriteRecordsAsync(_client, cancellationToken);
558+
TestWritePointAsync(_client, cancellationToken);
559+
TestWritePointsAsync(_client, cancellationToken);
560+
}
543561

544-
var httpResponseMessage = httpClient.Send(new HttpRequestMessage(HttpMethod.Get, "test"));
545-
Assert.That(httpResponseMessage.Content.ReadAsStringAsync().Result, Is.EqualTo("Still ok"));
562+
private static void TestWriteRecordAsync(InfluxDBClient client, CancellationToken? cancellationToken = null)
563+
{
564+
Assert.ThrowsAsync<TaskCanceledException>(async () =>
565+
{
566+
await client.WriteRecordAsync("mem,tag=a field=1", cancellationToken: cancellationToken ?? CancellationToken.None);
567+
});
568+
}
569+
570+
private static void TestWriteRecordsAsync(InfluxDBClient client, CancellationToken? cancellationToken = null)
571+
{
572+
Assert.ThrowsAsync<TaskCanceledException>(async () =>
573+
{
574+
await client.WriteRecordsAsync(
575+
records: new[] { "stat,unit=temperature value=24.5", "stat,unit=temperature value=25.5" },
576+
cancellationToken: cancellationToken ?? CancellationToken.None
577+
);
578+
});
579+
}
580+
581+
private static void TestWritePointAsync(InfluxDBClient client, CancellationToken? cancellationToken = null)
582+
{
583+
Assert.ThrowsAsync<TaskCanceledException>(async () =>
584+
{
585+
await client.WritePointAsync(
586+
PointData.Measurement("h2o").SetTag("location", "europe").SetField("level", 2),
587+
cancellationToken: cancellationToken ?? CancellationToken.None
588+
);
589+
});
590+
}
591+
592+
private static void TestWritePointsAsync(InfluxDBClient client, CancellationToken? cancellationToken = null)
593+
{
594+
Assert.ThrowsAsync<TaskCanceledException>(async () =>
595+
{
596+
await client.WritePointsAsync(
597+
points: new[]
598+
{
599+
PointData.Measurement("h2o").SetTag("location", "europe").SetField("level", 2),
600+
PointData.Measurement("h2o").SetTag("location", "us-west").SetField("level", 4),
601+
},
602+
cancellationToken: cancellationToken ?? CancellationToken.None
603+
);
604+
});
546605
}
547606
}

Client.Test/Internal/RestClientTest.cs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -279,19 +279,6 @@ public void AllowHttpRedirects()
279279
Assert.That(_client, Is.Not.Null);
280280
}
281281

282-
[Test]
283-
public void Timeout()
284-
{
285-
CreateAndConfigureRestClient(new ClientConfig
286-
{
287-
Host = MockServerUrl,
288-
Timeout = TimeSpan.FromSeconds(45)
289-
});
290-
291-
var httpClient = GetDeclaredField<HttpClient>(_client.GetType(), _client, "_httpClient");
292-
Assert.That(httpClient.Timeout, Is.EqualTo(TimeSpan.FromSeconds(45)));
293-
}
294-
295282
private void CreateAndConfigureRestClient(ClientConfig config)
296283
{
297284
_httpClient = InfluxDBClient.CreateOrGetHttpClient(config);

Client/Config/ClientConfig.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ namespace InfluxDB3.Client.Config;
1919
/// <item>- Organization: The organization to be used for operations.</item>
2020
/// <item>- Database: The database to be used for InfluxDB operations.</item>
2121
/// <item>- Headers: The set of HTTP headers to be included in requests.</item>
22-
/// <item>- Timeout: Timeout to wait before the HTTP request times out. Default to '10 seconds'.</item>
22+
/// <item>- [Deprecated] Timeout: Timeout to wait before the HTTP request times out. Default to '10 seconds'.</item>
23+
/// <item>- QueryTimeout: The maximum duration to wait for a query to complete before timing out.</item>
24+
/// <item>- WriteTimeout: The duration to wait before timing out a write operation to the InfluxDB server.</item>
2325
/// <item>- AllowHttpRedirects: Automatically following HTTP 3xx redirects. Default to 'false'.</item>
2426
/// <item>- DisableServerCertificateValidation: Disable server SSL certificate validation. Default to 'false'.</item>
2527
/// <item>- DisableCertificateRevocationListCheck: Disable SSL certificate revocation list (CRL) checking. Default to 'false'.</item>
@@ -165,8 +167,19 @@ public string Host
165167
/// <summary>
166168
/// Timeout to wait before the HTTP request times out. Default to '10 seconds'.
167169
/// </summary>
170+
[Obsolete("Please use more informative properties like WriteTimeout or QueryTimeout")]
168171
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(10);
169172

173+
/// <summary>
174+
/// The maximum duration to wait for a query to complete before timing out.
175+
/// </summary>
176+
public TimeSpan? QueryTimeout { get; set; }
177+
178+
/// <summary>
179+
/// The duration to wait before timing out a write operation to the InfluxDB server.
180+
/// </summary>
181+
public TimeSpan? WriteTimeout { get; set; }
182+
170183
/// <summary>
171184
/// Automatically following HTTP 3xx redirects. Default to 'false'.
172185
/// </summary>

0 commit comments

Comments
 (0)