2424import java .time .temporal .ChronoUnit ;
2525import java .util .Date ;
2626import java .util .Locale ;
27+ import java .util .Optional ;
2728import java .util .Properties ;
2829import java .util .Random ;
2930
3536import org .quartz .impl .JobDetailImpl ;
3637
3738import com .google .common .annotations .VisibleForTesting ;
38- import com .google .common .base .Optional ;
3939import com .typesafe .config .Config ;
4040
4141import javax .inject .Inject ;
6868@ Slf4j
6969public class FlowLaunchHandler {
7070 private final MultiActiveLeaseArbiter multiActiveLeaseArbiter ;
71- private Optional < DagActionStore > dagActionStore ;
71+ private DagActionStore dagActionStore ;
7272 private final MetricContext metricContext ;
7373 private final int schedulerMaxBackoffMillis ;
7474 private static Random random = new Random ();
@@ -80,9 +80,14 @@ public class FlowLaunchHandler {
8080 @ Inject
8181 public FlowLaunchHandler (Config config ,
8282 @ Named (ConfigurationKeys .SCHEDULER_LEASE_ARBITER_NAME ) MultiActiveLeaseArbiter leaseArbiter ,
83- SchedulerService schedulerService , Optional <DagActionStore > dagActionStore ) {
83+ SchedulerService schedulerService , com . google . common . base . Optional <DagActionStore > optDagActionStore ) {
8484 this .multiActiveLeaseArbiter = leaseArbiter ;
85- this .dagActionStore = dagActionStore ;
85+
86+ if (!optDagActionStore .isPresent ()) {
87+ throw new RuntimeException ("DagActionStore MUST be present for flow launch handling!" );
88+ }
89+ this .dagActionStore = optDagActionStore .get ();
90+
8691 this .schedulerMaxBackoffMillis = ConfigUtils .getInt (config , ConfigurationKeys .SCHEDULER_MAX_BACKOFF_MILLIS_KEY ,
8792 ConfigurationKeys .DEFAULT_SCHEDULER_MAX_BACKOFF_MILLIS );
8893 this .schedulerService = schedulerService ;
@@ -99,51 +104,46 @@ public FlowLaunchHandler(Config config,
99104 * This method is used in the multi-active scheduler case for one or more hosts to respond to a launch dag action
100105 * event triggered by the scheduler by attempting a lease for the launch event and processing the result depending on
101106 * the status of the attempt.
102- * @param jobProps
103- * @param dagAction
104- * @param eventTimeMillis
105- * @param isReminderEvent
106- * @param skipFlowExecutionIdReplacement
107- * @throws IOException
108107 */
109108 public void handleFlowLaunchTriggerEvent (Properties jobProps , DagActionStore .DagAction dagAction ,
110- long eventTimeMillis , boolean isReminderEvent , boolean skipFlowExecutionIdReplacement ) throws IOException {
111- LeaseAttemptStatus
112- leaseAttemptStatus = this .multiActiveLeaseArbiter
113- .tryAcquireLease (dagAction , eventTimeMillis , isReminderEvent , skipFlowExecutionIdReplacement );
114- if (leaseAttemptStatus instanceof LeaseAttemptStatus .LeaseObtainedStatus ) {
115- LeaseAttemptStatus .LeaseObtainedStatus leaseObtainedStatus =
116- (LeaseAttemptStatus .LeaseObtainedStatus ) leaseAttemptStatus ;
117- if (persistDagAction (leaseObtainedStatus )) {
118- log .info ("Successfully persisted lease: [{}, eventTimestamp: {}] " , leaseObtainedStatus .getDagAction (),
119- leaseObtainedStatus .getEventTimeMillis ());
120- return ;
121- }
122- // If persisting the dag action failed, then we set another trigger for this event to occur immediately to
123- // re-attempt handling the event
124- scheduleReminderForEvent (jobProps ,
125- new LeaseAttemptStatus .LeasedToAnotherStatus (leaseObtainedStatus .getDagAction (), 0L ), eventTimeMillis );
126- } else if (leaseAttemptStatus instanceof LeaseAttemptStatus .LeasedToAnotherStatus ) {
127- scheduleReminderForEvent (jobProps , (LeaseAttemptStatus .LeasedToAnotherStatus ) leaseAttemptStatus ,
128- eventTimeMillis );
129- }
130- // Otherwise leaseAttemptStatus instanceof MultiActiveLeaseArbiter.NoLongerLeasingStatus & no need to do anything
109+ long eventTimeMillis , boolean isReminderEvent , boolean adoptConsensusFlowExecutionId ) throws IOException {
110+ LeaseAttemptStatus leaseAttempt = this .multiActiveLeaseArbiter .tryAcquireLease (
111+ dagAction , eventTimeMillis , isReminderEvent , adoptConsensusFlowExecutionId );
112+ if (leaseAttempt instanceof LeaseAttemptStatus .LeaseObtainedStatus
113+ && persistDagAction ((LeaseAttemptStatus .LeaseObtainedStatus ) leaseAttempt )) {
114+ log .info ("Successfully persisted lease: [{}, eventTimestamp: {}] " , leaseAttempt .getConsensusDagAction (),
115+ ((LeaseAttemptStatus .LeaseObtainedStatus ) leaseAttempt ).getEventTimeMillis ());
116+ } else { // when NOT successfully `persistDagAction`, set a reminder to re-attempt handling (unless leasing finished)
117+ calcLeasedToAnotherStatusForReminder (leaseAttempt ).ifPresent (leasedToAnother ->
118+ scheduleReminderForEvent (jobProps , leasedToAnother , eventTimeMillis ));
119+ }
131120 }
132121
133- // Called after obtaining a lease to persist the dag action to {@link DagActionStore} and mark the lease as done
134- private boolean persistDagAction (LeaseAttemptStatus .LeaseObtainedStatus leaseStatus ) {
135- if (this .dagActionStore .isPresent ()) {
136- try {
137- DagActionStore .DagAction dagAction = leaseStatus .getDagAction ();
138- this .dagActionStore .get ().addFlowDagAction (dagAction .getFlowGroup (), dagAction .getFlowName (), dagAction .getFlowExecutionId (), dagAction .getDagActionType ());
139- // If the dag action has been persisted to the {@link DagActionStore} we can close the lease
140- this .numFlowsSubmitted .mark ();
141- return this .multiActiveLeaseArbiter .recordLeaseSuccess (leaseStatus );
142- } catch (IOException e ) {
143- throw new RuntimeException (e );
144- }
122+ /** @return {@link Optional} status for reminding, unless {@link LeaseAttemptStatus.NoLongerLeasingStatus} (hence nothing to do) */
123+ private Optional <LeaseAttemptStatus .LeasedToAnotherStatus > calcLeasedToAnotherStatusForReminder (LeaseAttemptStatus leaseAttempt ) {
124+ if (leaseAttempt instanceof LeaseAttemptStatus .NoLongerLeasingStatus ) { // all done: nothing to remind about
125+ return Optional .empty ();
126+ } else if (leaseAttempt instanceof LeaseAttemptStatus .LeasedToAnotherStatus ) { // already have one: just return it
127+ return Optional .of ((LeaseAttemptStatus .LeasedToAnotherStatus ) leaseAttempt );
128+ } else if (leaseAttempt instanceof LeaseAttemptStatus .LeaseObtainedStatus ) { // remind w/o delay to immediately re-attempt handling
129+ return Optional .of (new LeaseAttemptStatus .LeasedToAnotherStatus (leaseAttempt .getConsensusDagAction (), 0L ));
145130 } else {
146- throw new RuntimeException ("DagActionStore is " + (this .dagActionStore .isPresent () ? "" : "NOT" ) + " present." );
131+ throw new RuntimeException ("unexpected `LeaseAttemptStatus` derived type: '" + leaseAttempt .getClass ().getName () + "' in '" + leaseAttempt + "'" );
132+ }
133+ }
134+
135+ /**
136+ * Called after obtaining a lease to both persist to the {@link DagActionStore} and
137+ * {@link MultiActiveLeaseArbiter#recordLeaseSuccess(LeaseAttemptStatus.LeaseObtainedStatus)}
138+ */
139+ private boolean persistDagAction (LeaseAttemptStatus .LeaseObtainedStatus leaseStatus ) {
140+ try {
141+ this .dagActionStore .addDagAction (leaseStatus .getConsensusDagAction ());
142+ this .numFlowsSubmitted .mark ();
143+ // after successfully persisting, close the lease
144+ return this .multiActiveLeaseArbiter .recordLeaseSuccess (leaseStatus );
145+ } catch (IOException e ) {
146+ throw new RuntimeException (e );
147147 }
148148 }
149149
@@ -156,7 +156,7 @@ private boolean persistDagAction(LeaseAttemptStatus.LeaseObtainedStatus leaseSta
156156 */
157157 private void scheduleReminderForEvent (Properties jobProps , LeaseAttemptStatus .LeasedToAnotherStatus status ,
158158 long triggerEventTimeMillis ) {
159- DagActionStore .DagAction dagAction = status .getDagAction ();
159+ DagActionStore .DagAction dagAction = status .getConsensusDagAction ();
160160 JobKey origJobKey = new JobKey (jobProps .getProperty (ConfigurationKeys .JOB_NAME_KEY , "<<no job name>>" ),
161161 jobProps .getProperty (ConfigurationKeys .JOB_GROUP_KEY , "<<no job group>>" ));
162162 try {
@@ -196,7 +196,7 @@ protected Trigger createAndScheduleReminder(JobKey origJobKey, LeaseAttemptStatu
196196 Trigger reminderTrigger = JobScheduler .createTriggerForJob (reminderJobKey , getJobPropertiesFromJobDetail (jobDetail ),
197197 Optional .of (reminderSuffix ));
198198 log .debug ("Flow Launch Handler - [{}, eventTimestamp: {}] - attempting to schedule reminder for event {} with "
199- + "reminderJobKey {} and reminderTriggerKey {}" , status .getDagAction (), triggerEventTimeMillis ,
199+ + "reminderJobKey {} and reminderTriggerKey {}" , status .getConsensusDagAction (), triggerEventTimeMillis ,
200200 status .getEventTimeMillis (), reminderJobKey , reminderTrigger .getKey ());
201201 this .schedulerService .getScheduler ().scheduleJob (jobDetail , reminderTrigger );
202202 return reminderTrigger ;
@@ -258,7 +258,7 @@ public static JobDataMap updatePropsInJobDataMap(JobDataMap jobDataMap,
258258 // Saves the following properties in jobProps to retrieve when the trigger fires
259259 prevJobProps .setProperty (ConfigurationKeys .SCHEDULER_EXPECTED_REMINDER_TIME_MILLIS_KEY ,
260260 String .valueOf (getUTCTimeFromDelayPeriod (delayPeriodMillis )));
261- // Use the db laundered timestamp for the reminder to ensure consensus between hosts . Participant trigger timestamps
261+ // Use the db consensus timestamp for the reminder to ensure inter-host agreement . Participant trigger timestamps
262262 // can differ between participants and be interpreted as a reminder for a distinct flow trigger which will cause
263263 // excess flows to be triggered by the reminder functionality.
264264 prevJobProps .setProperty (ConfigurationKeys .SCHEDULER_PRESERVED_CONSENSUS_EVENT_TIME_MILLIS_KEY ,
0 commit comments