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?
Thx!
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.
Related
I have an hot IObservable<T> which may throw an exception. However, I would like to continue with it. I think I could use Retry operator for that. However, it would be great if I can also listen to any error in IObservable<T> through a separate IObservable<Exception>. Is it possible?
Your case is significantly more simplified in that you have a hot observable.
OnError is a notification outside your value stream, so we could materialize the notifications to retrieve the error. This still causes the tear-down of the stream with an OnCompleted, so you'll need to re-subscribe with Repeat.
var exceptions =
source
.Materialize()
.Where(notif => notif.Kind == NotificationKind.OnError)
.Select(notif => notif.Exception)
.Repeat();
Note
If you're using a Subject<T> for your hot observable, you might run into the usual problem of re-subbing a subject. A subject will replay its OnError or OnCompleted notifications for every new observer.
var source = new Subject<int>();
source.OnNext(1);
source.OnError(new Exception());
source.Subscribe(
i => Console.WriteLine(i),
ex => Console.WriteLine("Still got exception after the throw")
);
In this case your exception stream will go into an infinite re-subscription loop.
The premise of your question violates the observable contract:
An Observable may make zero or more OnNext notifications, each representing a single emitted item, and it may then follow those emission notifications by either an OnCompleted or an OnError notification, but not both. Upon issuing an OnCompleted or OnError notification, it may not thereafter issue any further notifications. (emphasis mine)
In other words, after your hot IObservable<T> throws an exception, the observable is ended. The observable of exceptions that comes out of that has a max count of one.
If you want to support a scenario where you re-start an observable after an exception, you're producing a stream of observables, or IObservable<IObservable<T>>. To work with that, here's a code sample:
var source = new Subject<Subject<int>>();
var exceptionStream = source
.SelectMany(o => o.Materialize())
.Where(n => n.Kind == NotificationKind.OnError)
.Select(n => n.Exception);
var itemStream = source
.SelectMany(o => o.Materialize())
.Where(n => n.Kind == NotificationKind.OnNext)
.Select(n => n.Value);
var items = new List<int>();
var exceptions = new List<Exception>();
itemStream.Subscribe(i => items.Add(i));
exceptionStream.Subscribe(e => exceptions.Add(e));
var currentSubject = new Subject<int>();
source.OnNext(currentSubject);
currentSubject.OnNext(1);
currentSubject.OnNext(2);
currentSubject.OnNext(3);
currentSubject.OnError(new Exception("First error"));
var currentSubject2 = new Subject<int>();
source.OnNext(currentSubject2);
currentSubject2.OnNext(4);
currentSubject2.OnNext(5);
currentSubject2.OnNext(6);
currentSubject2.OnError(new Exception("Second error"));
items.Dump(); //Linqpad
exceptions.Dump(); //Linqpad
I have written a subject that I use to push out messages that I receive from RabbitMQ.
I was expecting to always receive all messages on subscribe before finally is triggered but I am usually not receiving all messages and can see that that subscribe is called after finally is called.
_messageSummarySubject = new Subject<MessageSummary>();
_subscriber = _messageSummarySubject
.Finally(Finisehd)
.Buffer(TimeSpan.FromMilliseconds(100))
.SubscribeOn(Bootstrapper.Resolve<ISchedulerService>().DispatcherScheduler)
.ObserveOn(Bootstrapper.Resolve<ISchedulerService>().DispatcherScheduler)
.Subscribe(
x =>
{
//This is sometime called after finally
});
ThreadPool.QueueUserWorkItem(s => SomeAction(_messageSummarySubject));
SomeAction is basically calling
subject.OnNext(messageSummary);
a few times in a thread and once the thread is closed calling subject.OnCompleted();
Is this the correct behavior or am I doing something wrong?
Context: I've got many ConnectableObservables, almost all of which have a replay count of 1 or more. There are many observers of the observables subscribing and unsubscribing at any given time.
What I want: In many cases, when an observer subscribes to one of these observables, I don't care about the possible pre-existing emitted data that I'd receive because of the observable's data replay mechanism. The only data that the recently-subscribed observer is interested in, is data that is emitted after the moment of subscription.
const observable = Rx.Observable
.interval(100)
.take(4)
.publishReplay(3);
observable.connect();
Problem: As far as I can tell, when an observer subscribes to the observable, it has no way of knowing whether or not the data it observes was emitted before or after the moment of subscription.
observable.subscribe(x => console.log('observed', x));
setTimeout(() =>
observable.subscribe(y => console.log('delayed observed', y)),
400
);
The code above will output:
// => observed 0
// => observed 1
// => observed 2
// => delayed observed 0 **don't care**
// => delayed observed 1 **don't care**
// => delayed observed 2 **don't care**
// => observed 3
// => delayed observed 3
In this hypothetical situation, the delayed observer is only interested in data emitted after the moment of subscription; in this case, 3.
I've scoured the RxJS 5 reference docs and can't seem to find a silver-bullet operator to accomplish what I'm after. Any ideas?
You can use .skipUntil(Rx.Observable.timer(0)) because the replayed elements will be replayed synchronously, and skipUntil will take the rest of the Observable out of synchronous execution for exactly that moment when it would receive the replay values.
This code would produce the result you want:
const observable = Rx.Observable
.interval(100)
.take(4)
.publishReplay(3);
observable.subscribe(x => console.log('observed', x));
setTimeout(() =>
observable
.skipUntil(Rx.Observable.timer(0))
.subscribe(y => console.log('delayed observed', y)),
400
);
observable.connect();
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.3/Rx.min.js"></script>
Could you do something like this?
const observableNoReplay = Rx.Observable
.interval(100)
.take(4);
const observable = observableNoReplay
.publishReplay(3);
observable.connect();
You could subscribe to whichever observable you need at the time and not have to worry about any sort of silver bullet.
I'm using RxJava to pull out values from RabbitMQ. Here's the code:
val amqp = new RabbitQueue("queueName")
val obs = Observable[String](subscr => while (true) subscr onNext amqp.next)
obs subscribe (
s => println(s"String from rabbitmq: $s"),
error => amqp.connection.close
)
It works fine but now I have a requirement that a value should be pulled at most once per second while all the values should be preserved (so debounce won't do since it drops intermediary values).
It should be like amqp.next blocks thread so we're waiting... (RabbitMQ got two messages in queue) pulled a 1st message... wait 1 second... pulled a 2nd message... wait indefinitely for the next message...
How can I achieve this using rx methods?
Alternatively you could create a observable from a timer like that. I personally find this more elegant.
RabbitQueue amqp = new RabbitQueue("queueName");
Observable.timer(0, 1, TimeUnit.SECONDS)
.map(tick -> amp.next())
.subscribe(...)
One option may be to use the Schedulers API in combination with a PublishSubject as the observable.
Unfortunately, I don't know Scala syntax but here is the Java version you should be able to convert:
RabbitQueue amqp = new RabbitQueue("queueName");
Scheduler.Worker worker = Schedulers.newThread().createWorker();
PublishSubject<String> obs = PublishSubject.create();
worker.schedulePeriodically(new Action0() {
#Override
public void call() {
obs.onNext(amqp.next);
}
}, 1, 1, TimeUnit.SECONDS);
Your subscribe code from above would remain the same:
obs subscribe (
s => println(s"String from rabbitmq: $s"),
error => amqp.connection.close
)
I was wondering if there is a convenient method to check if an observable has been completed. For instance I have a test
test("An observable that tracks another observable is completed")
{
val sub = PublishSubject[Boolean](false)
val newOb = sub recovered // This methods returns an Observable[Try[T]]
val res = scala.collection.mutable.ListBuffer[Try[Boolean]]()
val cr = newOb subscribe( v => res += v, t => assert( false, "There shouldn't be an exception" ), () => println("Stream Completed") )
sub.onNext(true)
sub.onNext(false)
sub.onNext(true)
sub.onCompleted
assert( res.toList === List(Success(true), Success(false), Success(true) ))
newOb.isEmpty subscribe { v => assert( v == true, "Stream should be completed" ) }
}
The recovered method returns an Observable[Try[T]] and is an extension to the standard Observable. I want to check that the Observable[Try[T]] is completed when the source Observable is completed.
So I wrote a test with a Subject to which I Publish a few values and then eventually complete. Is there a simple way I can check to see that newOb is also completed? There is no method like isCompleted in Observable.
This is the essence of the pattern Observer, when there is a call onCompleted, the appropriate handler is triggered, and only it can be understood that the Observer completed. But I have heard that if the Observer has been completed and it is attached to the handler, it works immediately, but I think it has already been implemented at a lower level where asJavaObserver.
That link may help:
Netflix RxJava