RX.Net : Use Retry but log any Exception
Asked Answered
A

1

6

I am new to RX and have been investigating error handling and the use of Retry; I have the following (yes I know it's not a 'real' unit test but it gives me place to fiddle!!) and was wondering how I go about keeping the Retry but be able to log any Exception?

    [Test]
    public void Test()
    {
        var scheduler = new TestScheduler();

        var source = scheduler.CreateHotObservable(
            new Recorded<Notification<long>>(10000000, Notification.CreateOnNext(0L)),
            new Recorded<Notification<long>>(20000000, Notification.CreateOnNext(1L)),
            new Recorded<Notification<long>>(30000000, Notification.CreateOnNext(2L)),
            new Recorded<Notification<long>>(30000001, Notification.CreateOnError<long>(new Exception("Fail"))),
            new Recorded<Notification<long>>(40000000, Notification.CreateOnNext(3L)),
            new Recorded<Notification<long>>(40000000, Notification.CreateOnCompleted<long>())
        );

        source.Retry().Subscribe(
            l => Console.WriteLine($"OnNext {l}"), 
            exception => Console.WriteLine(exception.ToString()), // Would be logging this in production
            () => Console.WriteLine("OnCompleted"));

       scheduler.Start(
            () => source,
            0,
            TimeSpan.FromSeconds(1).Ticks,
            TimeSpan.FromSeconds(5).Ticks);
    }

Which results in...

OnNext 0
OnNext 1
OnNext 2
OnNext 3
OnCompleted

...which is exactly what I want to happen apart from fact I would like to log the Exception which occurs between 2 and 3.

Is there a way to allow the Subscriber to see the Exception in OnError (and log it) and then re-subscribe so it sees 3?

Adenoma answered 6/6, 2017 at 13:17 Comment(0)
G
8

You could achieve that with this:

source
    .Do(_ => { }, exception => Console.WriteLine(exception.ToString()), () => {})
    .Retry()
    .Subscribe(
        l => Console.WriteLine($"OnNext {l}"),
        //      exception => Console.WriteLine(exception.ToString()), // Would be logging this in production
        () => Console.WriteLine("OnCompleted")
    );

Just to clarify what's going on here: OnError is a terminating signal. If the error reached the subscription, that would terminate the rest of the stream. .Retry terminates the subscription, swallows the OnError, and then re-subscribes, melding the two subscriptions together. For example look at this:

source
    .StartWith(-1)
    .Retry()
    .Subscribe(
        l => Console.WriteLine($"OnNext {l}"),
        () => Console.WriteLine("OnCompleted")
    );

Your output would be

OnNext -1
OnNext 0
OnNext 1
OnNext 2
OnNext -1
OnNext 3
OnCompleted

The OnNext -1 shows up twice, because it shows up whenever you subscribe (which Retry does after the OnError.

Your test observable is frankly a bad test. It breaks the "Rx Contract" which is that notifications follow the following pattern:

OnNext* (OnCompleted | OnError)? 

That is, 0 or more OnNext notifications, followed by an optional OnError or an optional OnCompleted. No notifications of any type should follow either an OnError or an OnCompleted.

Greenbrier answered 6/6, 2017 at 14:51 Comment(6)
With regard to the the bad test; the result of trying to get a simple example together and failing!! :-( Thank you for the clarification, I hadn't fully appreciated how Retry functioned.Adenoma
What I actually have in my application is the hot observable being driven from FromEventPattern. I receive an event containing a byte[], I then Decrypt the byte[], establish the message type and deserialize accordingly; in the Observer.OnNext I carry out an action based on that message. What I don't want to happen is for the stream to terminate if I have any issues in those operations (decryption/serialization etc.). I just want to log the failure and continue on processing. Is that possible?Adenoma
"What I don't want to happen is for the stream to terminate" should be "What I don't want to happen is for the subscription to terminate "Adenoma
Yes, that's pretty much what .Retry does. It terminates and resubscribes, but in a transparent manner to downstream subscribers.Greenbrier
OK, thank you. So Retry is the correct approach but logging any failure is not feasible. Hope I am understanding that correctly!!Adenoma
You can log, it just has to be done prior to the Retry.Greenbrier

© 2022 - 2024 — McMap. All rights reserved.