|
8 | 8 | using System.Threading; |
9 | 9 | using System.Threading.Tasks; |
10 | 10 | using DurableTask.Core; |
| 11 | +using DurableTask.Core.Exceptions; |
11 | 12 | using Newtonsoft.Json; |
12 | 13 | using Newtonsoft.Json.Linq; |
13 | 14 |
|
@@ -185,70 +186,78 @@ public override async Task<string> Execute(OrchestrationContext innerContext, st |
185 | 186 |
|
186 | 187 | DurableTaskExtension.TagActivityWithOrchestrationStatus(status, this.context.InstanceId, true); |
187 | 188 | #endif |
188 | | - |
189 | | - if (this.operationBatch.Count == 0 |
190 | | - && this.lockRequest == null |
191 | | - && (this.toBeRescheduled == null || this.toBeRescheduled.Count == 0) |
192 | | - && !this.suspendAndContinueWithDelay) |
193 | | - { |
194 | | - // we are idle after a ContinueAsNew - the batch is empty. |
195 | | - // Wait for more messages to get here (via extended sessions) |
196 | | - await this.doneProcessingMessages.Task; |
197 | | - } |
198 | | - |
199 | | - if (!this.messageDataConverter.IsDefault) |
200 | | - { |
201 | | - innerContext.MessageDataConverter = this.messageDataConverter; |
202 | | - } |
203 | | - |
204 | | - if (!this.errorDataConverter.IsDefault) |
205 | | - { |
206 | | - innerContext.ErrorDataConverter = this.errorDataConverter; |
207 | | - } |
208 | | - |
209 | | - if (this.NumberEventsToReceive > 0) |
| 189 | + try |
210 | 190 | { |
211 | | - await this.doneProcessingMessages.Task; |
212 | | - } |
| 191 | + if (this.operationBatch.Count == 0 |
| 192 | + && this.lockRequest == null |
| 193 | + && (this.toBeRescheduled == null || this.toBeRescheduled.Count == 0) |
| 194 | + && !this.suspendAndContinueWithDelay) |
| 195 | + { |
| 196 | + // we are idle after a ContinueAsNew - the batch is empty. |
| 197 | + // Wait for more messages to get here (via extended sessions) |
| 198 | + await this.doneProcessingMessages.Task; |
| 199 | + } |
213 | 200 |
|
214 | | - // Commit the effects of this batch, if |
215 | | - // we have not already run into an internal error |
216 | | - // (in which case we will abort the batch instead of committing it) |
217 | | - if (this.context.InternalError == null) |
218 | | - { |
219 | | - bool writeBackSuccessful = true; |
220 | | - ResponseMessage serializationErrorMessage = null; |
| 201 | + if (!this.messageDataConverter.IsDefault) |
| 202 | + { |
| 203 | + innerContext.MessageDataConverter = this.messageDataConverter; |
| 204 | + } |
221 | 205 |
|
222 | | - if (this.RollbackFailedOperations) |
| 206 | + if (!this.errorDataConverter.IsDefault) |
223 | 207 | { |
224 | | - // the state has already been written back, since it is |
225 | | - // done right after each operation. |
| 208 | + innerContext.ErrorDataConverter = this.errorDataConverter; |
226 | 209 | } |
227 | | - else |
| 210 | + |
| 211 | + if (this.NumberEventsToReceive > 0) |
228 | 212 | { |
229 | | - // we are writing back the state here, after executing |
230 | | - // the entire batch of operations. |
231 | | - writeBackSuccessful = this.context.TryWriteback(out serializationErrorMessage, out var _); |
| 213 | + await this.doneProcessingMessages.Task; |
232 | 214 | } |
233 | 215 |
|
234 | | - // Reschedule all signals that were received before their time |
235 | | - this.context.RescheduleMessages(innerContext, this.toBeRescheduled); |
| 216 | + // Commit the effects of this batch, if |
| 217 | + // we have not already run into an internal error |
| 218 | + // (in which case we will abort the batch instead of committing it) |
| 219 | + if (this.context.InternalError == null) |
| 220 | + { |
| 221 | + bool writeBackSuccessful = true; |
| 222 | + ResponseMessage serializationErrorMessage = null; |
236 | 223 |
|
237 | | - // Send all buffered outgoing messages |
238 | | - this.context.SendOutbox(innerContext, writeBackSuccessful, serializationErrorMessage); |
| 224 | + if (this.RollbackFailedOperations) |
| 225 | + { |
| 226 | + // the state has already been written back, since it is |
| 227 | + // done right after each operation. |
| 228 | + } |
| 229 | + else |
| 230 | + { |
| 231 | + // we are writing back the state here, after executing |
| 232 | + // the entire batch of operations. |
| 233 | + writeBackSuccessful = this.context.TryWriteback(out serializationErrorMessage, out var _); |
| 234 | + } |
239 | 235 |
|
240 | | - // send a continue signal |
241 | | - if (this.suspendAndContinueWithDelay) |
242 | | - { |
243 | | - this.context.SendContinue(innerContext); |
244 | | - this.suspendAndContinueWithDelay = false; |
245 | | - this.context.State.Suspended = true; |
246 | | - } |
| 236 | + // Reschedule all signals that were received before their time |
| 237 | + this.context.RescheduleMessages(innerContext, this.toBeRescheduled); |
247 | 238 |
|
248 | | - var jstate = JToken.FromObject(this.context.State); |
| 239 | + // Send all buffered outgoing messages |
| 240 | + this.context.SendOutbox(innerContext, writeBackSuccessful, serializationErrorMessage); |
249 | 241 |
|
250 | | - // continue as new |
251 | | - innerContext.ContinueAsNew(jstate); |
| 242 | + // send a continue signal |
| 243 | + if (this.suspendAndContinueWithDelay) |
| 244 | + { |
| 245 | + this.context.SendContinue(innerContext); |
| 246 | + this.suspendAndContinueWithDelay = false; |
| 247 | + this.context.State.Suspended = true; |
| 248 | + } |
| 249 | + |
| 250 | + var jstate = JToken.FromObject(this.context.State); |
| 251 | + |
| 252 | + // continue as new |
| 253 | + innerContext.ContinueAsNew(jstate); |
| 254 | + } |
| 255 | + } |
| 256 | + catch (Exception e) |
| 257 | + { |
| 258 | + // we must catch unexpected exceptions here, otherwise entity goes into permanent failed state |
| 259 | + // for example, there can be an OOM thrown during serialization https://github.com/Azure/azure-functions-durable-extension/issues/2166 |
| 260 | + this.context.CaptureInternalError(e); |
252 | 261 | } |
253 | 262 |
|
254 | 263 | // The return value is not used. |
|
0 commit comments