2

I have the following Reactive Extensions Subject and need to log exceptions, and also shutdown this potentially blocking async task created on Observable.FromAsync cleanly.

In relation to this and on cancelling the token, the exception TaskCanceledException would be thrown by anything waiting on the token.

How can i trap these exceptions - ignoring the TaskCanceledException as that is expected on shutdown, and logging the rest?

internal sealed class TradeAggregator : IDisposable
{
    private readonly Subject<TradeExecuted> feed = new();
    private readonly CancellationTokenSource cts = new();
    private bool isStopped;
    private bool isDisposed;

    public TradeAggregator(Func<TradeAggregate, CancellationToken, Task> dispatch)
    {
        feed
            .GroupByUntil(x => (x.Execution.Contract.Symbol, x.Execution.AccountId, x.Tenant, x.UserId), x => Observable.Timer(TimeSpan.FromSeconds(5)))
            .SelectMany(x => x.ToList())
            .Select(trades => Observable.FromAsync(() => dispatch(AggregateTrades(trades), cts.Token)))
            .Concat() // Ensure that the results are serialized.
            .Subscribe(cts.Token); // Check status of calls.
    }

    private TradeAggregate AggregateTrades(IEnumerable<TradeExecuted> trades)
    {
        // Do stuff.
        return new TradeAggregate();
    }

    public void OnNext(ExecutedTrade trade) => this.feed.OnNext(trade);

    public void Stop()
    {
        if (isStopped) return;
        isStopped = true;
        cts.Cancel();
    }

    public void Dispose()
    {
        if (isDisposed) return;
        isDisposed = true;
        
        Stop();
        feed.Dispose();
        cts.Dispose();
    }
}
morleyc
  • 2,169
  • 10
  • 48
  • 108

1 Answers1

3

Use a different Subscribe overload:

    feed
        .GroupByUntil(x => (x.Execution.Contract.Symbol, x.Execution.AccountId, x.Tenant, x.UserId), x => Observable.Timer(TimeSpan.FromSeconds(5)))
        .SelectMany(x => x.ToList())
        .Select(trades => Observable.FromAsync(() => dispatch(AggregateTrades(trades), cts.Token)))
        .Concat() // Ensure that the results are serialized.
        .Subscribe(
          x => {}, //OnNext, do nothing
          e => {  //OnError, handle
            if(e.GetType() == typeof(TaskCanceledException))
              ; //ignore
            else
            {
              ; //log
            }
          },
          cts.Token
        );
Shlomo
  • 14,102
  • 3
  • 28
  • 43