efcore: Transaction commit failure: avoid retry by default?

With regards to failure on transaction commit, the docs state:

By default, the execution strategy will retry the operation as if the transaction was rolled back, but if it’s not the case this will result in an exception if the new database state is incompatible or could lead to data corruption if the operation does not rely on a particular state, for example when inserting a new row with auto-generated key values.

In contrast to other Entity Framework defaults, this seems like an unsafe default.

If a drastic operation results in a failure on commit, which would we prefer?

  1. Risk performing the operation twice, which also implies misinforming the client.
  2. Risk being unable to tell the client what the result was.

Let’s say that a client attempts to make a payment. Performing it twice could have bad consequences.

However, if we were to return an internal server error (since we did not catch the exception of this unexpected and highly unlikely failure), then we have technically informed the client that the result is unsure. Experienced developers know this, and can act accordingly. (Sure, some clients may assume that the attempt has failed, but that is a mistake. At least we have been as clear as possible.)

I’m aware that for optimal results we could manually implement verifySucceeded for each method, but that adds a lot of work and code overhead. More importantly, it’s no reason to keep us from choosing the best possible defaults!

Should we have retrying execution strategies not retry by default when a failure on commit occurs?

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Comments: 19 (14 by maintainers)

Most upvoted comments

On this point:

I’m aware that for optimal results we could manually implement verifySucceeded for each method, but that adds a lot of work and code overhead.

Would a simple way to do this currently be to implement a simple verifySucceeded that just returns false? And maybe a custom extension to wrap this up for less code overhead?

We should be careful about behavior we assume across databases (e.g. what errors can occur at commit time).

Not retrying should still be a safer default for any database.

Since we’re concentrating on networking transient errors, what would we do if the verifySucceeded function itself fails (because the network isn’t working)?

It’s retried according to the current execution strategy. If a fatal exception is thrown it will not be handled.

@roji It’s probably worth talking to @AndriySvyryd on this. This issue with SqlClient has been investigated extensively, and that’s where this guidance comes from.