Skip to content

Commit 86a1ab5

Browse files
authored
Replace RequestData with BoundConfiguration (#141)
This PR involves a comprehensive refactor to replace the `RequestData` class with a new class named `BoundConfiguration` across the codebase. The `BoundConfiguration` class encapsulates configuration details and implements the `IRequestConfiguration` interface, which avoids introducing new overloads of the `Request` \ `RequestAsync` methods. We type check in the transport, and when the provided `IRequestConfiguration` is a `BoundConfiguration`, we use that without rebinding. Closes #138
1 parent dbd958b commit 86a1ab5

File tree

47 files changed

+550
-514
lines changed

Some content is hidden

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

47 files changed

+550
-514
lines changed

src/Elastic.Transport.VirtualizedCluster/Components/ExposingPipelineFactory.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ public ExposingPipelineFactory(TConfiguration configuration)
2020
private TConfiguration Configuration { get; }
2121
public ITransport<TConfiguration> Transport { get; }
2222

23-
public override RequestPipeline Create(RequestData requestData) =>
24-
new RequestPipeline(requestData);
23+
public override RequestPipeline Create(BoundConfiguration boundConfiguration) => new(boundConfiguration);
2524
}
2625
#nullable restore

src/Elastic.Transport.VirtualizedCluster/Components/VirtualClusterConnection.cs

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,12 @@ private void UpdateCluster(VirtualCluster cluster)
119119
private bool IsPingRequest(Endpoint endpoint) => _productRegistration.IsPingRequest(endpoint);
120120

121121
/// <inheritdoc cref="IRequestInvoker.RequestAsync{TResponse}"/>>
122-
public Task<TResponse> RequestAsync<TResponse>(Endpoint endpoint, RequestData requestData, PostData? postData, CancellationToken cancellationToken)
122+
public Task<TResponse> RequestAsync<TResponse>(Endpoint endpoint, BoundConfiguration boundConfiguration, PostData? postData, CancellationToken cancellationToken)
123123
where TResponse : TransportResponse, new() =>
124-
Task.FromResult(Request<TResponse>(endpoint, requestData, postData));
124+
Task.FromResult(Request<TResponse>(endpoint, boundConfiguration, postData));
125125

126126
/// <inheritdoc cref="IRequestInvoker.Request{TResponse}"/>>
127-
public TResponse Request<TResponse>(Endpoint endpoint, RequestData requestData, PostData? postData)
127+
public TResponse Request<TResponse>(Endpoint endpoint, BoundConfiguration boundConfiguration, PostData? postData)
128128
where TResponse : TransportResponse, new()
129129
{
130130
if (!_calls.ContainsKey(endpoint.Uri.Port))
@@ -138,11 +138,11 @@ public TResponse Request<TResponse>(Endpoint endpoint, RequestData requestData,
138138
_ = Interlocked.Increment(ref state.Sniffed);
139139
return HandleRules<TResponse, ISniffRule>(
140140
endpoint,
141-
requestData,
141+
boundConfiguration,
142142
postData,
143143
nameof(VirtualCluster.Sniff),
144144
_cluster.SniffingRules,
145-
requestData.RequestTimeout,
145+
boundConfiguration.RequestTimeout,
146146
(r) => UpdateCluster(r.NewClusterState),
147147
(r) => _productRegistration.CreateSniffResponseBytes(_cluster.Nodes, _cluster.ElasticsearchVersion, _cluster.PublishAddressOverride, _cluster.SniffShouldReturnFqnd)
148148
);
@@ -152,36 +152,36 @@ public TResponse Request<TResponse>(Endpoint endpoint, RequestData requestData,
152152
_ = Interlocked.Increment(ref state.Pinged);
153153
return HandleRules<TResponse, IRule>(
154154
endpoint,
155-
requestData,
155+
boundConfiguration,
156156
postData,
157157
nameof(VirtualCluster.Ping),
158158
_cluster.PingingRules,
159-
requestData.PingTimeout,
159+
boundConfiguration.PingTimeout,
160160
(r) => { },
161161
(r) => null //HEAD request
162162
);
163163
}
164164
_ = Interlocked.Increment(ref state.Called);
165165
return HandleRules<TResponse, IClientCallRule>(
166166
endpoint,
167-
requestData,
167+
boundConfiguration,
168168
postData,
169169
nameof(VirtualCluster.ClientCalls),
170170
_cluster.ClientCallRules,
171-
requestData.RequestTimeout,
171+
boundConfiguration.RequestTimeout,
172172
(r) => { },
173173
CallResponse
174174
);
175175
}
176176
catch (TheException e)
177177
{
178-
return ResponseFactory.Create<TResponse>(endpoint, requestData, postData, e, null, null, Stream.Null, null, -1, null, null);
178+
return ResponseFactory.Create<TResponse>(endpoint, boundConfiguration, postData, e, null, null, Stream.Null, null, -1, null, null);
179179
}
180180
}
181181

182182
private TResponse HandleRules<TResponse, TRule>(
183183
Endpoint endpoint,
184-
RequestData requestData,
184+
BoundConfiguration boundConfiguration,
185185
PostData? postData,
186186
string origin,
187187
IList<TRule> rules,
@@ -203,28 +203,28 @@ private TResponse HandleRules<TResponse, TRule>(
203203
if (rule.OnPort == null || rule.OnPort.Value != endpoint.Uri.Port) continue;
204204

205205
if (always)
206-
return Always<TResponse, TRule>(endpoint, requestData, postData, timeout, beforeReturn, successResponse, rule);
206+
return Always<TResponse, TRule>(endpoint, boundConfiguration, postData, timeout, beforeReturn, successResponse, rule);
207207

208208
if (rule.ExecuteCount > times) continue;
209209

210-
return Sometimes<TResponse, TRule>(endpoint, requestData, postData, timeout, beforeReturn, successResponse, rule);
210+
return Sometimes<TResponse, TRule>(endpoint, boundConfiguration, postData, timeout, beforeReturn, successResponse, rule);
211211
}
212212
foreach (var rule in rules.Where(s => !s.OnPort.HasValue))
213213
{
214214
var always = rule.Times.Match(t => true, t => false);
215215
var times = rule.Times.Match(t => -1, t => t);
216216
if (always)
217-
return Always<TResponse, TRule>(endpoint, requestData, postData, timeout, beforeReturn, successResponse, rule);
217+
return Always<TResponse, TRule>(endpoint, boundConfiguration, postData, timeout, beforeReturn, successResponse, rule);
218218

219219
if (rule.ExecuteCount > times) continue;
220220

221-
return Sometimes<TResponse, TRule>(endpoint, requestData, postData, timeout, beforeReturn, successResponse, rule);
221+
return Sometimes<TResponse, TRule>(endpoint, boundConfiguration, postData, timeout, beforeReturn, successResponse, rule);
222222
}
223223
var count = _calls.Select(kv => kv.Value.Called).Sum();
224224
throw new Exception($@"No global or port specific {origin} rule ({endpoint.Uri.Port}) matches any longer after {count} calls in to the cluster");
225225
}
226226

