Skip to content

Commit 57d528a

Browse files
Fluf22millotp
andauthored
feat(clients): endpoint level timeout part 2 [skip-bc] (#4318)
Co-authored-by: Pierre Millot <[email protected]>
1 parent 487257a commit 57d528a

File tree

67 files changed

+544
-122
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+544
-122
lines changed

clients/algoliasearch-client-csharp/algoliasearch/Http/InternalRequestOptions.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ public InternalRequestOptions(RequestOptions options = null)
2525

2626
CustomPathParameters = new Dictionary<string, string>();
2727
PathParameters = new Dictionary<string, string>();
28-
Timeout = options?.Timeout;
28+
ConnectTimeout = options?.ConnectTimeout;
29+
ReadTimeout = options?.ReadTimeout;
30+
WriteTimeout = options?.WriteTimeout;
2931
}
3032

3133
public void AddQueryParameter(string key, object value)
@@ -76,9 +78,19 @@ public void AddCustomQueryParameters(IDictionary<string, object> values)
7678
public object Data { get; set; }
7779

7880
/// <summary>
79-
/// Request timeout
81+
/// Request read timeout
8082
/// </summary>
81-
public TimeSpan? Timeout { get; set; }
83+
public TimeSpan? ReadTimeout { get; set; }
84+
85+
/// <summary>
86+
/// Request write timeout
87+
/// </summary>
88+
public TimeSpan? WriteTimeout { get; set; }
89+
90+
/// <summary>
91+
/// Request connect timeout
92+
/// </summary>
93+
public TimeSpan? ConnectTimeout { get; set; }
8294

8395
/// <summary>
8496
/// Enforce the Read Transporter

clients/algoliasearch-client-csharp/algoliasearch/Http/RequestOptionBuilder.cs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,35 @@ public RequestOptionBuilder AddExtraQueryParameters(string key, object value)
4747
}
4848

4949
/// <summary>
50-
/// Set the request timeout
50+
/// Set the request read timeout
5151
/// </summary>
5252
/// <param name="timeout"></param>
5353
/// <returns></returns>
54-
public RequestOptionBuilder SetTimeout(TimeSpan timeout)
54+
public RequestOptionBuilder SetReadTimeout(TimeSpan timeout)
5555
{
56-
_options.Timeout = timeout;
56+
_options.ReadTimeout = timeout;
57+
return this;
58+
}
59+
60+
/// <summary>
61+
/// Set the request write timeout
62+
/// </summary>
63+
/// <param name="timeout"></param>
64+
/// <returns></returns>
65+
public RequestOptionBuilder SetWriteTimeout(TimeSpan timeout)
66+
{
67+
_options.WriteTimeout = timeout;
68+
return this;
69+
}
70+
71+
/// <summary>
72+
/// Set the request connect timeout
73+
/// </summary>
74+
/// <param name="timeout"></param>
75+
/// <returns></returns>
76+
public RequestOptionBuilder SetConnectTimeout(TimeSpan timeout)
77+
{
78+
_options.ConnectTimeout = timeout;
5779
return this;
5880
}
5981

clients/algoliasearch-client-csharp/algoliasearch/Http/RequestOptions.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,19 @@ public class RequestOptions
2020
public IDictionary<string, object> QueryParameters { get; set; }
2121

2222
/// <summary>
23-
/// Request timeout in seconds
23+
/// Request timeout
2424
/// </summary>
25-
public TimeSpan? Timeout { get; set; }
25+
public TimeSpan? ReadTimeout { get; set; }
26+
27+
/// <summary>
28+
/// Request timeout
29+
/// </summary>
30+
public TimeSpan? WriteTimeout { get; set; }
31+
32+
/// <summary>
33+
/// Request timeout
34+
/// </summary>
35+
public TimeSpan? ConnectTimeout { get; set; }
2636

2737
/// <summary>
2838
/// Constructs a new instance of <see cref="RequestOptions"/>

