-
Notifications
You must be signed in to change notification settings - Fork 41
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
Add support for per HalibutRuntime timeouts and limits #427
Conversation
95987c3
to
3343ea3
Compare
This pull request has been linked to Shortcut Story #45207: Improve Halibut to support testing with different TCP timeouts, allowing tests to control timeouts. |
@@ -58,5 +60,15 @@ public static LatestClientAndLatestServiceBuilder WithInstantReconnectPollingRet | |||
{ | |||
return builder.WithPollingReconnectRetryPolicy(() => new RetryPolicy(1, TimeSpan.Zero, TimeSpan.Zero)); | |||
} | |||
|
|||
public static LatestClientAndLatestServiceBuilder WhenTestingAsyncClient(this LatestClientAndLatestServiceBuilder builder, ClientAndServiceTestCase clientAndServiceTestCase, Action<LatestClientAndLatestServiceBuilder> action) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the existing timeout tests could be made faster by making use of this and lowering timeouts.
@@ -23,18 +23,19 @@ public class LatestClientAndLatestServiceTestCasesAttribute : TestCaseSourceAttr | |||
bool testSyncClients = true, | |||
bool testAsyncClients = true, | |||
bool testAsyncServicesAsWell = false, // False means only the sync service will be tested. | |||
bool testSyncService = true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if these could be in a #define
and so defined once but used here and in the below static method. As well as in other builders.
.And | ||
.BeLessThan(expectedTimeout + LowerHalibutLimitsForAllTests.HalfTheTcpReceiveTimeout, "We should be timing out on the tcp receive timeout"); | ||
|
||
// The polling tentacle, will not reconnect in time since it has a 133s receive control message timeout. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The raises other questions like if the tentacle has sent a large response back, will it be able to move into a control message read/write but timeout on that. Not because the next/proceed took a long time itself but because the data ahead of it resulted in the control messages taking some time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this something we need to look at? If so should we card it?
@@ -8,11 +8,15 @@ public class ServiceEndPoint : IEquatable<ServiceEndPoint> | |||
{ | |||
readonly string baseUriString; | |||
|
|||
// TODO - ASYNC ME UP! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In another PR we will need a ServiceEndPoint constructed with the halibuttimeoutsandlimits. Or perhaps the halibut runtime should have a ServiceEndPoint factory with timeouts configured from its limits.
Doing something here is captured on the ticket.
|
||
CancellationTokenSource GetCancellationTokenSourceFromStreamReadTimeoutAsync(Stream stream) | ||
{ | ||
if (stream.CanTimeout) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO mark this as TODO to note we should always be given a stream that can timeout.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WebSockets?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We were not thinking of wrapping that stream in a stream that fakes timing out?
source/Halibut.Tests/Timeouts/SendingAndReceivingRequestMessagesTimeoutsFixture.cs
Outdated
Show resolved
Hide resolved
@@ -44,7 +44,7 @@ public async Task DiscoveringNonExistentEndpointThrows(SyncOrAsync syncOrAsync) | |||
#pragma warning disable CS0612 | |||
await syncOrAsync | |||
.WhenSync(() => Assert.Throws<HalibutClientException>(() => client.Discover(fakeEndpoint), "No such host is known")).IgnoreResult() | |||
.WhenAsync(async () => await AssertAsync.Throws<HalibutClientException>(() => client.DiscoverAsync(fakeEndpoint, CancellationToken), "No such host is known")); | |||
.WhenAsync(async () => await AssertAsync.Throws<HalibutClientException>(() => client.DiscoverAsync(fakeEndpoint, new HalibutTimeoutsAndLimits(), CancellationToken), "No such host is known")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want a helper that sets some good defaults instead of new HalibutTimeoutsAndLimits()
so it's consistent with the statics we set at the moment to keep some consistency amount how timeouts are set. Individual tests can choose to change them, otherwise they all get the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, I see they are defaulted from the statics so it is doing that 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HalibutTimeoutsAndLimits
get its defaults from what is set statically which will be the reduced values as part of LowerHalibutLimits
. In theory those should be "good" defaults.
@@ -107,4 +108,102 @@ public static TimeSpan SafeTcpClientPooledConnectionTimeout | |||
} | |||
} | |||
} | |||
#pragma warning disable CS0612 | |||
public class HalibutTimeoutsAndLimits { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth adding a unit test for this class to make sure we are setting the properties from the correct statics?
@@ -91,6 +94,7 @@ public HalibutRuntime(IServiceFactory serviceFactory, X509Certificate2 serverCer | |||
this.messageSerializer = messageSerializer; | |||
this.pollingReconnectRetryPolicy = pollingReconnectRetryPolicy; | |||
invoker = new ServiceInvoker(serviceFactory); | |||
this.TimeoutsAndLimits = halibutTimeoutsAndLimits; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a sync Halibut runtime is created, would it make sense to enforce that halibutTimeoutsAndLimits
should be null, so the caller knows these are not going to apply and we don't create confusion? It may also help ensure we aren't using them in sync code paths at all as we will get object ref exceptions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes and done.
@@ -36,6 +36,7 @@ public class HalibutRuntime : IHalibutRuntime | |||
readonly ITypeRegistry typeRegistry; | |||
readonly Lazy<ResponseCache> responseCache = new(); | |||
readonly Func<RetryPolicy> pollingReconnectRetryPolicy; | |||
public HalibutTimeoutsAndLimits TimeoutsAndLimits { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could throw here if it's a sync runtime so callers know they are accessing something that is not supported / being used
@@ -50,7 +50,11 @@ public async Task<string> ReadTextMessage() | |||
var buffer = new ArraySegment<byte>(new byte[10000]); | |||
while (true) | |||
{ | |||
// TODO - ASYNC ME UP! | |||
// What should the timeout be here? thew new code that allows passing in a timeout or the static value? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#414 changes this stream to add async methods
@@ -30,7 +30,11 @@ public void NotifyUsed() | |||
|
|||
public bool HasExpired() | |||
{ | |||
// TODO - ASYNC ME UP! | |||
// Use the HalibutRuntimeLimits |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason some changes are being split out of this pr?
@@ -47,12 +47,12 @@ public IConnection EstablishNewConnection(ExchangeProtocolBuilder exchangeProtoc | |||
return new SecureConnection(client, ssl, exchangeProtocolBuilder, log); | |||
} | |||
|
|||
public async Task<IConnection> EstablishNewConnectionAsync(ExchangeProtocolBuilder exchangeProtocolBuilder, ServiceEndPoint serviceEndpoint, ILog log, CancellationToken cancellationToken) | |||
public async Task<IConnection> EstablishNewConnectionAsync(ExchangeProtocolBuilder exchangeProtocolBuilder, ServiceEndPoint serviceEndpoint, HalibutTimeoutsAndLimits halibutTimeoutsAndLimits, ILog log, CancellationToken cancellationToken) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given the limits are set on the runtime, should they be given to the constructor of the factory and then used in the methods so wee don't have to pass them around to all methods
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May mean some methods below would benefit from not being static
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, a few suggestions. Nice approach marking the old limits as obsolete so you get compile time errors where they are being used 👍
c6e7d14
to
06b298a
Compare
Background
Statics make testing hard
HalibutLimits are statically set, which means all tests are using the same timeout. This is a problem since either we can have:
This introduces a new type
HalibutTimeoutsAndLimits
, which holds timeouts and limits and is used only in async paths.The existing
HalibutLimits
class is marked obsolete.[SC-45207]
Related to OctopusDeploy/Issues#8266
How to review this PR
Quality ✔️
Pre-requisites