I have UNIQUE constraint on a database table which is occasionally throwing an exception when writing a new record. The constraint covers three fields - one of which is a DATETIMEOFFSET(7)
. The value for that field is being set to the current time when hitting a webapi
endpoint.
I can't think of any reason there would be an exact duplicate time being inserted by multiple webapi requests - it seems highly unlikely given the accuracy of the time field.
Unfortunately, I can't reproduce the exception, we're just occasionally seeing it in our logs.
The only thing I can think of is if within the same request, Entity Framework is somehow retrying the database write after thinking it's failed, when it hasn't - causing the exception. We're using SqlAzureExecutionStrategy
which after decompiling its ShouldRetryOn
method, it looks like it retries on the following error codes ...
40613: Database not currently available 41301: A transaction dependency failure occurred, and the current transaction can no longer commit. 41302: The current transaction attempted to update a record that has been updated since this transaction started. The transaction was aborted. 41305: The current transaction failed to commit due to a repeatable read validation failure. 41325: The current transaction failed to commit due to a serializable validation failure. 10928: Resource ID : %d. The %ls limit for the database is %d and has been reached. See 'http://go.microsoft.com/fwlink/?LinkId=267637' for assistance. 10929: Resource ID : %d. The %ls minimum guarantee is %d, maximum limit is %d and the current usage for the database is %d. However, the server is currently too busy to support %ls greater than %d for this database. See 'http://go.microsoft.com/fwlink/?LinkId=26 40197: The service has encountered an error processing your request. Please try again. Error code %d. 40501: The service is currently busy. Retry the request after 10 seconds. Incident ID: %ls. Code: %d 233: The column '%.*ls' in table '%.*ls' cannot be null. 10053: Could not convert the data value due to reasons other than sign mismatch or overflow. 10054: The data value for one or more columns overflowed the type used by the provider. 10060: Connection error 20: ? 64: A connection was successfully established with the server, but then an error occurred during the pre-login handshake
I would have thought that it would only retry if it fails to write though. Is it possible that it does write the record, but Entity Framework might get confused and try again anyway?
UPDATE
Below is the exception ...
System.Data.Entity.Infrastructure.RetryLimitExceededException: Maximum number of retries (5) exceeded while executing database operations with 'MyExecutionStrategy'.
See inner exception for the most recent failure. ---> System.Data.Entity.Core.UpdateException: An error occurred while updating the entries.
See the inner exception for details. ---> System.Data.SqlClient.SqlException: Violation of UNIQUE KEY constraint 'UQ_SessionLog'. Cannot insert duplicate key in object 'dbo.SessionLog'. The duplicate key value is (f6aeb1b8-c747-4f0b-9053-bf95a5630662, 2016-07-12 11:42:32.7077612 01:00, 0).
The statement has been terminated.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean%26 dataReady)
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task%26 task, Boolean asyncWrite, SqlDataReader ds)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task%26 task, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at StackExchange.Profiling.Data.ProfiledDbCommand.ExecuteNonQuery() in d:\Files\GitHub\miniprofiler\dotnet\StackExchange.Profiling\Data\ProfiledDbCommand.cs:line 264
at System.Data.Common.DbCommand.ExecuteNonQueryAsync(CancellationToken cancellationToken)