I'm new to AWS SWF and my task is to give notification(thru email) when an activity fails during first attempt ONLY. I'm using Settable<Boolean>
as my flag but the value is not reliable because the workflow is running async. Here's my code:
final AsyncExecutor asyncExecutor = new AsyncRetryingExecutor(retryPolicy, workflowClock);
final Settable<Boolean> notifyException = new Settable<>();
new TryCatch() {
@Override
protected void doTry() throws Throwable {
asyncExecutor.execute(() -> new TryCatch() {
@Override
protected void doTry() throws Throwable {
Promise<ActivityOne> activityOne = activityOneClient.performAction(activityOneRequest);
}
@Override
protected void doCatch(Throwable e) throws Throwable {
if (!notifyException.isReady()) {
// PERFORM notification service here!!
notifyException.set(true);
} else {
// DO NOTHING. Notification is already sent the first time :)
}
throw e;
}
});
}
@Override
protected void doCatch(Throwable e) throws Throwable {
System.out.println("======");
System.out.println("CATCH ALL!! " + e.getMessage());
System.out.println("======");
}
};
The notifyException
's value is always changing even though I explicitly set its value to true
inside if statement
. The result of my code is it will perform more than 1 the notification service
.
====UPDATE===
final AsyncExecutor asyncExecutor = new AsyncRetryingExecutor(retryPolicy, workflowClock);
final Settable<Boolean> notifyException = new Settable<>();
new TryCatch() {
@Override
protected void doTry() throws Throwable {
asyncExecutor.execute(() -> new TryCatch() {
@Override
protected void doTry() throws Throwable {
Promise<ActivityOne> activityOne = activityOneClient.performAction(activityOneRequest);
}
@Override
protected void doCatch(Throwable e) throws Throwable {
if (!notifyException.isReady()) {
activityOneClient.notify();
notifyException.set(true);
}
throw e;
}
});
}
@Override
protected void doCatch(Throwable e) throws Throwable {
System.out.println("======");
System.out.println("CATCH ALL!! " + e.getMessage());
System.out.println("======");
}
};
When I re-throw the exception, the activityOneClient.notify()
will only be executed on the last retry of asyncExecutor
, so if I dont re-throw, the activityOneClient.notify()
will be executed automatically. I just need to notify if an exception occur for the first time.
the code looks okay-ish (haven't run it).
your problem is somewhere else. the way the flow framework works is that every time there is some work to do the workflow workers or the activity workers pick up the task, run it and report back the results. reporting back the results means that the result of the execution is captured in the workflow history.
whenever the history changes the decider (workflow) worker runs and makes a decision to what should happen next. under the covers the decider just replays all the history and makes all the decisions from the beginning of time. now, it the decisions match what's in the history the decider just goes on until it hits something new.
simple example. say you have a workflow that has 3 steps:
[ keep in mind that multiply activities can run at the same time, and if there isn't a link between output of an activity and input into the other the flow framework will move ahead and schedule multiple things in parallel. keeping it simple so that you get the idea ]
at T0 when the workflow starts there is no history, the decider runs and activity1 is scheduled.
at T1 the activity1 workers pick up the task, execute it and report the result.
at T2 as a result of activity1 updating history the decider is scheduled and runs + it reads the history. it sees activity1 should run, it sees it in the history, it sees that it completed, it schedules activity2
at T3 the activity2 workers pick up the task, execute it and report the result.
at T4 as a result of activity2 updating history the decider is scheduled and runs + it reads the history. it sees activity1 should run, it sees it in the history, it sees that it completed. it now sees that activity2 should run, it sees it in the history, it sees that it's completed and move on to activity3. it schedules activity3.
and so on.
your problem is that the code in the decider that dictates the flow is run every time the decider runs (every decision). so when the decider reaches that activity it will see that it has run and has thrown and exception and it will go though the code that sets the flag + sends the email.
when the exponential retries kick is, the flag will be set, but the code that sends the email will be run on every decision (the decider is basically stateless and the state is built based on the history).
what you could do in this particular case is to move the part that sends the email in its own activity. this way the decider will see it in the history and fast forward instead of running it each time.