Using Observables to process queue messages which require a callback at end of processing? - rxjs

This is a bit of a conceptual question, so let me know if it's off topic.
I'm looking at writing yet another library to process messages off a queue - in this case an Azure storage queue. It's pretty easy to create an observable and throw a message into it every time a message is available.
However, there's a snag here that I'm not sure how to handle. The issue is this: when you're done processing the message, you need to call an API on the storage queue to actually delete the message. Otherwise the visibility timeout will expire and the message will reappear to be dequeued again.
As an example, here's how this loop looks in C#:
public event EventHandler<string> OnMessage;
public void Run()
{
while(true)
{
// Read message
var message = queue.GetMessage();
if (message != null)
{
// Run any handlers
OnMessage?.Invoke(this, message.AsString);
// Delete off queue when done
queue.DeleteMessage(message);
}
else
{
Thread.Sleep(2500);
}
}
}
The important thing here is that we read the message, trigger any registered event handlers to do things, then delete the message after the handlers are done. I've omitted error handling here, but in general if the handler fails we should NOT delete the message, but instead let it return to visibility automatically and get redelivered later.
How do you handle this kind of thing using Rx? Ideally I'd like to expose the observable for anyone to subscribe to. But I need to do stuff at the end of processing for that message, whatever the "end" happens to mean here.
I can think of a couple of possible solutions, but I don't really like any of them. One would be to have the library call a function supplied by the consumer, that takes in the source observable, hooks up whatever it wants, then returns a new observable that the library can then subscribe on to do the final cleanup. But that's pretty limiting, as consumers basically only have one shot to hook up to the messages, which seems pretty limiting.
I guess I could put the call to delete the message after the call to onNext, but then I don't know if the processing succeeded or failed unless there's some sort of back channel in that api I don't know about?
Any ideas/suggestions/previous experience here?

Try having a play with this:
IObservable<int> source =
Observable
.Range(0, 3)
.Select(x =>
Observable
.Using(
() => Disposable.Create(() => Console.WriteLine($"Removing {x}")),
d => Observable.Return(x)))
.Merge();
source
.Subscribe(x => Console.WriteLine($"Processing {x}"));
It produces:
Processing 0
Removing 0
Processing 1
Removing 1
Processing 2
Removing 2

Related

RX programming - how to get previous messages from MessageBroker?

I use UniRX (C#) which tries to resemble RXJS and others.
I try to make sure my network-dependent objects initialize after the data arrived.
Some of my objects get created and Subscribe later than MSGPlayerDataLoaded actually fired thus never proceed to OnPlayerDataLoaded.
protected virtual void Awake()
{
MessageBroker.Default.Receive<BaseMessage>().Where(msg => msg.id == GameController.MSGPlayerDataLoaded).Subscribe(msg => OnPlayerDataLoaded());
}
Is it possible to look into the past and grab old events since creation of MessageBroker?
From the documentation of RXJS I suspect that something like withLatestFrom could be of help, but it would need a dummy auxiliary stream that looks hacky.

Rxjs - How to retry an errored observable while informing UI of the error

Problem
Suppose there is a Http request observable that errored, we can just retry it. But I also want the UI to inform the user that this resource failed to load. What is the best architecture?
Intended Behavior for the Target Observable
Retry-able.
Long-running. Doesn't complete or error.
Shared. Does not generate unnecessary requests when multiple subscriber.
Load on need. Does not generate unnecessary requests when not subscribed.
Inform UI of the errors.
(3 and 4 can be achieved by shareReplay({bufferSize: 1, refCount: true}))
My Attempts
I think it's best to pass an error message to the downstream observer while keeping retrying the source. It causes minimum changes to the architecture. But I didn't see a way I can do it with Rxjs, because
retry() always intercepts the error. If you materialze the error, then retry() won't retry. If not, then no error will propagate to the downstream.
catchError() without rethrowing will always complete the stream.
Although let the UI observer tap(,,onError) and retry() can satisfy this need, but I think it is dangerous to let the UI take this responsibility. And multiple UI observer means a LOT of duplicated retries.
Well, I seem to have accidentally find the answer while browsing through the documentations.
It starts with the usage of the second parameter of the catchError. According to the documentation, retry is implemented by catchError. And we can express more logic with the lower-level catchError.
So it's just
catchError((err, caught) => {
return timer(RETRY_DELAY_TIME).pipe(
mergeMap(() => caught)
startWith(err)
);
})
It retries the observable, meanwhile sending error messages to the downstream observers. So the downstream is aware of the connection error, and can expect to receive retried values.
It sounds like you're looking for something akin to an NgRx side effect. You can encase it all in an outer Observable, piping the error handler to the inner Observable (your HTTP call), something like this:
const myObs$ = fromEvent('place event that triggers call here').pipe(
// just one example, you can trigger this as you please
switchMap(() => this.myHttpService.getResource().pipe(
catchError(err => handleAndRethrowError()),
retry(3)
),
shareReplay()
);
This way, if the request throws an error, it is retried 3 times (with error handling in the catchError block, and even if it fully errors out, the outer Observable is still alive. Does that look like it makes sense?

Why does Rxjs unsubscribe on error?

In short:
How to proceed listening after an error in stream without putting a .catch before every .subscribe?
If you need more details they are here:
Lets assume I have a Subject of current user or null. I get the data from API sometimes and send to the Subject. It updates the view accordingly.
But at some point error occurs on my server and I want my application to continue working as before but notify some places about the error and KEEP listening to my Subject.
Initially I thought that if I just do userSubject.error(...) it will only trigger .catch callback and error handlers on subscribes and skip all success handlers and chains.
And if after I call userSubject.next(...) all my chains and subscribers will work as before
BUT unluckily it is not the case. After the first uncaught .error it unsubscribes subscribers from the stream and they do not operate any more.
So my question: Why???
And what to do instead if I want to handle null value normally but also handle errors only in some places?
Here is the link to RxJs source code where Subscriber unsubscribes on error
https://github.com/ReactiveX/rxjs/blob/master/src/Subscriber.ts#L140
Rx observables follow the grammar next*(error|complete)?, meaning that they can produce nothing after error or complete notification has been delivered.
An explanation of why this matters can be found from Rx design guidelines:
The single message indicating that an observable sequence has finished ensures that consumers of the observable sequence can deterministically establish that it is safe to perform cleanup operations.
A single failure further ensures that abort semantics can be maintained for operators that work on multiple observable sequences.
In short, if you want your observers to keep listening to the subject after a server error has occurred, do not deliver that error to the subject, but rather handle it in some other way (e.g. use catch, retry or deliver the error to a dedicated subject).
Every Observable emits zero or more next notifications and one error or complete but never both.
For this reason, Subjects have internal state.
Then it depends how you construct your chain. For example you can use retry() to resubscribe to its source Observable on error.
Or when you pass values to your Subject you can send only next notifications and ignore the other two:
.subscribe(v => subject.next(v));
Or if you want to throw error when the user is null you can use any operator that captures exceptions and sends them as error notifications. For example like this:
.map(v => {
if (v === null) {
throw new Error("It's broken");
}
return v;
})
Anyway it's hard to give more precise advice without any code.

Observing when stream is unsubscribed

I have an RxJS observable stream that I'm sharing like the following:
var sub = Observable.create(obs => {
// logic here
return () => {
// call rest service to notify server
};
})
.publish()
.refCount();
When the last subscriber unsubscribes, I need to make a REST request. The obvious choice is to add that call into the return cleanup function - but you then have broken out of any observable sequence and any errors etc aren't easily handled.
I could just use a Subject, push a value onto it in the cleanup function, and observe it elsewhere with the REST call hanging off that.
Ideally I'd do something like concatenating to the disposed stream with my REST call (concat obviously wouldn't work as it's not completing).
Does anyone have any suggestions for the cleanest way of handling this? All the options above seem a bit clunky and I feel like I've missed something.
You could implement a finally(...) in your stream, that does the cleanup.
The finally is automatically executed when the stream finalizes (error or complete).
Note: This will not work when you unsubscribe manually and not call complete on your stream.

Not receiving Apache Camel Event Notifications under the smallest load

I have extended EventNotiferSupport, and set the isEnable() to respond True for all events. I have a notify() that logs what events I receive and the corresponding Exchange ID for the event.
I have added my ExchangeMessageNotifier with this.context.getManagementStrategy().addEventNotifier(this.exchangeMessageNotifier);
I run my program under basically no load, sending 1 message at a time 1 second delay between messages into Camel to send out. Everything works the way I expect. I receive my events everything looks good.
I decrease the delay between messages to 0 milliseconds, and I find that 1 out of approximately 20 messages I fail to receive one of the Events, (Often the Completed event).
Add a second thread sending at the same rate and I don't get any events for any messages.
What am I missing? I've done searches and I don't find anything that I need to do differently. Is there something I am missing?
I am using Apache Camel 2.16.3, and moved to 2.18.1 still see the same behavior.
Well found my own answer. Part of the fun of inheriting code without any informaiton.
In your implementation of the EventNotifierSupport you need to override the doStart() method and configure the EventNotifierSupport for what events you wish to receive.
protected void doStart() throws Exception {
// filter out unwanted events
setIgnoreCamelContextEvents(true);
setIgnoreServiceEvents(true);
setIgnoreRouteEvents(true);
setIgnoreExchangeCreatedEvent(true);
setIgnoreExchangeCompletedEvent(false);
setIgnoreExchangeFailedEvents(true);
setIgnoreExchangeRedeliveryEvents(true);
setIgnoreExchangeSentEvents(false);
}
This is in addition to doing the following:
public boolean isEnabled(EventObject event) {
return true;
}
Which enables you to determine if you want a particular event, out of the selected groups you had set in the doStart().
Once these changes were in I was receiving consistent events.

Resources