I have a long-running class (not a BackgroundService) that has an internal Channel that I’m using to buffer incoming requests.
An Enqueue
method exists, which is as follows:
public async Task<bool> Enqueue(IMessage message)
{
bool result;
try
{
if (_cts.IsCancellationRequested)
{
_logger.LogInformation("{TypeName} cannot be enqueued because the queue is stopped.", message.GetType().Name);
result = false;
}
else
{
await _messageChannel.Writer.WriteAsync(message);
result = true;
_logger.LogInformation("Message {TypeName} written to internal channel.", message.GetType().Name);
}
}
catch (ChannelClosedException)
{
_logger.LogInformation("{TypeName} cannot be enqueued because the channel is closed.", message.GetType().Name);
result = false;
}
catch (Exception e)
{
_logger.LogError(e, "Error enqueuing {TypeName}", message.GetType().Name);
result = false;
}
return result;
}
In the class constructor, a message reader loop is started up using:
Task.Run(() => _MainMessageLoop(_cts.Token));
Where _MainMessageLoop looks like:
private async Task _MainMessageLoop(CancellationToken stoppingToken)
{
try
{
var channelReader = _messageChannel.Reader;
while (await channelReader.WaitToReadAsync(stoppingToken).ConfigureAwait(false))
{
try
{
var item = await channelReader.ReadAsync(stoppingToken).ConfigureAwait(false);
_logger.LogInformation("Message {TypeName} read from internal channel.", item.GetType().Name);
await _DoSomethingAsync(item);
}
catch (Exception e)
{
_logger.LogError(e, "Error doing something");
}
}
_logger.LogInformation("Channel reader loop exited {MethodName}.", nameof(_MainMessageLoop));
}
catch (OperationCanceledException)
{
_logger.LogDebug("OperationCanceled {MethodName}", nameof(_MainMessageLoop));
throw;
}
catch (Exception e)
{
_logger.LogError(e, "Error in {MethodName}", nameof(_MainMessageLoop));
throw;
}
}
This works great for awhile (a few hours with a very light message load) and then stops. Per the logs, the writes are still happening, but the Read side is getting stuck.
There are no log messages related to Read failures, Exceptions getting thrown, etc. The read loop just seems to stop.
I’ve tried several combinations of Bounded/Unbounded, TryRead
vs. ReadAsync
, each with roughly the same results. The writes are still happening (which means that the object is alive and well), without any luck.
Most of the docs don’t seem to hint at this, but I figure I’m doing something that’s not intended, or there’s a bug somewhere. Other Channels I’m using in the app are working fine, but they’re singletons at the HostedService level.
Has anyone run into something similar?
9