简体   繁体   中英

Single entry to datatable triggers deadlock

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.

  1. Make sure you are not breaking in a TransactionScope or that another developer is sitting on a breakpoint inside a TransactionScope block of code.

  2. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM