The part I am having trouble with is that the dispatch exception does not happen until after the HttpResponse has started. The client receives a successful status code even though the transaction was rolled back.
I have attached a POC with a simple readme to recreate this problem. POC.zip (67.4 KB)
It seems that middleware may not be the right place to open & commit a transactional session.
While writing the sample we have never thought about the status code problem I must say. It seems that this is quite tricky because when the middleware executed the next then the response is already written and it is no longer possible to modify the headers.
but unfortunately, there is no way to actually add a specific status code other than getting the InternalServerError
The obvious workaround for now would be moving the session handling into the controller action. Then you can use exception filters to globally handle the status code for session commit failures but you would still need explicit session handling code within the controller which is the thing we wanted to avoid.
I’ll think a bit more about this and keep you posted. Once we have a better approach, we will also update the sample.
We considered moving the session code to the controllers. We are adding messaging to a large existing RESTful API and wanted to treat that option as a last resort. I like your solution, it is much simpler. It would be great to provide a better status code but I’ll take the 500 to get our dev teams moving again. I’ll keep digging to see if I can build off of the solution you provided.
Thanks, I appreciate the help. I’ll keep an eye out for any updates and follow up here if I find another approach.
I do apologize if the answer to my question is obvious, but I was a bit confused by the documentation. Mike’s sample and Daniel’s answer help, but I am still not completely clear on the outcomes of this pattern.
The documentation says, “ exactly one of the following outcomes occur:
Transaction finishes with data being stored, and outgoing messages eventually sent - when the Commit path successfully stores the OutboxRecord
Transaction finishes with no visible side effects - when the control message stores the OutboxRecord”
Then, it says “ If dispatching the control message fails, the transactional session changes will roll back, and an error will be raised to the user committing the session.”
Are there 2 or 3 potential outcomes? Is the failure to dispatch the control message the same as the second outcome? Perhaps it is just the wording.
More importantly, how are the results detected/differentiated in these 2 or 3 different outcomes?
Failure to send the control messages means it can’t commit (thus rollback) the database transaction as these are sequential.
It could happen that the control messages succeed but the storage transaction fails because some database error. In that case no issue as the outbox records pointed by the control messages will not exist and can be ignored after a timeout period.
Step 1: Send a control message directly to the transport
Step 2: Commit database transaction with a business data update and prepared outgoing messages
Step 3: Process the control message of step 1 and dispatch the prepared outgoing messages to the transport
Failure to send the control message means the database will never be committed. State wise that means nothing got modified and no messages are dispatched to the transport.
The failure scenarios section you are highlighting is explaining what happens when a failure would occur during database commit or when the control message was dispatched to the transport. The outcome for either is the same, no committed data and an exception.
Does that help? Would you have a suggestion on how to improve that section?