简体   繁体   中英

AWS Simple workflow notification technique

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:

  • activity1
  • activity2 - depends on result of activity1
  • activity3 - depends on result of activity2

[ 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.

To notify only the first exception thrown, what I did is:

@Override
    protected void doTry() throws Throwable {
        asyncExecutor.execute(() -> new TryCatchFinally() {

            Throwable throwable = null;
            @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);
                }
                throwable = e;
            }

            @Override
            protected void doFinally() throws Throwable {
                if (throwable != null) {
                    throw throwable;
                }
            }
        });
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM