[go: nahoru, domu]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#1314 #1869 Default timeout enhancements #2073

Open
wants to merge 33 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8ed9057
RequestTimeoutSeconds FileGlobalConfiguration
hogwartsdeveloper May 25, 2024
138dc7b
Fix Test
hogwartsdeveloper May 25, 2024
9bcb23d
Fix Remarks
hogwartsdeveloper May 26, 2024
7ab3672
Add DI FileGlobalConfiguration. PollyQoSProvider, PollyQoSResilienceP…
hogwartsdeveloper May 27, 2024
0eb8d73
Fix Remarks
hogwartsdeveloper May 28, 2024
bf24893
Fix remarks
hogwartsdeveloper May 28, 2024
97407d0
Add DownStream Timeout
hogwartsdeveloper May 29, 2024
f6da820
Use One Timeout
hogwartsdeveloper May 29, 2024
dd150f7
Fix
hogwartsdeveloper May 29, 2024
e914ca6
Fix Remarks
hogwartsdeveloper May 29, 2024
ebe7176
Nullable timeouts
hogwartsdeveloper May 30, 2024
24f2260
Remove TimeoutCreator
hogwartsdeveloper May 30, 2024
e94ab50
RoutesCreator CreateTimeout
hogwartsdeveloper May 30, 2024
602d871
PollyQoSResiliencePipelineProvider Return Old Logic And if-condition
hogwartsdeveloper May 30, 2024
aefa309
Fix
hogwartsdeveloper May 30, 2024
249732e
Fix Remarks
hogwartsdeveloper Jun 1, 2024
6d9a796
Fix remarks
hogwartsdeveloper Jun 2, 2024
68b88a8
Fix
hogwartsdeveloper Jun 2, 2024
65c78a2
Fix Tests
hogwartsdeveloper Jun 2, 2024
aaa3e3f
Update src/Ocelot/Configuration/File/FileRoute.cs
hogwartsdeveloper Jun 1, 2024
3ce5151
Fix build error
hogwartsdeveloper Jun 2, 2024
8b51284
Fix warnings: SA1108 SA1629 SA1518
raman-m Jun 3, 2024
d78bd9e
Fix SA1108 warning
raman-m Jun 3, 2024
d98c971
Try to fix... failed
raman-m Jun 3, 2024
490f55e
Fix Tests
hogwartsdeveloper Jun 4, 2024
68e517a
MessageInvokerPoolTests
hogwartsdeveloper Jun 4, 2024
c9bdec2
RoutesCreatorTests
hogwartsdeveloper Jun 4, 2024
c457a31
Review fixed/new unit tests
raman-m Jun 4, 2024
69a197a
Remove BDDfy from unit tests
raman-m Jun 4, 2024
2e35a20
Review `MessageInvokerPoolTests` and AAA pattern
raman-m Jun 4, 2024
2a688da
Fix test : `Should_timeout_per_default_after_90_seconds`
raman-m Jun 4, 2024
4d6e72d
More refactoring: QoS vs DynRoute timeouts
raman-m Jun 4, 2024
ef44dcc
Polly constraints
raman-m Jun 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/Ocelot.Provider.Polly/OcelotResiliencePipelineKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ namespace Ocelot.Provider.Polly;
/// <summary>
/// Object used to identify a resilience pipeline in <see cref="ResiliencePipelineRegistry{OcelotResiliencePipelineKey}"/>.
/// </summary>
/// <value>
/// Object used to identify a resilience pipeline in <see cref="ResiliencePipelineRegistry{OcelotResiliencePipelineKey}"/>
/// </value>
/// <value>An <see cref="OcelotResiliencePipelineKey"/> record object.</value>
/// <param name="Key">The key for the resilience pipeline.</param>
public record OcelotResiliencePipelineKey(string Key);
40 changes: 30 additions & 10 deletions src/Ocelot.Provider.Polly/PollyQoSResiliencePipelineProvider.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using Microsoft.Extensions.Options;
using Ocelot.Configuration;
using Ocelot.Configuration.File;
using Ocelot.Logging;
using Ocelot.Provider.Polly.Interfaces;
using Polly.CircuitBreaker;
using Polly.Registry;
using Polly.Timeout;
using System;
using System.Net;

namespace Ocelot.Provider.Polly;
Expand All @@ -15,13 +18,18 @@ public class PollyQoSResiliencePipelineProvider : IPollyQoSResiliencePipelinePro
{
private readonly ResiliencePipelineRegistry<OcelotResiliencePipelineKey> _registry;
private readonly IOcelotLogger _logger;
private readonly FileGlobalConfiguration _global;

public const int DefaultQoSTimeoutMilliseconds = 40_000;

public PollyQoSResiliencePipelineProvider(
IOcelotLoggerFactory loggerFactory,
ResiliencePipelineRegistry<OcelotResiliencePipelineKey> registry)
ResiliencePipelineRegistry<OcelotResiliencePipelineKey> registry,
IOptions<FileGlobalConfiguration> global)
raman-m marked this conversation as resolved.
Show resolved Hide resolved
{
_logger = loggerFactory.CreateLogger<PollyQoSResiliencePipelineProvider>();
_registry = registry;
_global = global.Value;
}

protected static readonly HashSet<HttpStatusCode> DefaultServerErrorCodes = new()
Expand Down Expand Up @@ -79,14 +87,21 @@ protected virtual ResiliencePipelineBuilder<HttpResponseMessage> ConfigureCircui

var options = route.QosOptions;
var info = $"Circuit Breaker for Route: {GetRouteName(route)}: ";

// Polly constraints
int minimumThroughput = options.ExceptionsAllowedBeforeBreaking >= QoSOptions.LowMinimumThroughput
? options.ExceptionsAllowedBeforeBreaking
: QoSOptions.DefaultMinimumThroughput;
int breakDurationMs = options.DurationOfBreak > QoSOptions.LowBreakDuration
? options.DurationOfBreak
: QoSOptions.DefaultBreakDuration;

