1

I'm doing SignalR server-to-client streaming using System.Threading.Channel with a .NET client. The usage is fairly basic, similar to what is described in the introductory docs.

The hub code is similar to this:

public ChannelReader<byte[]> Retrieve(Guid id, CancellationToken cancellationToken)
{
    var channel = Channel.CreateBounded<byte[]>(_limit);
    _ = WriteItemsAsync(channel.Writer, id, cancellationToken);
    return channel.Reader;
}

private async Task WriteItemsAsync(ChannelWriter<byte[]> writer, Guid id, CancellationToken cancellationToken)
{
    Exception localException = null;
    try
    {
        //loop and write to the ChannelWriter until finished
    }
    catch (Exception ex)
    {
        localException = ex;
    }
    finally
    {
        writer.Complete(localException);
    }
}

And client similar to this:

var channel = await hubConnection.StreamAsChannelAsync<byte[]>("Retrieve", _guid, cancellationTokenSource.Token);

while (await channel.WaitToReadAsync())
{
    while (channel.TryRead(out var data))
    {
        //handle data
    }
}

When my hub method is done streaming, it calls Complete() on its ChannelWriter. SignalR, presumably, internally sees the Complete call on the corresponding ChannelReader, translates that into an internal SignalR message and delivers that to the client. The client's own ChannelReader is then marked as complete by SignalR and my client code wraps up its own work on the stream.

Is that "completed" notification, from server to client, guaranteed to be delivered? In other cases where the hub is broadcasting non-streaming messages to clients, it generally "fires and forgets", but I've got to assume that calling Complete on the streaming Channel has acknowledged delivery, otherwise the client could be in a state where it is holding a streaming ChannelReader open indefinitely while the server sees the stream as closed.

Less important to the question, but the reason I ask is that I am trying to narrow down just such a scenario where a data flow pipeline that consumes a SignalR streaming interface very occasionally hangs, and it seems that the only point where it is hanging is somewhere in the SignalR client.

koopaking3
  • 3,375
  • 2
  • 25
  • 36
  • SIgnalR is not a reliable message bus. It has no acks, and there are several situation where the client will not get a message. If you need to guarantee reliability you will need to implement this yourself and deal with the 2 generals problems and all the other issues. Its worth noting in most cases that singalr use cases are not so worried about life or death reliability and is focused on a lightweight scalable "*good enough*" messaging – TheGeneral Feb 11 '21 at 03:09
  • I'm aware that normal server to client messaging is not guaranteed in SignalR, but if that is the case for the new streaming interfaces, it seems you are being forced into a infinite blocking situation I described above. The client must use a Channel or IAsyncEnumerable to consume, which must rely on receiving Completion in order to stop blocking. If that's the case I guess I'll need to implement my own timeout on the client. – koopaking3 Feb 11 '21 at 03:26
  • 2
    This might be a good question for the signalR team on github, I honestly don't know the internals well enough to know how the client will deal with a connection issue in regards to streams and whether it will just hang in a rare situations and eventually time out. My gut feeling is this might need to be timeded out manually (in some way or another, for sanity), however it would be nice to get an authorative answer to this, as its fairly interesting and i would think a commonly pondered question – TheGeneral Feb 11 '21 at 03:34

1 Answers1

1

I asked the devs over on github: https://github.com/dotnet/aspnetcore/issues/30128.

Here is a quick summary:

"Messages over SignalR are as reliable as TCP. Basically what this means is that either you get the message, or the connection closes. In both cases the channel on the client side will be completed."

And:

"Right it should never hang forever. It'll either send the complete message or disconnect. Either way, a hang would be a bug in the client"

koopaking3
  • 3,375
  • 2
  • 25
  • 36