33using System . IO ;
44using System . Linq ;
55using System . Threading ;
6+ using System . Threading . Tasks ;
67using ConductorSharp . Client ;
78using ConductorSharp . Client . Generated ;
89using ConductorSharp . Client . Service ;
@@ -30,6 +31,7 @@ internal class ExecutionManager
3031 private readonly IServiceScopeFactory _lifetimeScopeFactory ;
3132 private readonly IPollTimingStrategy _pollTimingStrategy ;
3233 private readonly IPollOrderStrategy _pollOrderStrategy ;
34+ private readonly ICancellationNotifier _cancellationNotifier ;
3335
3436 public ExecutionManager (
3537 WorkerSetConfig options ,
@@ -39,7 +41,8 @@ public ExecutionManager(
3941 IExternalPayloadService externalPayloadService ,
4042 IServiceScopeFactory lifetimeScope ,
4143 IPollTimingStrategy pollTimingStrategy ,
42- IPollOrderStrategy pollOrderStrategy
44+ IPollOrderStrategy pollOrderStrategy ,
45+ ICancellationNotifier cancellationNotifier
4346 )
4447 {
4548 _configuration = options ;
@@ -50,6 +53,7 @@ IPollOrderStrategy pollOrderStrategy
5053 _lifetimeScopeFactory = lifetimeScope ;
5154 _pollTimingStrategy = pollTimingStrategy ;
5255 _pollOrderStrategy = pollOrderStrategy ;
56+ _cancellationNotifier = cancellationNotifier ;
5357 _externalPayloadService = externalPayloadService ;
5458 }
5559
@@ -110,18 +114,58 @@ private static Type GetInputType(Type workerType)
110114
111115 private async Task PollAndHandle ( TaskToWorker scheduledWorker , CancellationToken cancellationToken )
112116 {
113- Client . Generated . Task pollResponse = null ;
117+ Client . Generated . Task pollResponse ;
118+
119+ // TODO: Maybe this should be configurable
120+ var workerId = Guid . NewGuid ( ) . ToString ( ) ;
114121 try
115122 {
116- var workerId = Guid . NewGuid ( ) . ToString ( ) ;
117-
118123 pollResponse = await _taskManager . PollAsync (
119124 scheduledWorker . TaskName ,
120125 workerId ,
121126 scheduledWorker . TaskDomain ?? _configuration . Domain ,
122127 cancellationToken
123128 ) ;
129+ }
130+ catch ( ApiException exception ) when ( exception . StatusCode == 204 )
131+ {
132+ // This handles the case when PollAsync throws exception in case there are no tasks in queue
133+ // Even though Conductor reports 1 task in queue for particular task type this endpoint won't return scheduled task immmediately
134+ // We skip the further handling as task will be handled in next call to this method
135+ return ;
136+ }
137+ catch ( Exception exception )
138+ {
139+ _logger . LogError ( exception , "Exception during the task polling" ) ;
140+ return ;
141+ }
142+
143+ try
144+ {
145+ using var tokenHolder = _cancellationNotifier . GetCancellationToken ( pollResponse . TaskId , cancellationToken ) ;
146+ await ProcessPolledTask ( pollResponse , workerId , scheduledWorker , tokenHolder . CancellationToken ) ;
147+ }
148+ catch ( TaskCanceledException )
149+ {
150+ _logger . LogWarning (
151+ "Polled task {Task}(id={TaskId}) of workflow {Workflow}(id={WorkflowId}) is cancelled" ,
152+ pollResponse . TaskDefName ,
153+ pollResponse . TaskId ,
154+ pollResponse . WorkflowType ,
155+ pollResponse . WorkflowInstanceId
156+ ) ;
157+ }
158+ }
124159
160+ private async Task ProcessPolledTask (
161+ Client . Generated . Task pollResponse ,
162+ string workerId ,
163+ TaskToWorker scheduledWorker ,
164+ CancellationToken cancellationToken
165+ )
166+ {
167+ try
168+ {
125169 if ( ! string . IsNullOrEmpty ( pollResponse . ExternalInputPayloadStoragePath ) )
126170 {
127171 _logger . LogDebug ( "Fetching storage {location}" , pollResponse . ExternalInputPayloadStoragePath ) ;
@@ -180,20 +224,19 @@ await _taskManager.UpdateAsync(
180224 cancellationToken
181225 ) ;
182226 }
183- catch ( ApiException exception ) when ( exception . StatusCode == 204 )
227+ catch ( TaskCanceledException )
184228 {
185- // This handles the case when PollAsync throws exception in case there are no tasks in queue
186- // Even though Conductor reports 1 task in queue for particular task type this endpoint won't return scheduled task immmediately
187- // This causes NullReferenceException in logging code below hence why we ignore the exception
229+ // Propagate this exception to outer handler
230+ throw ;
188231 }
189232 catch ( Exception exception )
190233 {
191234 _logger . LogError (
192235 "{@Exception} while executing {Task} as part of {Workflow} with id {WorkflowId}" ,
193236 exception ,
194- pollResponse ? . TaskDefName ,
195- pollResponse ? . WorkflowType ,
196- pollResponse ? . WorkflowInstanceId
237+ pollResponse . TaskDefName ,
238+ pollResponse . WorkflowType ,
239+ pollResponse . WorkflowInstanceId
197240 ) ;
198241
199242 var errorMessage = new ErrorOutput { ErrorMessage = exception . Message } ;
@@ -207,16 +250,16 @@ await Task.WhenAll(
207250 _taskManager . UpdateAsync (
208251 new TaskResult
209252 {
210- TaskId = pollResponse ? . TaskId ,
253+ TaskId = pollResponse . TaskId ,
211254 Status = TaskResultStatus . FAILED ,
212255 ReasonForIncompletion = exception . Message ,
213256 OutputData = SerializationHelper . ObjectToDictionary ( errorMessage , ConductorConstants . IoJsonSerializerSettings ) ,
214257 WorkflowInstanceId = pollResponse ? . WorkflowInstanceId
215258 } ,
216259 cancellationToken
217260 ) ,
218- _taskManager . LogAsync ( pollResponse ? . TaskId , exception . Message , cancellationToken ) ,
219- _taskManager . LogAsync ( pollResponse ? . TaskId , exception . StackTrace , cancellationToken )
261+ _taskManager . LogAsync ( pollResponse . TaskId , exception . Message , cancellationToken ) ,
262+ _taskManager . LogAsync ( pollResponse . TaskId , exception . StackTrace , cancellationToken )
220263 ]
221264 ) ;
222265 }
0 commit comments