227-
private TResponse Always<TResponse, TRule>(Endpoint endpoint, RequestData requestData, PostData? postData, TimeSpan timeout, Action<TRule> beforeReturn, Func<TRule, byte[]?> successResponse, TRule rule
227+
private TResponse Always<TResponse, TRule>(Endpoint endpoint, BoundConfiguration boundConfiguration, PostData? postData, TimeSpan timeout, Action<TRule> beforeReturn, Func<TRule, byte[]?> successResponse, TRule rule
228228
)
229229
where TResponse : TransportResponse, new()
230230
where TRule : IRule
@@ -233,20 +233,20 @@ private TResponse Always<TResponse, TRule>(Endpoint endpoint, RequestData reques
233233
{
234234
var time = timeout < rule.Takes.Value ? timeout : rule.Takes.Value;
235235
_dateTimeProvider.ChangeTime(d => d.Add(time));
236-
if (rule.Takes.Value > requestData.RequestTimeout)
236+
if (rule.Takes.Value > boundConfiguration.RequestTimeout)
237237
{
238238
throw new TheException(
239239
$"Request timed out after {time} : call configured to take {rule.Takes.Value} while requestTimeout was: {timeout}");
240240
}
241241
}
242242

243243
return rule.Succeeds
244-
? Success<TResponse, TRule>(endpoint, requestData, postData, beforeReturn, successResponse, rule)
245-
: Fail<TResponse, TRule>(endpoint, requestData, postData, rule);
244+
? Success<TResponse, TRule>(endpoint, boundConfiguration, postData, beforeReturn, successResponse, rule)
245+
: Fail<TResponse, TRule>(endpoint, boundConfiguration, postData, rule);
246246
}
247247

248248
private TResponse Sometimes<TResponse, TRule>(
249-
Endpoint endpoint, RequestData requestData, PostData? postData, TimeSpan timeout, Action<TRule> beforeReturn, Func<TRule, byte[]?> successResponse, TRule rule
249+
Endpoint endpoint, BoundConfiguration boundConfiguration, PostData? postData, TimeSpan timeout, Action<TRule> beforeReturn, Func<TRule, byte[]?> successResponse, TRule rule
250250
)
251251
where TResponse : TransportResponse, new()
252252
where TRule : IRule
@@ -255,20 +255,20 @@ private TResponse Sometimes<TResponse, TRule>(
255255
{
256256
var time = timeout < rule.Takes.Value ? timeout : rule.Takes.Value;
257257
_dateTimeProvider.ChangeTime(d => d.Add(time));
258-
if (rule.Takes.Value > requestData.RequestTimeout)
258+
if (rule.Takes.Value > boundConfiguration.RequestTimeout)
259259
{
260260
throw new TheException(
261261
$"Request timed out after {time} : call configured to take {rule.Takes.Value} while requestTimeout was: {timeout}");
262262
}
263263
}
264264

265265
if (rule.Succeeds)
266-
return Success<TResponse, TRule>(endpoint, requestData, postData, beforeReturn, successResponse, rule);
266+
return Success<TResponse, TRule>(endpoint, boundConfiguration, postData, beforeReturn, successResponse, rule);
267267

268-
return Fail<TResponse, TRule>(endpoint, requestData, postData, rule);
268+
return Fail<TResponse, TRule>(endpoint, boundConfiguration, postData, rule);
269269
}
270270

271-
private TResponse Fail<TResponse, TRule>(Endpoint endpoint, RequestData requestData, PostData? postData, TRule rule, RuleOption<Exception, int>? returnOverride = null)
271+
private TResponse Fail<TResponse, TRule>(Endpoint endpoint, BoundConfiguration boundConfiguration, PostData? postData, TRule rule, RuleOption<Exception, int>? returnOverride = null)
272272
where TResponse : TransportResponse, new()
273273
where TRule : IRule
274274
{
@@ -282,13 +282,13 @@ private TResponse Fail<TResponse, TRule>(Endpoint endpoint, RequestData requestD
282282

283283
return ret.Match(
284284
e => throw e,
285-
statusCode => _inMemoryRequestInvoker.BuildResponse<TResponse>(endpoint, requestData, postData, CallResponse(rule),
285+
statusCode => _inMemoryRequestInvoker.BuildResponse<TResponse>(endpoint, boundConfiguration, postData, CallResponse(rule),
286286
//make sure we never return a valid status code in Fail responses because of a bad rule.
287287
statusCode >= 200 && statusCode < 300 ? 502 : statusCode, rule.ReturnContentType)
288288
);
289289
}
290290

291-
private TResponse Success<TResponse, TRule>(Endpoint endpoint, RequestData requestData, PostData? postData, Action<TRule> beforeReturn, Func<TRule, byte[]?> successResponse,
291+
private TResponse Success<TResponse, TRule>(Endpoint endpoint, BoundConfiguration boundConfiguration, PostData? postData, Action<TRule> beforeReturn, Func<TRule, byte[]?> successResponse,
292292
TRule rule
293293
)
294294
where TResponse : TransportResponse, new()
@@ -299,7 +299,7 @@ TRule rule
299299
rule.RecordExecuted();
300300

301301
beforeReturn?.Invoke(rule);
302-
return _inMemoryRequestInvoker.BuildResponse<TResponse>(endpoint, requestData, postData, successResponse(rule), contentType: rule.ReturnContentType);
302+
return _inMemoryRequestInvoker.BuildResponse<TResponse>(endpoint, boundConfiguration, postData, successResponse(rule), contentType: rule.ReturnContentType);
303303
}
304304

305305
private static byte[] CallResponse<TRule>(TRule rule)

src/Elastic.Transport.VirtualizedCluster/Rules/RuleBase.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ public TRule ReturnResponse<T>(T response)
8080
r = ms.ToArray();
8181
}
8282
Self.ReturnResponse = r;
83-
Self.ReturnContentType = RequestData.DefaultContentType;
83+
Self.ReturnContentType = BoundConfiguration.DefaultContentType;
8484
return (TRule)this;
8585
}
8686

87-
public TRule ReturnByteResponse(byte[] response, string responseContentType = RequestData.DefaultContentType)
87+
public TRule ReturnByteResponse(byte[] response, string responseContentType = BoundConfiguration.DefaultContentType)
8888
{
8989
Self.ReturnResponse = response;
9090
Self.ReturnContentType = responseContentType;

src/Elastic.Transport/Components/Pipeline/RequestData.cs renamed to src/Elastic.Transport/Components/Pipeline/BoundConfiguration.cs

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,10 @@
1111
namespace Elastic.Transport;
1212

1313
/// <summary>
14-
/// Where and how <see cref="IRequestInvoker.Request{TResponse}" /> should connect to.
15-
/// <para>
1614
/// Represents the cumulative configuration from <see cref="ITransportConfiguration" />
1715
/// and <see cref="IRequestConfiguration" />.
18-
/// </para>
1916
/// </summary>
20-
public sealed record RequestData
17+
public sealed record BoundConfiguration : IRequestConfiguration
2118
{
2219
private const string OpaqueIdHeader = "X-Opaque-Id";
2320

@@ -27,8 +24,8 @@ public sealed record RequestData
2724
/// The security header used to run requests as a different user.
2825
public const string RunAsSecurityHeader = "es-security-runas-user";
2926

30-
/// <inheritdoc cref="RequestData"/>
31-
public RequestData(ITransportConfiguration global, IRequestConfiguration? local = null)
27+
/// <inheritdoc cref="BoundConfiguration"/>
28+
public BoundConfiguration(ITransportConfiguration global, IRequestConfiguration? local = null)
3229
{
3330
ConnectionSettings = global;
3431
MemoryStreamFactory = global.MemoryStreamFactory;
@@ -55,7 +52,7 @@ public RequestData(ITransportConfiguration global, IRequestConfiguration? local
5552
Accept = local?.Accept ?? global.Accept ?? DefaultContentType;
5653
ThrowExceptions = local?.ThrowExceptions ?? global.ThrowExceptions ?? false;
5754
RequestTimeout = local?.RequestTimeout ?? global.RequestTimeout ?? RequestConfiguration.DefaultRequestTimeout;
58-
RequestMetaData = local?.RequestMetaData?.Items ?? EmptyReadOnly<string, string>.Dictionary;
55+
RequestMetaData = local?.RequestMetaData;
5956
AuthenticationHeader = local?.Authentication ?? global.Authentication;
6057
AllowedStatusCodes = local?.AllowedStatusCodes ?? EmptyReadOnly<int>.Collection;
6158
ClientCertificates = local?.ClientCertificates ?? global.ClientCertificates;
@@ -81,6 +78,7 @@ public RequestData(ITransportConfiguration global, IRequestConfiguration? local
8178
Headers[key] = local.Headers[key];
8279
}
8380

81+
OpaqueId = local?.OpaqueId;
8482
if (!string.IsNullOrEmpty(local?.OpaqueId))
8583
{
8684
Headers ??= [];
@@ -115,6 +113,7 @@ public RequestData(ITransportConfiguration global, IRequestConfiguration? local
115113
}
116114

117115
ProductResponseBuilders = global.ProductRegistration.ResponseBuilders;
116+
DisableAuditTrail = local?.DisableAuditTrail ?? global.DisableAuditTrail ?? false;
118117
}
119118

120119
/// <inheritdoc cref="ITransportConfiguration.MemoryStreamFactory"/>
@@ -140,7 +139,7 @@ public RequestData(ITransportConfiguration global, IRequestConfiguration? local
140139
/// <inheritdoc cref="ITransportConfiguration.DnsRefreshTimeout"/>
141140
public TimeSpan DnsRefreshTimeout { get; }
142141
/// <inheritdoc cref="IRequestConfiguration.RequestMetaData"/>
143-
public IReadOnlyDictionary<string, string> RequestMetaData { get; }
142+
public RequestMetaData? RequestMetaData { get; }
144143
/// <inheritdoc cref="IRequestConfiguration.Accept"/>
145144
public string Accept { get; }
146145
/// <inheritdoc cref="IRequestConfiguration.AllowedStatusCodes"/>
@@ -191,4 +190,45 @@ public RequestData(ITransportConfiguration global, IRequestConfiguration? local
191190
public IReadOnlyCollection<IResponseBuilder> ProductResponseBuilders { get; }
192191
/// <inheritdoc cref="IRequestConfiguration.ResponseBuilders"/>
193192
public IReadOnlyCollection<IResponseBuilder> ResponseBuilders { get; }
193+
/// <inheritdoc cref="IRequestConfiguration.DisableAuditTrail"/>
194+
public bool DisableAuditTrail { get; }
195+
/// <inheritdoc cref="IRequestConfiguration.OpaqueId"/>
196+
public string? OpaqueId { get; }
197+
198+
string? IRequestConfiguration.Accept => Accept;
199+
IReadOnlyCollection<int>? IRequestConfiguration.AllowedStatusCodes => AllowedStatusCodes;
200+
AuthorizationHeader? IRequestConfiguration.Authentication => AuthenticationHeader;
201+
X509CertificateCollection? IRequestConfiguration.ClientCertificates => ClientCertificates;
202+
string? IRequestConfiguration.ContentType => ContentType;
203+
bool? IRequestConfiguration.DisableDirectStreaming => DisableDirectStreaming;
204+
bool? IRequestConfiguration.DisableAuditTrail => DisableAuditTrail;
205+
bool? IRequestConfiguration.DisablePings => DisablePings;
206+
bool? IRequestConfiguration.DisableSniff => DisableSniff;
207+
bool? IRequestConfiguration.HttpPipeliningEnabled => HttpPipeliningEnabled;
208+
bool? IRequestConfiguration.EnableHttpCompression => HttpCompression;
209+
Uri? IRequestConfiguration.ForceNode => ForceNode;
210+
int? IRequestConfiguration.MaxRetries => MaxRetries;
211+
TimeSpan? IRequestConfiguration.MaxRetryTimeout => RequestTimeout;
212+
string? IRequestConfiguration.OpaqueId => OpaqueId;
213+
bool? IRequestConfiguration.ParseAllHeaders => ParseAllHeaders;
214+
TimeSpan? IRequestConfiguration.PingTimeout => PingTimeout;
215+
TimeSpan? IRequestConfiguration.RequestTimeout => RequestTimeout;
216+
IReadOnlyCollection<IResponseBuilder> IRequestConfiguration.ResponseBuilders => ResponseBuilders;
217+
HeadersList? IRequestConfiguration.ResponseHeadersToParse => ResponseHeadersToParse;
218+
string? IRequestConfiguration.RunAs => RunAs;
219+
bool? IRequestConfiguration.ThrowExceptions => ThrowExceptions;
220+
bool? IRequestConfiguration.TransferEncodingChunked => TransferEncodingChunked;
221+
NameValueCollection? IRequestConfiguration.Headers => Headers;
222+
bool? IRequestConfiguration.EnableTcpStats => EnableTcpStats;
223+
bool? IRequestConfiguration.EnableThreadPoolStats => EnableThreadPoolStats;
224+
RequestMetaData? IRequestConfiguration.RequestMetaData => RequestMetaData;
225+
226+
/// <summary>
227+
/// Create a cachable instance of <see cref="BoundConfiguration"/> for use in high-performance scenarios.
228+
/// </summary>
229+
/// <param name="transport">An existing <see cref="ITransport{TConfiguration}"/> from which to bind transport configuration.</param>
230+
/// <param name="requestConfiguration">A request specific <see cref="IRequestConfiguration"/>.</param>
231+
/// <returns></returns>
232+
public static BoundConfiguration Create(ITransport<ITransportConfiguration> transport, IRequestConfiguration requestConfiguration) =>
233+
new(transport.Configuration, requestConfiguration);
194234
}

src/Elastic.Transport/Components/Pipeline/DefaultResponseBuilder.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,26 @@ internal sealed class DefaultResponseBuilder : IResponseBuilder
2222
bool IResponseBuilder.CanBuild<TResponse>() => true;
2323

2424
/// <inheritdoc/>
25-
public TResponse Build<TResponse>(ApiCallDetails apiCallDetails, RequestData requestData,
25+
public TResponse Build<TResponse>(ApiCallDetails apiCallDetails, BoundConfiguration boundConfiguration,
2626
Stream responseStream, string contentType, long contentLength)
2727
where TResponse : TransportResponse, new() =>
28-
SetBodyCoreAsync<TResponse>(false, apiCallDetails, requestData, responseStream).EnsureCompleted();
28+
SetBodyCoreAsync<TResponse>(false, apiCallDetails, boundConfiguration, responseStream).EnsureCompleted();
2929

3030
/// <inheritdoc/>
3131
public Task<TResponse> BuildAsync<TResponse>(
32-
ApiCallDetails apiCallDetails, RequestData requestData, Stream responseStream, string contentType, long contentLength,
32+
ApiCallDetails apiCallDetails, BoundConfiguration boundConfiguration, Stream responseStream, string contentType, long contentLength,
3333
CancellationToken cancellationToken) where TResponse : TransportResponse, new() =>
34-
SetBodyCoreAsync<TResponse>(true, apiCallDetails, requestData, responseStream, cancellationToken).AsTask();
34+
SetBodyCoreAsync<TResponse>(true, apiCallDetails, boundConfiguration, responseStream, cancellationToken).AsTask();
3535

3636
private static async ValueTask<TResponse> SetBodyCoreAsync<TResponse>(bool isAsync,
37-
ApiCallDetails details, RequestData requestData, Stream responseStream,
37+
ApiCallDetails details, BoundConfiguration boundConfiguration, Stream responseStream,
3838
CancellationToken cancellationToken = default)
3939
where TResponse : TransportResponse, new()
4040
{
4141
TResponse response = null;
4242

4343
if (details.HttpStatusCode.HasValue &&
44-
requestData.SkipDeserializationForStatusCodes.Contains(details.HttpStatusCode.Value))
44+
boundConfiguration.SkipDeserializationForStatusCodes.Contains(details.HttpStatusCode.Value))
4545
{
4646
return response;
4747
}
@@ -51,9 +51,9 @@ private static async ValueTask<TResponse> SetBodyCoreAsync<TResponse>(bool isAsy
5151
var beforeTicks = Stopwatch.GetTimestamp();
5252

5353
if (isAsync)
54-
response = await requestData.ConnectionSettings.RequestResponseSerializer.DeserializeAsync<TResponse>(responseStream, cancellationToken).ConfigureAwait(false);
54+
response = await boundConfiguration.ConnectionSettings.RequestResponseSerializer.DeserializeAsync<TResponse>(responseStream, cancellationToken).ConfigureAwait(false);
5555
else
56-
response = requestData.ConnectionSettings.RequestResponseSerializer.Deserialize<TResponse>(responseStream);
56+
response = boundConfiguration.ConnectionSettings.RequestResponseSerializer.Deserialize<TResponse>(responseStream);
5757

5858
var deserializeResponseMs = (Stopwatch.GetTimestamp() - beforeTicks) / (Stopwatch.Frequency / 1000);
5959

0 commit comments

Comments
 (0)