Detecting a retry failure within a handler

We have a scenario where due to a design flaw we receive messages that have a key that is unique to their respective databases, but not to another where they are recorded. As, we are recording these in a single database we occasionally get a conflict, preventing the handler from succeeding. One of the source databases is for Live data the other for Test, we’d like to favor the Live over the Test. That is if we get a conflict and the current message has Live data then we’d like to remove the Test record then try again. However, given the transaction is not within our control, we can’t do a simple try…catch. We could solve the issue with a Get but given that conflicts are quite rare this adds too much overhead. So our question is how can we detect the original exception on the first retry?

You can use a try..catch, in this case you would want to catch a conflict exception and then do an alternative action like sending a message that will do a compensating action part of that same transaction.

Is there something specific why that wouldn’t work?

You could also send a message in the try…catch using immediate dispatch and still throw.

What transport and persistence are you using? What transaction are you referring to?

Sorry I guess I wasn’t very clear. I’m referring to using the outbox pattern and the transaction session on the handler context. Ie Accessing and modifying data from a message handler • NServiceBus • Particular Docs

So in a hander I might have something like the following:

handler(m, context){
var session = context.SynchronizedStorageSession.CosmosPersistenceSession();
// update database using context

Assuming outbox support is enabled, in this example if the database update fails due to an error then the code won’t know about this within the handler as the transaction isn’t committed until after the handler completes, so can’t use try…catch. However, during the retry process I’d like to know what caused the initial failure so I can avoid it on subsequent retries (based on logic).

Wanted to follow-up to see if there was any solution to this, or if I’m just thinking about this the wrong way.

There isn’t an easy way to achieve what you want. I’m not even sure there is a reliable way of doing it.

In the scenario you describe or any message retry attempt, the message context is new, and there is no way to access the “previous” processing context.

For my curiosity, what could be logic to apply to prevent a transaction commit failure in a subsequent attempt?

Thanks, I thought the answer might be as you point out. For context, this is the scenario we have, though maybe I wasn’t too clear the first time around.

We receive a message, its an audit/journal record. Ie we want to record the current state of a record. The process goes like this:

Web API -->Update Database–>Send CreateJournalEntryCommand(data) (its in a separate collection so couldn’t use same transaction)

Handler (CreateJournalEntryCommand) → update Journal Db with data -->commit trans

Now the issue is the Web API is working against two collections (Live/Test). The Journal however uses only a single collection. So we end up with conflicts. The Id is a 6 char key not a full Guid (readability I guess), we have dedup loigic in the WebAPI to account for that, but obviously not across collections.

End result is we get a conflict every so often. In these scenarios I want to delete all the test data associated with this request, then record the new Live data. I’ll admit this is a rather edge case, though I could see other scenarios where you want to do something in response to knowing what exception was thrown, though again edge cases. We obviously have a design flaw, that needs fixing but needed a short-term solution, as the fix is more complex than it first seems.

1 Like

can anyone please simplify the solution

It is not clear to me exactly what you want but it might be that you do not want to have that IO operation participate in the same context as the outbox but in an independent nested operation and to make the handler idempotent.

It is not clear what you are asking Trudi. What would you want us to simplify?