clients/algoliasearch-client-csharp/algoliasearch/Transport/HttpTransport.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ private async Task<TResult> ExecuteRequestAsync<TResult, TData>(HttpMethod metho
119119
request.Uri = BuildUri(host, uri, requestOptions?.CustomPathParameters, requestOptions?.PathParameters,
120120
requestOptions?.QueryParameters);
121121
var requestTimeout =
122-
TimeSpan.FromTicks((requestOptions?.Timeout ?? GetTimeOut(callType)).Ticks * (host.RetryCount + 1));
122+
TimeSpan.FromTicks((GetTimeOut(callType, requestOptions)).Ticks * (host.RetryCount + 1));
123123

124124
if (request.Body == null && (method == HttpMethod.Post || method == HttpMethod.Put))
125125
{
@@ -137,7 +137,7 @@ private async Task<TResult> ExecuteRequestAsync<TResult, TData>(HttpMethod metho
137137
}
138138

139139
var response = await _httpClient
140-
.SendRequestAsync(request, requestTimeout, _algoliaConfig.ConnectTimeout ?? Defaults.ConnectTimeout, ct)
140+
.SendRequestAsync(request, requestTimeout, requestOptions?.ConnectTimeout ?? _algoliaConfig.ConnectTimeout ?? Defaults.ConnectTimeout, ct)
141141
.ConfigureAwait(false);
142142

143143
_errorMessage = response.Error;
@@ -280,13 +280,14 @@ private static Uri BuildUri(StatefulHost host, string baseUri,
280280
/// Compute the request timeout with the given call type and configuration
281281
/// </summary>
282282
/// <param name="callType"></param>
283+
/// <param name="requestOptions"></param>
283284
/// <returns></returns>
284-
private TimeSpan GetTimeOut(CallType callType)
285+
private TimeSpan GetTimeOut(CallType callType, InternalRequestOptions requestOptions = null)
285286
{
286287
return callType switch
287288
{
288-
CallType.Read => _algoliaConfig.ReadTimeout ?? Defaults.ReadTimeout,
289-
CallType.Write => _algoliaConfig.WriteTimeout ?? Defaults.WriteTimeout,
289+
CallType.Read => requestOptions?.ReadTimeout ?? _algoliaConfig.ReadTimeout ?? Defaults.ReadTimeout,
290+
CallType.Write => requestOptions?.WriteTimeout ?? _algoliaConfig.WriteTimeout ?? Defaults.WriteTimeout,
290291
_ => Defaults.WriteTimeout
291292
};
292293
}

clients/algoliasearch-client-dart/packages/client_core/lib/src/transport/dio/dio_requester.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,12 @@ class DioRequester implements Requester {
121121
void setClientApiKey(String apiKey) {
122122
_authInterceptor.apiKey = apiKey;
123123
}
124+
125+
@override
126+
get connectTimeout => _client.options.connectTimeout;
127+
128+
@override
129+
void setConnectTimeout(Duration connectTimeout) {
130+
_client.options.connectTimeout = connectTimeout;
131+
}
124132
}

clients/algoliasearch-client-dart/packages/client_core/lib/src/transport/request_options.dart

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
/// Represents options for configuring a request to an endpoint.
22
final class RequestOptions {
3-
/// The write timeout for the request in milliseconds.
3+
/// The write timeout for the request.
44
final Duration? writeTimeout;
55

6-
/// The read timeout for the request in milliseconds.
6+
/// The read timeout for the request.
77
final Duration? readTimeout;
88

9+
/// The connect timeout for the request.
10+
final Duration? connectTimeout;
11+
912
/// Header names to their respective values to be sent with the request.
1013
final Map<String, dynamic> headers;
1114

@@ -18,18 +21,35 @@ final class RequestOptions {
1821
const RequestOptions({
1922
this.writeTimeout,
2023
this.readTimeout,
24+
this.connectTimeout,
2125
this.headers = const {},
2226
this.urlParameters = const {},
2327
this.body,
2428
});
2529

30+
RequestOptions operator +(RequestOptions? other) {
31+
if (other == null) {
32+
return this;
33+
}
34+
35+
return RequestOptions(
36+
writeTimeout: other.writeTimeout ?? writeTimeout,
37+
readTimeout: other.readTimeout ?? readTimeout,
38+
connectTimeout: other.connectTimeout ?? connectTimeout,
39+
headers: {...headers, ...other.headers},
40+
urlParameters: {...urlParameters, ...other.urlParameters},
41+
body: other.body ?? body,
42+
);
43+
}
44+
2645
@override
2746
bool operator ==(Object other) =>
2847
identical(this, other) ||
2948
other is RequestOptions &&
3049
runtimeType == other.runtimeType &&
3150
writeTimeout == other.writeTimeout &&
3251
readTimeout == other.readTimeout &&
52+
connectTimeout == other.connectTimeout &&
3353
headers == other.headers &&
3454
urlParameters == other.urlParameters &&
3555
body == other.body;
@@ -38,12 +58,13 @@ final class RequestOptions {
3858
int get hashCode =>
3959
writeTimeout.hashCode ^
4060
readTimeout.hashCode ^
61+
connectTimeout.hashCode ^
4162
headers.hashCode ^
4263
urlParameters.hashCode ^
4364
body.hashCode;
4465

4566
@override
4667
String toString() {
47-
return 'RequestOptions{writeTimeout: $writeTimeout, readTimeout: $readTimeout, headers: $headers, urlParameters: $urlParameters, body: $body}';
68+
return 'RequestOptions{writeTimeout: $writeTimeout, readTimeout: $readTimeout, connectTimeout: $connectTimeout, headers: $headers, urlParameters: $urlParameters, body: $body}';
4869
}
4970
}

clients/algoliasearch-client-dart/packages/client_core/lib/src/transport/requester.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ abstract class Requester {
1414
/// Allows to switch the API key used to authenticate requests.
1515
void setClientApiKey(String apiKey);
1616

17+
/// Allows to customise the connect timeout for the requester.
18+
get connectTimeout => null;
19+
void setConnectTimeout(Duration connectTimeout);
20+
1721
/// Closes any underlying resources that the Requester might be using.
1822
///
1923
/// By default, it does nothing (no-op), but it can be implemented to handle resource cleanup

clients/algoliasearch-client-dart/packages/client_core/lib/src/transport/retry_strategy.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,13 @@ final class RetryStrategy {
5353
final List<AlgoliaException> errors = [];
5454
for (final host in hosts) {
5555
final httpRequest = _buildRequest(host, request, callType, options);
56+
final requesterConnectTimeout = requester.connectTimeout;
57+
if (options?.connectTimeout != null) {
58+
requester.setConnectTimeout(options!.connectTimeout!);
59+
}
5660
try {
5761
final response = await requester.perform(httpRequest);
62+
requester.setConnectTimeout(requesterConnectTimeout);
5863
return response.body ?? const {};
5964
} on AlgoliaTimeoutException catch (e) {
6065
host.timedOut();

clients/algoliasearch-client-go/algolia/transport/configuration.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,9 @@ type Configuration struct {
2020
Compression compression.Compression
2121
ExposeIntermediateNetworkErrors bool
2222
}
23+
24+
type RequestConfiguration struct {
25+
ReadTimeout *time.Duration
26+
WriteTimeout *time.Duration
27+
ConnectTimeout *time.Duration
28+
}

clients/algoliasearch-client-go/algolia/transport/transport.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func New(cfg Configuration) *Transport {
4444
return transport
4545
}
4646

47-
func (t *Transport) Request(ctx context.Context, req *http.Request, k call.Kind) (*http.Response, []byte, error) {
47+
func (t *Transport) Request(ctx context.Context, req *http.Request, k call.Kind, c RequestConfiguration) (*http.Response, []byte, error) {
4848
var intermediateNetworkErrors []error
4949

5050
// Add Content-Encoding header, if needed
@@ -59,9 +59,29 @@ func (t *Transport) Request(ctx context.Context, req *http.Request, k call.Kind)
5959
// before the early returns, but when we do so, we do it **after**
6060
// reading the body content of the response. Otherwise, a `context
6161
// cancelled` error may happen when the body is read.
62-
perRequestCtx, cancel := context.WithTimeout(ctx, h.timeout)
62+
var (
63+
ctxTimeout time.Duration
64+
connectTimeout time.Duration
65+
)
66+
67+
switch {
68+
case k == call.Read && c.ReadTimeout != nil:
69+
ctxTimeout = *c.ReadTimeout
70+
case k == call.Write && c.WriteTimeout != nil:
71+
ctxTimeout = *c.WriteTimeout
72+
default:
73+
ctxTimeout = h.timeout
74+
}
75+
76+
if c.ConnectTimeout != nil {
77+
connectTimeout = *c.ConnectTimeout
78+
} else {
79+
connectTimeout = t.connectTimeout
80+
}
81+
82+
perRequestCtx, cancel := context.WithTimeout(ctx, ctxTimeout)
6383
req = req.WithContext(perRequestCtx)
64-
res, err := t.request(req, h, h.timeout, t.connectTimeout)
84+
res, err := t.request(req, h, ctxTimeout, connectTimeout)
6585

6686
code := 0
6787
if res != nil {

clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/ApiClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import java.util.concurrent.ExecutorService;
1818
import java.util.stream.Collectors;
1919
import javax.annotation.Nonnull;
20-
import org.jetbrains.annotations.Nullable;
20+
import javax.annotation.Nullable;
2121

2222
/**
2323
* Represents a base client for making API requests. The client uses a {@link Requester} for

clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/config/RequestOptions.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.time.Duration;
55
import java.util.HashMap;
66
import java.util.Map;
7+
import javax.annotation.Nullable;
78

89
/**
910
* Request options are used to pass extra parameters, headers, timeout to the request. Parameters
@@ -15,6 +16,7 @@ public final class RequestOptions {
1516
private final Map<String, String> queryParameters = new HashMap<>();
1617
private Duration readTimeout;
1718
private Duration writeTimeout;
19+
private Duration connectTimeout;
1820

1921
public RequestOptions addExtraHeader(String key, Object value) {
2022
if (value == null) return this;
@@ -62,6 +64,32 @@ public RequestOptions setWriteTimeout(Duration writeTimeout) {
6264
return this;
6365
}
6466

67+
public Duration getConnectTimeout() {
68+
return connectTimeout;
69+
}
70+
71+
public RequestOptions setConnectTimeout(Duration connectTimeout) {
72+
this.connectTimeout = connectTimeout;
73+
return this;
74+
}
75+
76+
// `this` will be merged in `other`. Values in `other` will take precedence over `this`'s.
77+
public RequestOptions mergeRight(@Nullable RequestOptions other) {
78+
if (other == null) {
79+
return this;
80+
}
81+
82+
RequestOptions requestOptions = new RequestOptions();
83+
requestOptions.headers.putAll(this.headers);
84+
requestOptions.headers.putAll(other.headers);
85+
requestOptions.queryParameters.putAll(this.queryParameters);
86+
requestOptions.queryParameters.putAll(other.queryParameters);
87+
requestOptions.readTimeout = other.readTimeout != null ? other.readTimeout : this.readTimeout;
88+
requestOptions.writeTimeout = other.writeTimeout != null ? other.writeTimeout : this.writeTimeout;
89+
requestOptions.connectTimeout = other.connectTimeout != null ? other.connectTimeout : this.connectTimeout;
90+
return requestOptions;
91+
}
92+
6593
@Override
6694
public String toString() {
6795
return (
@@ -74,6 +102,8 @@ public String toString() {
74102
readTimeout +
75103
", writeTimeout=" +
76104
writeTimeout +
105+
", connectTimeout=" +
106+
connectTimeout +
77107
'}'
78108
);
79109
}

clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/internal/HttpRequester.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ private OkHttpClient getOkHttpClient(RequestOptions requestOptions) {
172172
if (requestOptions.getWriteTimeout() != null) {
173173
builder.writeTimeout(requestOptions.getWriteTimeout());
174174
}
175+
if (requestOptions.getConnectTimeout() != null) {
176+
builder.connectTimeout(requestOptions.getConnectTimeout());
177+
}
175178
return builder.build();
176179
}
177180

0 commit comments

Comments
 (0)