question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

DbUpdateConcurrencyException on token update

See original GitHub issue

Confirm you’ve already contributed to this project or that you sponsor it

  • I confirm I’m a sponsor or a contributor

Version

4.x

Question

I’m using OpenIddict 4.1 in an ASP.NET Core 7.0 Web Api. The publicy available frontend is written in Angular and a simple password/refresh-token process is used. ASP.NET Identity is used to store user information, and an MS SQL Server database with Entity Framework to store OpenIddict data.

Normally, OpenIddict works fine with the password/refresh-token flow, but every now and then I can see a DbUpdateConcurrencyException in the log when OpenIddictEntityFrameworkCoreTokenStore.UpdateAsync() is called by OpenIddict. I traced the problem down: the exception occurs when a user opens a browser with multiple previously opened tabs of my web application. Then multiple parallel requests could be sent to the /connect/token endpoint to get a new refresh token. This could happen in exact the same millisecond (I’ve seen this when using Firefox for myself). And then the following exception gets logged (logger: Microsoft.EntityFrameworkCore.Update):

Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
   at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ThrowAggregateUpdateConcurrencyExceptionAsync(RelationalDataReader reader, Int32 commandIndex, Int32 expectedRowsAffected, Int32 rowsAffected, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeResultSetWithRowsAffectedOnlyAsync(Int32 commandIndex, RelationalDataReader reader, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeAsync(RelationalDataReader reader, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.SqlServer.Update.Internal.SqlServerModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at OpenIddict.EntityFrameworkCore.OpenIddictEntityFrameworkCoreTokenStore`5.UpdateAsync(TToken token, CancellationToken cancellationToken)

That’s all information I get in the log, there is no further stacktrace from where OpenIddictEntityFrameworkCoreTokenStore.UpdateAsync() has been called. I tried to cover the code in the /connect/token endpoint with a user-based lock, but this has not helped.

I noticed the problems began when I switched from OpenIddict 3 to OpenIddict 4 and from .NET 6 to .NET 7 (the problem didn’t occur before). Any help is highly appreciated 👍

Issue Analytics

  • State:closed
  • Created 7 months ago
  • Comments:12 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
matthias-jauernigcommented, Mar 2, 2023

Looking at the EF sourcecode and internal EF logging, this seems to be the cause of the problem. So, OpenIddict is handling the exception as expected from the sourcecode. I’ll configure EF to ignore the DbUpdateConcurrencyException in logging. Thanks for your help.

1reaction
marcusbercommented, Mar 2, 2023

A guess is that it is coming from the logger from inside the DbContext. It is handled in the code, but the log still appears since it is logged prior to that from Ef.

Read more comments on GitHub >

github_iconTop Results From Across the Web

EF Core DbUpdateConcurrencyException does not work ...
Problem is that DbUpdateConcurrencyException is not fired whenever two threads are trying to update it at the same time and thus I don't...
Read more >
Handling Concurrency Conflicts - EF Core
The concurrency token is loaded and tracked when an entity is queried - just like any other property. Then, when an update or...
Read more >
DbUpdateConcurrencyException Class (System.Data. ...
This usually indicates that the database has been concurrently updated such that a concurrency token that was expected to match did not actually...
Read more >
Timestamp-based Concurrency Token is not updated ...
The reason for DbUpdateConcurrencyException can be seen in the following screenshot: In the first try of SaveChanges() there is an UPDATE ...
Read more >
Allow for deferred concurrency check by using token's ...
By deferred, I mean I would like to retrieve an entity, and then set the value for its concurrency token as provided by...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found