We have a webapi, which handles a lot of requests, sometimes per second and at the start of every request we send a log entry to our logtable using Entity framework.
However, often this entry to our logtable will deadlock on another process and I don't understand what is causing it.
using (UnitOfWork unitofwork = new UnitOfWork())
{
unitofwork.WebApiRequestLogRepository.Insert(new WebApiRequestLog
{
Created = DateTime.Now,
Username = System.Threading.Thread.CurrentPrincipal.Identity.Name,
Controller = actionContext.ActionDescriptor.ControllerDescriptor.ControllerName,
Method = actionContext.ActionDescriptor.ActionName,
MethodParameters = xml,
ApplicationVersion = AppSettings.Instance.Version,
Url = HttpContext.Current.Request.Url.ToString(),
});
unitofwork.Save();
}
The unitofwork is a wrapper around a single context where multiple changes can be done at the same time. The save calls a context.SaveChanges
SQL Code that is generated by entity framework
INSERT [log].[WebApiRequestLog]([Created], [Username], [Controller], [Method], [ApplicationVersion], [Url], [MethodParameters])
VALUES (@0, @1, @2, @3, @4, @5, @6)
SELECT [Id]
FROM [log].[WebApiRequestLog]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()
Can anyone explain to me why a single insert into a log table can cause a deadlock?
Edit
public virtual void Insert(TEntity entity)
{
DbSet.Add(entity);
}
public void Save()
{
StringBuilder builder = new StringBuilder();
try
{
_context.Database.Log = s => builder.AppendLine(s);
_context.SaveChanges();
}
catch (DbEntityValidationException e)
{
HandleDbEntityValidationException(e);
}
catch (Exception e)
{
Exception ex = new Exception(builder.ToString(), e);
throw ex;
}
}
I am throwing a few helpful queries that you can run during a deadlock to show current waits. They may help you identify situations for a deadlock.
Also, you may want to lookup how to save deadlock graphs to the database when they occur. If you posted a deadlock graph over at Database Administrator Exchange , I am sure you would find out more information about what is locking.
Make sure you are not breaking in a TransactionScope or that another developer is sitting on a breakpoint inside a TransactionScope
block of code.
If your database is super busy and you are not worried about dirty then you can reduce read locks with the hints below.
CREATE PROCEDURE SaveWebApiRequestLog(...) AS BEGIN SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED INSERT log.WebApiRequestLog(Created,Username,Controller,Method,ApplicationVersion,Url,MethodParameters) VALUES(...) SELECT Id FROM log.WebApiRequestLog WHERE Id = @@IDENTITY END
OR
INSERT [log].[WebApiRequestLog]([Created], [Username], [Controller], [Method], [ApplicationVersion], [Url], [MethodParameters]) VALUES (@0, @1, @2, @3, @4, @5, @6) SELECT [Id] FROM [log].[WebApiRequestLog] WITH NOLOCK WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()
Show Waits
--kill 65
SELECT
S.Text, R.Session_id,R.Status,R.Command,R.CPU_Time,R.Total_Elapsed_Time
FROM
sys.dm_exec_requests R
CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS S
--EXEC sp_who2
SELECT W.session_id,ot.task_state,W.wait_type, W.wait_duration_ms, W.blocking_session_id, W.resource_description, S.host_name, S.program_name
FROM
sys.dm_os_waiting_tasks W
INNER JOIN sys.dm_os_tasks ot ON ot.task_address = W.waiting_task_address
INNER JOIN sys.dm_exec_sessions S ON S.session_id = W.session_id
WHERE
S.is_user_process = 1
SELECT
SessionID = es.session_id,
Login =es.original_login_name,
Host = es.host_name,
RequestStart=es.last_request_start_time,
RequerstEnd=es.last_request_end_time,
Status =es.status,
BockedBy =er.blocking_session_id,
WaitType =er.wait_type,
WaitTime = er.wait_time,
LastWaitType = er.last_wait_type,
WaitResource = er.wait_resource,
DatabaseID = DB_NAME(er.database_id),
Command = er.command,
--,sql_text=st.text
TransactionIsolation =
CASE es.transaction_isolation_level
WHEN 0 THEN 'Unspecified'
WHEN 1 THEN 'Read Uncommitted'
WHEN 2 THEN 'Read Committed'
WHEN 3 THEN 'Repeatable'
WHEN 4 THEN 'Serializable'
WHEN 5 THEN 'Snapshot'
END,
CPUTime = COALESCE(es.cpu_time,0) + COALESCE(er.cpu_time,0),
IOTime = COALESCE(es.reads,0) + COALESCE(es.writes,0) + COALESCE(er.reads,0) + COALESCE(er.writes,0),
TransactioCount = COALESCE(er.open_transaction_count,-1),
ProgramName = COALESCE(es.program_name,''),
LoginTme = es.login_time
FROM
sys.dm_exec_sessions es
LEFT OUTER JOIN sys.dm_exec_connections ec ON es.session_id = ec.session_id
LEFT OUTER JOIN sys.dm_exec_requests er ON es.session_id = er.session_id
LEFT OUTER JOIN sys.server_principals sp ON es.security_id = sp.sid
LEFT OUTER JOIN sys.dm_os_tasks ota ON es.session_id = ota.session_id
LEFT OUTER JOIN sys.dm_os_threads oth ON ota.worker_address = oth.worker_address
CROSS APPLY sys.dm_exec_sql_text(er.sql_handle) AS st
WHERE
es.is_user_process = 1
and
es.session_id <> @@spid
ORDER BY
es.session_id
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.