var strategyOptions = new CircuitBreakerStrategyOptions<HttpResponseMessage>
{
FailureRatio = 0.8,
SamplingDuration = TimeSpan.FromSeconds(10),
MinimumThroughput = options.ExceptionsAllowedBeforeBreaking,
BreakDuration = options.DurationOfBreak > QoSOptions.LowBreakDuration
? TimeSpan.FromMilliseconds(options.DurationOfBreak)
: TimeSpan.FromMilliseconds(QoSOptions.DefaultBreakDuration),
MinimumThroughput = minimumThroughput,
BreakDuration = TimeSpan.FromMilliseconds(breakDurationMs),
ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
.HandleResult(message => ServerErrorCodes.Contains(message.StatusCode))
.Handle<TimeoutRejectedException>()
Expand All @@ -113,18 +128,23 @@ protected virtual ResiliencePipelineBuilder<HttpResponseMessage> ConfigureCircui

protected virtual ResiliencePipelineBuilder<HttpResponseMessage> ConfigureTimeout(ResiliencePipelineBuilder<HttpResponseMessage> builder, DownstreamRoute route)
{
var options = route.QosOptions;
int? timeoutMs = route?.QosOptions?.TimeoutValue
?? _global?.QoSOptions?.TimeoutValue
?? DefaultQoSTimeoutMilliseconds; // TODO Need team's consensus!!! Prefer QoSOptions.DefaultTimeout ?

// Add Timeout strategy if TimeoutValue is not int.MaxValue and greater than 0
// TimeoutValue must be defined in QosOptions!
if (options.TimeoutValue == int.MaxValue || options.TimeoutValue <= 0)
if (!timeoutMs.HasValue || timeoutMs.Value <= 0) // 0 means No option!
{
return builder;
}

// Polly constraints
timeoutMs = timeoutMs.Value > QoSOptions.LowTimeout && timeoutMs.Value < QoSOptions.HighTimeout
? timeoutMs.Value
: QoSOptions.DefaultTimeout;

var strategyOptions = new TimeoutStrategyOptions
{
Timeout = TimeSpan.FromMilliseconds(options.TimeoutValue),
Timeout = TimeSpan.FromMilliseconds(timeoutMs.Value),
=>
{
_logger.LogInformation($"Timeout for Route: {GetRouteName(route)}");
Expand Down
10 changes: 9 additions & 1 deletion src/Ocelot/Configuration/Builder/DownstreamRouteBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class DownstreamRouteBuilder
private HttpVersionPolicy _downstreamHttpVersionPolicy;
private Dictionary<string, UpstreamHeaderTemplate> _upstreamHeaders;
private MetadataOptions _metadataOptions;
private int _timeout;

public DownstreamRouteBuilder()
{
Expand Down Expand Up @@ -282,6 +283,12 @@ public DownstreamRouteBuilder WithMetadata(MetadataOptions metadataOptions)
return this;
}

public DownstreamRouteBuilder WithTimeout(int timeout)
{
_timeout = timeout;
return this;
}
raman-m marked this conversation as resolved.
Show resolved Hide resolved

public DownstreamRoute Build()
{
return new DownstreamRoute(
Expand Down Expand Up @@ -321,6 +328,7 @@ public DownstreamRoute Build()
_downstreamHttpVersion,
_downstreamHttpVersionPolicy,
_upstreamHeaders,
_metadataOptions);
_metadataOptions,
_timeout);
}
}
4 changes: 2 additions & 2 deletions src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public class QoSOptionsBuilder

private int _durationOfBreak;

private int _timeoutValue;
private int? _timeoutValue;

private string _key;

Expand All @@ -22,7 +22,7 @@ public QoSOptionsBuilder WithDurationOfBreak(int durationOfBreak)
return this;
}

public QoSOptionsBuilder WithTimeoutValue(int timeoutValue)
public QoSOptionsBuilder WithTimeoutValue(int? timeoutValue)
{
_timeoutValue = timeoutValue;
return this;
Expand Down
25 changes: 18 additions & 7 deletions src/Ocelot/Configuration/Creator/DynamicsCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ public class DynamicsCreator : IDynamicsCreator
private readonly IVersionPolicyCreator _versionPolicyCreator;
private readonly IMetadataCreator _metadataCreator;

/// <summary>
/// Defines the default timeout in seconds for all routes, applicable at both the Route-level and globally.
/// </summary>
public const int DefaultRequestTimeoutSeconds = 90;

public DynamicsCreator(
IRateLimitOptionsCreator rateLimitOptionsCreator,
IVersionCreator versionCreator,
Expand All @@ -29,22 +34,28 @@ public List<Route> Create(FileConfiguration fileConfiguration)
.ToList();
}

private Route SetUpDynamicRoute(FileDynamicRoute fileDynamicRoute, FileGlobalConfiguration globalConfiguration)
public int CreateTimeout(FileDynamicRoute route, FileGlobalConfiguration global)
=> route.Timeout > 0
? route.Timeout.Value
: global.Timeout ?? DefaultRequestTimeoutSeconds;

private Route SetUpDynamicRoute(FileDynamicRoute dynamicRoute, FileGlobalConfiguration globalConfiguration)
{
var rateLimitOption = _rateLimitOptionsCreator
.Create(fileDynamicRoute.RateLimitRule, globalConfiguration);
.Create(dynamicRoute.RateLimitRule, globalConfiguration);

var version = _versionCreator.Create(fileDynamicRoute.DownstreamHttpVersion);
var versionPolicy = _versionPolicyCreator.Create(fileDynamicRoute.DownstreamHttpVersionPolicy);
var metadata = _metadataCreator.Create(fileDynamicRoute.Metadata, globalConfiguration);
var version = _versionCreator.Create(dynamicRoute.DownstreamHttpVersion);
var versionPolicy = _versionPolicyCreator.Create(dynamicRoute.DownstreamHttpVersionPolicy);
var metadata = _metadataCreator.Create(dynamicRoute.Metadata, globalConfiguration);

var downstreamRoute = new DownstreamRouteBuilder()
.WithEnableRateLimiting(rateLimitOption.EnableRateLimiting)
.WithRateLimitOptions(rateLimitOption)
.WithServiceName(fileDynamicRoute.ServiceName)
.WithServiceName(dynamicRoute.ServiceName)
.WithDownstreamHttpVersion(version)
.WithDownstreamHttpVersionPolicy(versionPolicy)
.WithMetadata(metadata)
.WithMetadata(metadata)
.WithTimeout(CreateTimeout(dynamicRoute, globalConfiguration))
.Build();

var route = new RouteBuilder()
Expand Down
8 changes: 8 additions & 0 deletions src/Ocelot/Configuration/Creator/IDynamicsCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,13 @@ namespace Ocelot.Configuration.Creator
public interface IDynamicsCreator
{
List<Route> Create(FileConfiguration fileConfiguration);

/// <summary>
/// Creates a timeout value for a given file route based on the global configuration.
/// </summary>
/// <param name="route">The file route for which to create the timeout.</param>
/// <param name="global">The global configuration to use for creating the timeout.</param>
/// <returns>The timeout value in seconds.</returns>
int CreateTimeout(FileDynamicRoute route, FileGlobalConfiguration global);
}
}
8 changes: 8 additions & 0 deletions src/Ocelot/Configuration/Creator/IRoutesCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,12 @@ namespace Ocelot.Configuration.Creator;
public interface IRoutesCreator
{
List<Route> Create(FileConfiguration fileConfiguration);

/// <summary>
/// Creates a timeout value for a given file route based on the global configuration.
/// </summary>
/// <param name="route">The file route for which to create the timeout.</param>
/// <param name="global">The global configuration to use for creating the timeout.</param>
/// <returns>The timeout value in seconds.</returns>
int CreateTimeout(FileRoute route, FileGlobalConfiguration global);
}
2 changes: 1 addition & 1 deletion src/Ocelot/Configuration/Creator/QoSOptionsCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public QoSOptions Create(QoSOptions options, string pathTemplate, List<string> h
return Map(key, options.TimeoutValue, options.DurationOfBreak, options.ExceptionsAllowedBeforeBreaking);
}

private static QoSOptions Map(string key, int timeoutValue, int durationOfBreak, int exceptionsAllowedBeforeBreaking)
private static QoSOptions Map(string key, int? timeoutValue, int durationOfBreak, int exceptionsAllowedBeforeBreaking)
{
return new QoSOptionsBuilder()
.WithExceptionsAllowedBeforeBreaking(exceptionsAllowedBeforeBreaking)
Expand Down
33 changes: 23 additions & 10 deletions src/Ocelot/Configuration/Creator/RoutesCreator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;

namespace Ocelot.Configuration.Creator
Expand All @@ -21,8 +21,13 @@ public class RoutesCreator : IRoutesCreator
private readonly IRouteKeyCreator _routeKeyCreator;
private readonly ISecurityOptionsCreator _securityOptionsCreator;
private readonly IVersionCreator _versionCreator;
private readonly IVersionPolicyCreator _versionPolicyCreator;
private readonly IMetadataCreator _metadataCreator;
private readonly IVersionPolicyCreator _versionPolicyCreator;
private readonly IMetadataCreator _metadataCreator;

/// <summary>
/// Defines the default timeout in seconds for all routes, applicable at both the Route-level and globally.
/// </summary>
public const int DefaultRequestTimeoutSeconds = 90;
raman-m marked this conversation as resolved.
Show resolved Hide resolved

public RoutesCreator(
IClaimsToThingCreator claimsToThingCreator,
Expand All @@ -40,7 +45,7 @@ public class RoutesCreator : IRoutesCreator
IRouteKeyCreator routeKeyCreator,
ISecurityOptionsCreator securityOptionsCreator,
IVersionCreator versionCreator,
IVersionPolicyCreator versionPolicyCreator,
IVersionPolicyCreator versionPolicyCreator,
IUpstreamHeaderTemplatePatternCreator upstreamHeaderTemplatePatternCreator,
IMetadataCreator metadataCreator)
{
Expand All @@ -60,9 +65,9 @@ public class RoutesCreator : IRoutesCreator
_loadBalancerOptionsCreator = loadBalancerOptionsCreator;
_securityOptionsCreator = securityOptionsCreator;
_versionCreator = versionCreator;
_versionPolicyCreator = versionPolicyCreator;
_versionPolicyCreator = versionPolicyCreator;
_upstreamHeaderTemplatePatternCreator = upstreamHeaderTemplatePatternCreator;
_metadataCreator = metadataCreator;
_metadataCreator = metadataCreator;
}

public List<Route> Create(FileConfiguration fileConfiguration)
Expand All @@ -76,6 +81,13 @@ public List<Route> Create(FileConfiguration fileConfiguration)
.ToList();
}

public int CreateTimeout(FileRoute route, FileGlobalConfiguration global)
{
return route.Timeout > 0
? route.Timeout.Value
: global.Timeout ?? DefaultRequestTimeoutSeconds;
}

private DownstreamRoute SetUpDownstreamRoute(FileRoute fileRoute, FileGlobalConfiguration globalConfiguration)
{
var fileRouteOptions = _fileRouteOptionsCreator.Create(fileRoute);
Expand Down Expand Up @@ -112,11 +124,11 @@ private DownstreamRoute SetUpDownstreamRoute(FileRoute fileRoute, FileGlobalConf

var downstreamHttpVersion = _versionCreator.Create(fileRoute.DownstreamHttpVersion);

var downstreamHttpVersionPolicy = _versionPolicyCreator.Create(fileRoute.DownstreamHttpVersionPolicy);
var downstreamHttpVersionPolicy = _versionPolicyCreator.Create(fileRoute.DownstreamHttpVersionPolicy);

var cacheOptions = _cacheOptionsCreator.Create(fileRoute.FileCacheOptions, globalConfiguration, fileRoute.UpstreamPathTemplate, fileRoute.UpstreamHttpMethod);

var metadata = _metadataCreator.Create(fileRoute.Metadata, globalConfiguration);
var metadata = _metadataCreator.Create(fileRoute.Metadata, globalConfiguration);

var route = new DownstreamRouteBuilder()
.WithKey(fileRoute.Key)
Expand Down Expand Up @@ -153,9 +165,10 @@ private DownstreamRoute SetUpDownstreamRoute(FileRoute fileRoute, FileGlobalConf
.WithDangerousAcceptAnyServerCertificateValidator(fileRoute.DangerousAcceptAnyServerCertificateValidator)
.WithSecurityOptions(securityOptions)
.WithDownstreamHttpVersion(downstreamHttpVersion)
.WithDownstreamHttpVersionPolicy(downstreamHttpVersionPolicy)
.WithDownstreamHttpVersionPolicy(downstreamHttpVersionPolicy)
.WithDownStreamHttpMethod(fileRoute.DownstreamHttpMethod)
.WithMetadata(metadata)
.WithMetadata(metadata)
.WithTimeout(CreateTimeout(fileRoute, globalConfiguration))
.Build();

return route;
Expand Down
26 changes: 18 additions & 8 deletions src/Ocelot/Configuration/DownstreamRoute.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.Creator;
using Ocelot.Values;

namespace Ocelot.Configuration
Expand Down Expand Up @@ -39,10 +39,11 @@ public class DownstreamRoute
bool dangerousAcceptAnyServerCertificateValidator,
SecurityOptions securityOptions,
string downstreamHttpMethod,
Version downstreamHttpVersion,
Version downstreamHttpVersion,
HttpVersionPolicy downstreamHttpVersionPolicy,
Dictionary<string, UpstreamHeaderTemplate> upstreamHeaders,
MetadataOptions metadataOptions)
MetadataOptions metadataOptions,
int timeout)
{
DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator;
AddHeadersToDownstream = addHeadersToDownstream;
Expand Down Expand Up @@ -77,10 +78,11 @@ public class DownstreamRoute
AddHeadersToUpstream = addHeadersToUpstream;
SecurityOptions = securityOptions;
DownstreamHttpMethod = downstreamHttpMethod;
DownstreamHttpVersion = downstreamHttpVersion;
DownstreamHttpVersionPolicy = downstreamHttpVersionPolicy;
DownstreamHttpVersion = downstreamHttpVersion;
DownstreamHttpVersionPolicy = downstreamHttpVersionPolicy;
UpstreamHeaders = upstreamHeaders ?? new();
MetadataOptions = metadataOptions;
MetadataOptions = metadataOptions;
Timeout = timeout;
raman-m marked this conversation as resolved.
Show resolved Hide resolved
}

public string Key { get; }
Expand Down Expand Up @@ -116,7 +118,7 @@ public class DownstreamRoute
public bool DangerousAcceptAnyServerCertificateValidator { get; }
public SecurityOptions SecurityOptions { get; }
public string DownstreamHttpMethod { get; }
public Version DownstreamHttpVersion { get; }
public Version DownstreamHttpVersion { get; }

/// <summary>The <see cref="HttpVersionPolicy"/> enum specifies behaviors for selecting and negotiating the HTTP version for a request.</summary>
/// <value>An <see cref="HttpVersionPolicy"/> enum value being mapped from a <see cref="VersionPolicies"/> constant.</value>
Expand All @@ -128,8 +130,16 @@ public class DownstreamRoute
/// <item><see href="https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httprequestmessage.versionpolicy">HttpRequestMessage.VersionPolicy Property</see></item>
/// </list>
/// </remarks>
public HttpVersionPolicy DownstreamHttpVersionPolicy { get; }
public HttpVersionPolicy DownstreamHttpVersionPolicy { get; }
public Dictionary<string, UpstreamHeaderTemplate> UpstreamHeaders { get; }
public MetadataOptions MetadataOptions { get; }

/// <summary>
/// The timeout duration for the downstream request in seconds.
/// </summary>
/// <value>
/// The timeout value in seconds.
/// </value>
public int Timeout { get; }
raman-m marked this conversation as resolved.
Show resolved Hide resolved
}
}
7 changes: 7 additions & 0 deletions src/Ocelot/Configuration/File/FileDynamicRoute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Ocelot.Configuration.File
{
/// <summary>
/// TODO: Make it as a base Route File-model.
/// </summary>
public class FileDynamicRoute
{
public string ServiceName { get; set; }
Expand All @@ -20,5 +23,9 @@ public class FileDynamicRoute
/// </remarks>
public string DownstreamHttpVersionPolicy { get; set; }
public IDictionary<string, string> Metadata { get; set; }

/// <summary>The timeout in seconds for requests.</summary>
/// <value>A <see cref="Nullable{T}"/> where T is <see cref="int"/> value in seconds.</value>
public int? Timeout { get; set; }
}
}
Loading