aspnetcore
aspnetcore copied to clipboard
Customizable serverTimeoutInMilliseconds in Server-Side Blazor
When debugging a server-side blazor app we often see this error pop up in the client (browser) while stepping through code:
Error: Connection disconnected with error 'Error: Server timeout elapsed without receiving a message from the server.'.
This can be pretty disruptive, requiring a refresh of the browser to recover after continuing the debug session.
Presumably, this happens because the server isn't sending keepalives on the SignalR connection while I'm stepping through code in the debugger. When running in a 'development' environment, I'd like to be able to customize the serverTimeoutInMilliseconds on the HubConnection to prevent this. I see examples of how to customize the SignalR settings for a server-side Blazor app using code like this:
<environment exclude="Development">
<script src="_framework/blazor.server.js"></script>
</environment>
<environment include="Development">
<script src="_framework/blazor.server.js" autostart="false"></script>
<script>
(function start() {
Blazor.start({
logLevel: 1, // LogLevel.Debug
configureSignalR: builder => builder.configureLogging("debug") // LogLevel.Debug
});
})();
</script>
</environment>
However, I'm unable to change the serverTimeoutInMilliseconds setting. This setting is available on the HubConnection object, but I don't have access to that object. I only have access to HubConnectionBuilder. And HubConnectionBuilder doesn't have a serverTimeoutInMilliseconds property.
It seems like adding support for configuring the serverTimeoutInMilliseconds via HubConnectionBuilder would be the simplest solution.
Even nicer would be some way to configure this setting in my Startup.cs, probably with a new property available somewhere under the AddServerSideBlazor call, similar to how I can customize the server-side hub settings via AddServerSideBlazor().AddHubOptions(options => ... ).
@mvarblow thanks for contacting us.
I believe there's already an issue open for this. @anurse is this something that can be done at the signalr level, even if its for all hubs, so that customers can get unblocked?
When debugging a server-side blazor app we often see this error pop up in the client (browser) while stepping through code:
Yeah, debugging is a problematic thing for timeouts. We actually disable server-side timeouts when a debugger is attached but can’t control client-side timeouts in the same way.
@anurse is this something that can be done at the signalr level, even if its for all hubs, so that customers can get unblocked?
Right now it can only be set on the HubConnection in the client. Either Blazor will have to expose the HubConnection or SignalR will have to add support for setting the property in HubConnectionBuilder (or both). Seems like a reasonable ask, but it’s new API and not patchable. If someone wants to file a bug for the SignalR fix that would be great (on my phone at the moment).
In the short-term (since adding new APIs is a 5.0 change) is it possible to dig the HubConnection object out of Blazor somehow? Remember that “private” in TypeScript doesn’t mean anything :). It might also be possible to hack in a hook somehow. If it’s ONLY used in Development, it would be fairly reasonable to do something sneaky here ;).
Even nicer would be some way to configure this setting in my Startup.cs, probably with a new property available somewhere under the AddServerSideBlazor call, similar to how I can customize the server-side hub settings via AddServerSideBlazor().AddHubOptions(options => ... ).
We don’t support setting client options via server configuration by design. It was something that ASP.NET SignalR (for OWIN/System.Web apps) supported but it causes problems. This is the client’s timeout so it should be owned/set by the client.
Thanks, @anurse. I'd love to try something sneaky, but I haven't been able to come up with any way to get a hold of that HubConnection object. I don't see it getting stashed away anywhere. It seems to be referenced only as a method-scoped variable or parameter. And it doesn't seem to be passed to any functions I can hook into. As far as I can tell this internal implementation detail was successfully kept internal.
We've moved this issue to the Backlog milestone. This means that it is not going to happen for the coming release. We will reassess the backlog following the current release and consider this item at that time. However, keep in mind that there are many other high priority features with which it will be competing for resources.
Thanks for contacting us.
We're moving this issue to the Next sprint planning milestone for future evaluation / consideration. We will evaluate the request when we are planning the work for the next milestone. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.
hi, is there any update on this feature request? in stress test our blazor server application is not sending the keepalive message in 30s and we see this server timeout error periodically.
this is the log on ws traffic, during busy loading time, the down message gap was more than 30s, and client terminated the connection.
Here's how I worked around this issue:
Blazor.start({
configureSignalR: function (builder) {
let c = builder.build();
c.serverTimeoutInMilliseconds = 60000;
c.keepAliveIntervalInMilliseconds = 30000;
builder.build = () => {
return c;
};
};
});
Just FYI for everybody, the above code is placed inside _Layout.cshtml typically between the <environment include="Development"> tags.
Thanks for contacting us.
We're moving this issue to the .NET 8 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s).
If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues.
To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.
This is resolved now. I understand the feedback.
~I'm working on the doc updates to cover this. The workaround code shown above doesn't work, but setting the props on the hub connection in a component works for the client on both WASM and Server. The following approach for connection building is what what we show in the Blazor-SignalR tutorial. The following values are purely for demo purposes and aren't meant to make sense.~
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chathub"))
.Build();
hubConnection.ServerTimeout = TimeSpan.FromSeconds(61);
hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(31);
hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(17);
Logger.LogInformation($"\n\n\nhubConnection.ServerTimeout: {hubConnection.ServerTimeout}" +
$"\nhubConnection.HandshakeTimeout: {hubConnection.HandshakeTimeout}" +
$"\nhubConnection.KeepAliveInterval: {hubConnection.KeepAliveInterval}\n\n");
hubConnection.On<string, string>( ... );
await hubConnection.StartAsync();
}
~Output ...~
hubConnection.ServerTimeout: 00:01:01 hubConnection.HandshakeTimeout: 00:00:31 hubConnection.KeepAliveInterval: 00:00:17
~The code pitched earlier for Blazor.start is ineffective for both hosting models AFAICT. I'll angle coverage this way for Blazor Server and hosted WASM for the server (not shown here, but it will be on the PR) and client (shown above). If there's any further discussion on the doc updates, feel free to add remarks to the docs tracking issue at https://github.com/dotnet/AspNetCore.Docs/issues/27320. I've placed a copy of this comment on the docs issue.~
@guardrex I am not sure what you are trying to get at.
The snippet you described is for opening a hub within the app, not for configuring the Blazor Server hub.
For that, the only thing that works is:
Blazor.start({
configureSignalR: function (builder) {
let c = builder.build();
c.serverTimeoutInMilliseconds = 60000;
c.keepAliveIntervalInMilliseconds = 30000;
builder.build = () => {
return c;
};
};
});
This is resolved now. I understand the feedback.
~Our tutorials show building a hub connection on the client in a component as I show above. Using the code that you just showed doesn't seem to have an effect on that hub connection for the server timeout or the keep-alive. Setting the properties directly on the hub connection do work.~
~I might be ... perhaps very likely now that you just said that ... misinterpreting what the difference is for setting the properties on the hub connection (after building a HubConnectionBuilder) in a component and setting the serverTimeoutInMilliseconds and keepAliveIntervalInMilliseconds in Blazor.start. That needs to be explained to me further.~
~Also, I haven't been able to figure out how to log that those settings for serverTimeoutInMilliseconds and keepAliveIntervalInMilliseconds have been set anywhere else in the app (e.g., in a component) via C#. I can perform trace logging on the hub connection built in the component, but the logging doesn't indicate anything about the values of serverTimeoutInMilliseconds and keepAliveIntervalInMilliseconds. I presume that they could be read via JS interop in a component tho, which is something that I didn't try.~
This is resolved now. I understand the feedback.
~I see you say ...~
The snippet you described is for opening a hub within the app, not for configuring the Blazor Server hub.
~Let me think about that a little more. Keep in mind that I'm not a SignalR guy really. What I know has come about via Blazor docs, not from using SignalR to any great degree outside of sample apps and working on our Blazor text. I never used it professionally prior to writing here.~
@surayya-MS can you help bring clarity to the docs?
This is resolved now. I understand the feedback.
~I'll just add one more bit on this.~
not for configuring the Blazor Server hub.
~Why isn't that performed in Program.cs as ...~
builder.Services.AddServerSideBlazor()
.AddHubOptions(options =>
{
options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
});
~... and besides that ... what about hosted WASM? We'd show this for the Server project ...~
builder.Services.AddSignalR(options =>
{
options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
});
~... and then in the Client project in a "opening a hub within the app" (or a component of a Blazor Server app) ...~
hubConnection.ServerTimeout = TimeSpan.FromSeconds(60);
hubConnection.HandshakeTimeout = TimeSpan.FromSeconds(30);
hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(15);
~Those server (Blazor Server, Program.cs) settings don't change the values of the hub connection on the client ("opening a hub within the app"). Those server (Blazor WASM, Program.cs) settings also don't change the values of the hub connection on the client in a hosted WASM app either. Setting them on the HubConnection in the components in both cases does work.~
~Ok ... I'll wait and move on to something else until we get the 🦖 straightened out 😄.~
@guardrex that controls the timeouts on the server waiting on clients, the other controls the timeouts from the client waiting on the server.
n
It is not possible to set serverTimeoutInMilliseconds in Program.cs like so builder.Services.AddServerSideBlazor() .AddHubOptions(options => ..) . This is basically what the author of the issue asks us to implement. Either this or through Blazor.start({ configureSignalR: builder => ...})
The workaround works because it overrides the build method of the builder to return the hub connection with set serverTimeoutInMilliseconds.
Blazor.start({
configureSignalR: function (builder) {
let c = builder.build();
c.serverTimeoutInMilliseconds = 60000;
c.keepAliveIntervalInMilliseconds = 30000;
builder.build = () => {
return c;
};
};
});
Remarks removed and summarized (thank goodness! 🙈) on the docs PR at https://github.com/dotnet/AspNetCore.Docs/pull/27374.
Tagging this as SignalR as we would like to be able to set these options in the builder directly rather than having to expose an extra knob.