1818package org .apache .dolphinscheduler .api .executor .workflow ;
1919
2020import org .apache .dolphinscheduler .api .exceptions .ServiceException ;
21+ import org .apache .dolphinscheduler .api .service .WorkflowLineageService ;
2122import org .apache .dolphinscheduler .api .validator .workflow .BackfillWorkflowDTO ;
2223import org .apache .dolphinscheduler .common .enums .ComplementDependentMode ;
2324import org .apache .dolphinscheduler .common .enums .ExecutionOrder ;
4950@ Component
5051public class BackfillWorkflowExecutorDelegate implements IExecutorDelegate <BackfillWorkflowDTO , List <Integer >> {
5152
53+ @ Autowired
54+ private WorkflowLineageService workflowLineageService ;
55+
5256 @ Autowired
5357 private RegistryClient registryClient ;
5458
5559 @ Override
5660 public List <Integer > execute (final BackfillWorkflowDTO backfillWorkflowDTO ) {
61+ return executeWithDependentExpansion (backfillWorkflowDTO );
62+ }
63+
64+ /**
65+ * Expands optional downstream workflows, then submits root and each downstream in list order.
66+ * <p>
67+ * {@link RunMode} (serial vs parallel date sharding) is taken only from the <strong>root</strong>
68+ * {@code backfillWorkflowDTO}'s {@link BackfillWorkflowDTO.BackfillParamsDTO#getRunMode()}; downstream DTOs
69+ * mirror the same mode in their params for consistency.
70+ */
71+ List <Integer > executeWithDependentExpansion (final BackfillWorkflowDTO backfillWorkflowDTO ) {
5772 // todo: directly call the master api to do backfill
73+ List <BackfillWorkflowDTO > dependentBackfillDtos = new ArrayList <>();
74+ dependentBackfillDtos .add (backfillWorkflowDTO );
75+ if (backfillWorkflowDTO .getBackfillParams ()
76+ .getBackfillDependentMode () == ComplementDependentMode .ALL_DEPENDENT ) {
77+
78+ List <WorkflowDefinition > downstreamWorkflowList =
79+ workflowLineageService .resolveDownstreamWorkflowDefinitionCodes (
80+ backfillWorkflowDTO .getWorkflowDefinition ().getCode (),
81+ backfillWorkflowDTO .getBackfillParams ().isAllLevelDependent (),
82+ true );
83+ if (downstreamWorkflowList .isEmpty ()) {
84+ log .info ("No downstream dependent workflows found for workflow code {}" ,
85+ backfillWorkflowDTO .getWorkflowDefinition ().getCode ());
86+ } else {
87+ dependentBackfillDtos .addAll (buildResolvedDownstreamBackfillDtos (backfillWorkflowDTO ,
88+ backfillWorkflowDTO .getBackfillParams ().getBackfillDateList (),
89+ downstreamWorkflowList ));
90+ }
91+ }
92+ List <Integer > workflowInstanceIdList = new ArrayList <>();
93+ // RunMode is defined by the root request only (not per downstream DTO).
5894 if (backfillWorkflowDTO .getBackfillParams ().getRunMode () == RunMode .RUN_MODE_SERIAL ) {
59- return doSerialBackfillWorkflow (backfillWorkflowDTO );
95+ for (BackfillWorkflowDTO dependentDto : dependentBackfillDtos ) {
96+ workflowInstanceIdList .addAll (doSerialBackfillWorkflow (dependentDto ));
97+ }
6098 } else {
61- return doParallelBackfillWorkflow (backfillWorkflowDTO );
99+ for (BackfillWorkflowDTO dependentDto : dependentBackfillDtos ) {
100+ workflowInstanceIdList .addAll (doParallelBackfillWorkflow (dependentDto ));
101+ }
62102 }
103+ return workflowInstanceIdList ;
63104 }
64105
65106 private List <Integer > doSerialBackfillWorkflow (final BackfillWorkflowDTO backfillWorkflowDTO ) {
@@ -71,9 +112,7 @@ private List<Integer> doSerialBackfillWorkflow(final BackfillWorkflowDTO backfil
71112 Collections .sort (backfillTimeList );
72113 }
73114
74- final Integer workflowInstanceId = doBackfillWorkflow (
75- backfillWorkflowDTO ,
76- backfillTimeList .stream ().map (DateUtils ::dateToString ).collect (Collectors .toList ()));
115+ final Integer workflowInstanceId = doBackfillWorkflow (backfillWorkflowDTO , backfillTimeList );
77116 return Lists .newArrayList (workflowInstanceId );
78117 }
79118
@@ -92,8 +131,7 @@ private List<Integer> doParallelBackfillWorkflow(final BackfillWorkflowDTO backf
92131 final List <Integer > workflowInstanceIdList = Lists .newArrayList ();
93132 for (List <ZonedDateTime > stringDate : splitDateTime (listDate , expectedParallelismNumber )) {
94133 final Integer workflowInstanceId = doBackfillWorkflow (
95- backfillWorkflowDTO ,
96- stringDate .stream ().map (DateUtils ::dateToString ).collect (Collectors .toList ()));
134+ backfillWorkflowDTO , stringDate );
97135 workflowInstanceIdList .add (workflowInstanceId );
98136 }
99137 return workflowInstanceIdList ;
@@ -124,12 +162,15 @@ private List<List<ZonedDateTime>> splitDateTime(List<ZonedDateTime> dateTimeList
124162 }
125163
126164 private Integer doBackfillWorkflow (final BackfillWorkflowDTO backfillWorkflowDTO ,
127- final List <String > backfillTimeList ) {
165+ final List <ZonedDateTime > backfillDateTimes ) {
128166 final Server masterServer = registryClient .getRandomServer (RegistryNodeType .MASTER ).orElse (null );
129167 if (masterServer == null ) {
130168 throw new ServiceException ("no master server available" );
131169 }
132170
171+ final List <String > backfillTimeList =
172+ backfillDateTimes .stream ().map (DateUtils ::dateToString ).collect (Collectors .toList ());
173+
133174 final WorkflowDefinition workflowDefinition = backfillWorkflowDTO .getWorkflowDefinition ();
134175 final WorkflowBackfillTriggerRequest backfillTriggerRequest = WorkflowBackfillTriggerRequest .builder ()
135176 .userId (backfillWorkflowDTO .getLoginUser ().getId ())
@@ -149,22 +190,76 @@ private Integer doBackfillWorkflow(final BackfillWorkflowDTO backfillWorkflowDTO
149190 .dryRun (backfillWorkflowDTO .getDryRun ())
150191 .build ();
151192
152- final WorkflowBackfillTriggerResponse backfillTriggerResponse = Clients
153- .withService (IWorkflowControlClient .class )
154- .withHost (masterServer .getHost () + ":" + masterServer .getPort ())
155- .backfillTriggerWorkflow (backfillTriggerRequest );
193+ final WorkflowBackfillTriggerResponse backfillTriggerResponse =
194+ triggerBackfillWorkflow (backfillTriggerRequest , masterServer );
156195 if (!backfillTriggerResponse .isSuccess ()) {
157196 throw new ServiceException ("Backfill workflow failed: " + backfillTriggerResponse .getMessage ());
158197 }
159- final BackfillWorkflowDTO .BackfillParamsDTO backfillParams = backfillWorkflowDTO .getBackfillParams ();
160- if (backfillParams .getBackfillDependentMode () == ComplementDependentMode .ALL_DEPENDENT ) {
161- doBackfillDependentWorkflow (backfillWorkflowDTO , backfillTimeList );
162- }
163198 return backfillTriggerResponse .getWorkflowInstanceId ();
164199 }
165200
166- private void doBackfillDependentWorkflow (final BackfillWorkflowDTO backfillWorkflowDTO ,
167- final List <String > backfillTimeList ) {
168- // todo:
201+ protected WorkflowBackfillTriggerResponse triggerBackfillWorkflow (final WorkflowBackfillTriggerRequest request ,
202+ final Server masterServer ) {
203+ return Clients
204+ .withService (IWorkflowControlClient .class )
205+ .withHost (masterServer .getHost () + ":" + masterServer .getPort ())
206+ .backfillTriggerWorkflow (request );
207+ }
208+
209+ /**
210+ * Builds {@link BackfillWorkflowDTO} list for resolved downstream workflows.
211+ * {@link RunMode} in each downstream {@link BackfillWorkflowDTO.BackfillParamsDTO} matches the root (see
212+ * {@link #executeWithDependentExpansion(BackfillWorkflowDTO)}).
213+ */
214+ private List <BackfillWorkflowDTO > buildResolvedDownstreamBackfillDtos (final BackfillWorkflowDTO backfillWorkflowDTO ,
215+ final List <ZonedDateTime > backfillDateTimes ,
216+ final List <WorkflowDefinition > downstreamWorkflows ) {
217+ final long upstreamWorkflowCode = backfillWorkflowDTO .getWorkflowDefinition ().getCode ();
218+ final List <ZonedDateTime > upstreamBackfillDates = new ArrayList <>(backfillDateTimes );
219+ final BackfillWorkflowDTO .BackfillParamsDTO originalParams = backfillWorkflowDTO .getBackfillParams ();
220+ final boolean allLevelDependent = originalParams .isAllLevelDependent ();
221+
222+ final List <BackfillWorkflowDTO > result = new ArrayList <>();
223+ for (WorkflowDefinition downstreamWorkflow : downstreamWorkflows ) {
224+ final long downstreamCode = downstreamWorkflow .getCode ();
225+
226+ final BackfillWorkflowDTO .BackfillParamsDTO dependentParams =
227+ BackfillWorkflowDTO .BackfillParamsDTO .builder ()
228+ // Same as root; executor also branches on root RunMode only.
229+ .runMode (originalParams .getRunMode ())
230+ .backfillDateList (upstreamBackfillDates )
231+ .expectedParallelismNumber (originalParams .getExpectedParallelismNumber ())
232+ // Downstream expansion has already been decided in resolution stage.
233+ .backfillDependentMode (ComplementDependentMode .OFF_MODE )
234+ .allLevelDependent (allLevelDependent )
235+ .executionOrder (originalParams .getExecutionOrder ())
236+ .build ();
237+
238+ final BackfillWorkflowDTO dependentBackfillDTO = BackfillWorkflowDTO .builder ()
239+ .loginUser (backfillWorkflowDTO .getLoginUser ())
240+ .workflowDefinition (downstreamWorkflow )
241+ .startNodes (null )
242+ .failureStrategy (backfillWorkflowDTO .getFailureStrategy ())
243+ .taskDependType (backfillWorkflowDTO .getTaskDependType ())
244+ .execType (backfillWorkflowDTO .getExecType ())
245+ .warningType (backfillWorkflowDTO .getWarningType ())
246+ .warningGroupId (downstreamWorkflow .getWarningGroupId ())
247+ .runMode (dependentParams .getRunMode ())
248+ .workflowInstancePriority (backfillWorkflowDTO .getWorkflowInstancePriority ())
249+ .workerGroup (backfillWorkflowDTO .getWorkerGroup ())
250+ .tenantCode (backfillWorkflowDTO .getTenantCode ())
251+ .environmentCode (backfillWorkflowDTO .getEnvironmentCode ())
252+ .startParamList (backfillWorkflowDTO .getStartParamList ())
253+ .dryRun (backfillWorkflowDTO .getDryRun ())
254+ .backfillParams (dependentParams )
255+ .build ();
256+
257+ log .info ("Built dependent backfill DTO for workflow {} (upstream {}) with backfill dates {}" ,
258+ downstreamCode , upstreamWorkflowCode ,
259+ backfillDateTimes .stream ().map (DateUtils ::dateToString ).collect (Collectors .toList ()));
260+
261+ result .add (dependentBackfillDTO );
262+ }
263+ return result ;
169264 }
170265}
0 commit comments