handle errors gracefully with ngrx efffects - ngrx

I have a scenario, where in when a http port error occurs, the effects gets unsubscribed, and doesn't work anymore. For Example,
#Effect() newMessages$ : Observable<any> = this.actions$
.ofType(SEND_NEW_MESSAGE_ACTION)
.switchMap(action => this.threadsService.saveNewMessage(action.payload))
.catch(() => Observable.of(new ErrorOccurredAction("Error Ocurred while saving
message")) );
lets say error occurs, and it gets displayed in in the UI in the messages section. However after that, the send new message (post) button doesn't do another post, as the effect is no longer subscribed. Any idea how to handle this gracefully?

There are basically two ways (probably more) what you could do:
1. Handle the error inside the switchMap:
#Effect() newMessages$ = this.actions$
.ofType(SEND_NEW_MESSAGE_ACTION)
.switchMap(action => this.threadsService.saveNewMessage(action.payload)
.catch(() => Observable.of(new ErrorOccurredAction("Error Ocurred while saving message")))
);
Meaning, that if there is an error in saveNewMessage, it will not affect the effect, just the single run of saveNewMessage.
2. Re-subscribe the original observable (see the catch-documentation)
#Effect() newMessages$ = this.actions$
.ofType(SEND_NEW_MESSAGE_ACTION)
.switchMap(action => this.threadsService.saveNewMessage(action.payload))
.catch((err, orig) => orig.startWith(new ErrorOccurredAction("Error Ocurred while saving message")));
What the catch here does, it will return the original stream(basically a clean version of newMessages$) for the old subscription to switch the subscription to and initially emits the ErrorOccurredAction.
Both solutions are technically valid solutions, personally I prefer the first one, because it seems to be more intuitive to read for someone who it not super-familiar with the rxjs-api. (But that's just my personal taste)

Related

Moving Error message from error queue back to original queue with Rebus

Is it possible to move error message from error queue to its original queue, programmatically or via UI?
Update
Questions below on the code below:
1 Does the code below apply to Publiser or Subscriber or both?
The code below:
Configure.With(activator)
.Transport(t => (...)) //< use queue "error" here
.Routing(r =>
{
r.AddTransportMessageForwarder(async transportMessage =>
{
var sourceQueue = transportMessage.Headers.TryGetValue(Headers.SourceQueue, out var result)
? result
: throw new ArgumentException($"Could not find '{Headers.SourceQueue}' header");
return ForwardAction.ForwardTo(sourceQueue);
});
})
.Start();
2 Transport method below works for my code. However, the code above suggests using error queue name, will it work?
Where are the Publiser and Subscriber queue name like below specified if the code above is used?
Please provide code for pub sub pattern.
Publisher:
.Transport(t => t.UseAzureServiceBus(Consts.ServiceBusConnectionString, Consts.Publisher))
Subscriber:
.Transport(t=>t.UseAzureServiceBus(Consts.ServiceBusConnectionString, Consts.Subscriber1))
https://github.com/rebus-org/Rebus/wiki/Transport-message-forwarding
Since Rebus uses ordinary queues as its dead-letter queues, it's quite easy to start a bus instance with error as the input queue – then you can e.g. use Rebus' built-in transport message forwarding capability to do that you want to the messages – e.g. forward them to their source queues:
Configure.With(activator)
.Transport(t => (...)) //< use queue "error" here
.Routing(r =>
{
r.AddTransportMessageForwarder(async transportMessage =>
{
var sourceQueue = transportMessage.Headers.TryGetValue(Headers.SourceQueue, out var result)
? result
: throw new ArgumentException($"Could not find '{Headers.SourceQueue}' header");
return ForwardAction.ForwardTo(sourceQueue);
});
})
.Start();
or whatever you want in there.
There also exists a UI, Fleet Manager, that can do this – it replaces the need for dead-letter queues entirely, as it stores failed messages in its database and makes it possible to return the failed messages to their source queues (or another queue, if that's what you want), but it's only available if you're a Rebus Pro subscriber.
Update (with answers to the questions in your update):
1) AddTransportMessageForwarder is only relevant for an endpoint that receives messages.
2) It's the "queue name" specified as an argument to the .Useblablabla method. For example, with Azure Service Bus it would read
.Transport(t => t.UseAzureServiceBus(Consts.ServiceBusConnectionString, "error"))

Await the asynchronous dispatch of an action in redux-observable

I have an epic which dispatches an action that is handled by a redux middleware, which returns a promise once the action is dispatched. The epic looks approximately like this:
const myEpic = action$ =>
action$.pipe(
ofType(SAVE_ACTION),
switchMap(action => [
saveData(action.payload)
])
)
When the SAVE_ACTION is dispatched, it is picked up by the epic, which dispatches an action created by the saveAction action creator.
This resulting action is intercepted by a redux middleware (specifically redux-axios-middleware) which does an HTTP request and converts the result of dispatching such action to a promise, which has the standard behavior of resolving when the HTTP request succeeds and rejecting when the HTTP request fails.
What I would need to do is do additional work once that promise is resolved, within this epic, because I need data contained both in the payload of the initial SAVE_ACTION action and the payload of the HTTP response.
Because the middleware also dispatches actions upon completion of the underlying HTTP request, it would be easy to write an additional epic to do the additional work I need to do in a distinct epic, but I wouldn't have access to the payload of the initial SAVE_ACTION anymore, so I'm trying to figure out if this can be handled all within a single epic, and I haven't found a way so far. I have found online posts like this one, which is very informative, but still doesn't address the issue of awaiting the dispatch of an action, rather than a plain observable.
One approach would be to use merge like this:
import { merge } from 'rxjs/observable/merge';
const myEpic = action$ => {
let initialPayload = null;
return merge(
action$.pipe(
ofType(SAVE_ACTION),
switchMap(action => {
initialPayload = action.payload;
return [saveData(action.payload)]
})
),
action$.pipe(
ofType(SAVE_ACTION_SUCCESS),
mergeMap(action => {
// Use initialPayload here.
})
),
)
}

Pause epic when criteria met, then emit buffered action when criteria complete

I am working on an application whereby I we periodically persist information to a server when a user navigates between pages.
We currently do this by scheduling a "persist" action, which propagates a sequenced number of events, before finishing with a "persist_end" action. Currently, if a user navigates quickly, these grouped actions can intercept each other, resulting in various problems. I thought I could buffer the starting action and wait until the ending action was executed.
I've created a similar example using the ping-pong example from Redux-Observables site: https://codepen.io/dualcyclone/pen/GOZRxW?editors=0011
const actionPauser = new BehaviorSubject(false);
const pingEpic = action$ =>
action$.ofType(PING)
.do(action => console.log(action)) // check if action caught by epic
.buffer(actionPauser.asObservable().filter(paused => !paused))
.do(eh => console.log('buffered? ',eh)) // check if buffered actions is occurring
.do(() => actionPauser.next(true)) // tell pauser to pause
.map((buf) => buf[buf.length-1])
.filter(action => action !== undefined)
.delay(1000)
.mapTo({ type: PONG });
const pauseEpic = action$ =>
action$.ofType(PONG)
.delay(1000)
.do(() => actionPauser.next(false)) // tell pauser to not pause
.mapTo({ type: PING });
The premise is similar, I am allowing the user to press the "start PING" button as often as they like, the epic that is listening to this should check to see if there is a ping action currently occuring (by the "actionPauser" BehaviorSubject), and queue any actions until a previous ping action has completed.
The epic should emit the most recent buffered action, so it filters the buffered list then passes through the latest one.
What I can't seem to understand is - the console log to indicate how many buffered actions there are fires as soon as the page loads; which may indicate a problem with the way this is built - am I missing something?
So, whilst the output of the actions isn't exactly desirable (theres not really much I can do about it given the starting action is emitted by a user event), the suggestion Cartant recommended actually does precisely what I need.
Audit:
Ignores source values for a duration determined by another Observable, then emits the most recent value from the source Observable, then repeats this process.
In essence, this allows me to ignore multiple emitted 'PING' events whilst one is currently ongoing. It will then continue the execution of the last most recent 'PING' event, so the output we see is as follows:
(click) PING (click) PING (click) PING (click) PING PONG DONE PONG DONE
The first and last 'PING' actions are the only ones propagated through the Epic, so we see two final PONG actions, both followed by a DONE action.
So, here is the answered example (as also seen on my codepen here)
const pingEpic = action$ =>
action$.ofType(PING)
  .audit(() => actionPauser.filter(paused => !paused))
.do(() => actionPauser.next(true))
.delay(1000)
.mapTo({ type: PONG });
const pauseEpic = action$ =>
action$.ofType(PONG)
.delay(1000)
  .mapTo({ type: DONE })
.do(() => actionPauser.next(false));
Sounds like concatMap might work well in this case.
Projects each source value to an Observable which is merged in the output Observable, in a serialized fashion waiting for each one to complete before merging the next.
So in the case below, only one timer is running at a time. Any PINGs that come in while the previous is still waiting will be buffered.
const pingEpic = action$ =>
action$.ofType(PING)
.concatMap(() =>
Observable.timer(1000)
.mapTo({ type: PONG })
);
https://jsbin.com/gocezut/edit?js,output
(rapidly click four times)
PING
PING
PING
PING
(1000ms pass)
PONG
(1000ms pass)
PONG
(1000ms pass)
PONG
(1000ms pass)
PONG
Remember that most redux-observable questions can be reframed to just be regular RxJS questions, broadening the resources and help you can find. That's the beauty of redux-observable: it's almost entirely just regular RxJS patterns.

ngrx/effects errors not caught by global handler

We have effects defined as:
#Effect()
loadAll$: Observable<Action> = this.actions$
.ofType(actions.ActionTypes.LOAD_ALL)
.map(action => action.payload)
.switchMap((options) => {
return this.service.loadAll(options)
.map(all=> new actions.LoadAllCompleteAction(all))
.catch(() => Observable.of(new actions.LoadAllFailureAction(null)));
});
there is a reducer listening for LoadAllFailureAction and does changes to some flags in store.
Now, if in handling that action i do object destructuring of that null (sent in LoadAllFailuerAction), which producec TypeError, that error is not caught by GlobalErrorHandler (which catches uncaught exceptions). Also, effects stops running.
On the other hand, with exactly the same effects/reducers setup, if i manually do throw Error() in reducer, it does get caught.
Now that's a complete mystery for me.
Any thoughts?

Rebus MSMQ appears to be losing messages

I have followed the pub/sub demo with msmq and am loosing messages when the publisher is started before the subscribers. The msmq has already been created.
My Publisher code in one console app
_activator = new BuiltinHandlerActivator();
Configure.With(_activator)
.Transport(t => t.UseMsmq("PaymentsToTake"))
.Subscriptions(s => s.StoreInMemory())
.Start();
/* In the timer code */
MyDateMessage m = new MyDateMessage()
{
NowTime = DateTime.Now,
Counter = _index
};
_activator.Bus.Publish(m).Wait();
_index++;
My Subscriber Code in another console app
_activator = new BuiltinHandlerActivator();
_activator.Register(() => new PrintDateTime());
Configure.With(_activator)
.Transport(t => t.UseMsmq("PaymentsToTake-Receiver1"))
.Routing(r => r.TypeBased().Map<MyDateMessage>("PaymentsToTake"))
.Start();
_activator.Bus.Subscribe<MyDateMessage>().Wait();
Results
When I run the subscriber, I get the message Sending MyDateMessage -> and then when I run the consumer, the first message that comes up is "53 The time is" hence messages 0-52 were lost!
I suspect this is because you are using the in-mem subscription storage, meaning that the publisher does NOT remember who subscribed when it was previously running.
For most (if not all) real-world scenarios, you should choose some kind of persistent storage for subscriptions. It could be a database like SQL Server, or it could even be a local JSON file.
You just need to change the line
.Subscriptions(s => s.StoreInMemory())
into something like e.g.
.Subscriptions(s => s.UseJsonFile(#"subscriptions.json"))
Could you try and see if that fixes your problem? :)

Resources