mediumLocking

LCK_M_BU_ABORT_BLOCKERS Wait Type Explained

LCK_M_BU_ABORT_BLOCKERS SQL Server wait type occurs during ALTER operations with low priority wait settings. Learn root causes, diagnostic queries, and fixes.

Quick Answer

LCK_M_BU_ABORT_BLOCKERS occurs when a task waits to acquire a Bulk Update lock with the ABORT_AFTER_WAIT=BLOCKERS option, introduced in SQL Server 2014. This wait appears during ALTER TABLE or ALTER INDEX operations configured to kill blocking sessions after the wait timeout expires. It's typically not concerning unless wait times exceed the configured timeout threshold.

Root Cause Analysis

This wait type emerges from SQL Server's implementation of the low priority wait mechanism for DDL operations. When ALTER TABLE or ALTER INDEX includes WAIT_AT_LOW_PRIORITY with ABORT_AFTER_WAIT=BLOCKERS, the operation requests a Bulk Update (BU) lock but defers acquisition until blocking sessions complete or the timeout expires.

The lock manager places the request in a special queue with reduced priority. During this phase, the session exhibits LCK_M_BU_ABORT_BLOCKERS waits while the scheduler periodically checks if blocking conditions have cleared. The BU lock provides shared access for readers while preventing concurrent schema modifications, making it essential for online index operations and table alterations.

SQL Server 2014 introduced this mechanism specifically to address blocking chains during maintenance windows. Unlike traditional lock waits that queue indefinitely, this implementation includes timeout logic within the lock manager itself. When the timeout expires, the lock manager identifies blocking sessions through sys.dm_tran_locks and terminates them via the KILL mechanism.

The wait accumulates in sys.dm_os_wait_stats during the entire timeout period, not just during active blocking scenarios. This behavior differs from standard lock waits that only register time spent actually waiting for resources.

Version-specific behavior remains consistent from SQL Server 2014 through 2022, though lock manager efficiency improvements in SQL Server 2019 reduced overhead for tracking these specialized wait states.

AutoDBA checks Blocking session detection, DDL operation monitoring, and low priority wait configurations across your entire SQL Server instance in 60 seconds. Download the free diagnostic script and see what else needs attention.

Diagnostic Queries

-- Check current sessions with LCK_M_BU_ABORT_BLOCKERS waits
SELECT 
    s.session_id,
    s.wait_type,
    s.wait_time,
    s.blocking_session_id,
    t.text AS current_statement,
    s.cpu_time,
    s.logical_reads
FROM sys.dm_exec_requests s
    CROSS APPLY sys.dm_exec_sql_text(s.sql_handle) t
WHERE s.wait_type = 'LCK_M_BU_ABORT_BLOCKERS'
ORDER BY s.wait_time DESC;
-- Identify blocking chains affecting BU lock requests
WITH blocking_tree AS (
    SELECT 
        session_id,
        blocking_session_id,
        wait_type,
        wait_resource,
        wait_time,
        0 AS level
    FROM sys.dm_exec_requests 
    WHERE blocking_session_id = 0 
        AND session_id IN (
            SELECT DISTINCT blocking_session_id 
            FROM sys.dm_exec_requests 
            WHERE blocking_session_id != 0
        )
    
    UNION ALL
    
    SELECT 
        r.session_id,
        r.blocking_session_id,
        r.wait_type,
        r.wait_resource,
        r.wait_time,
        b.level + 1
    FROM sys.dm_exec_requests r
        INNER JOIN blocking_tree b ON r.blocking_session_id = b.session_id
    WHERE r.blocking_session_id != 0
)
SELECT * FROM blocking_tree
WHERE EXISTS (
    SELECT 1 FROM sys.dm_exec_requests 
    WHERE wait_type = 'LCK_M_BU_ABORT_BLOCKERS'
        AND blocking_session_id IN (SELECT session_id FROM blocking_tree)
);
-- Check ALTER operations with low priority wait settings
SELECT 
    r.session_id,
    r.command,
    r.percent_complete,
    r.estimated_completion_time,
    t.text,
    r.wait_type,
    r.wait_time
FROM sys.dm_exec_requests r
    CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
WHERE r.command LIKE 'ALTER%'
    OR t.text LIKE '%WAIT_AT_LOW_PRIORITY%'
ORDER BY r.start_time;
-- Analyze historical wait patterns for this wait type
SELECT 
    wait_type,
    waiting_tasks_count,
    wait_time_ms,
    max_wait_time_ms,
    signal_wait_time_ms,
    wait_time_ms / NULLIF(waiting_tasks_count, 0) AS avg_wait_ms
FROM sys.dm_os_wait_stats 
WHERE wait_type = 'LCK_M_BU_ABORT_BLOCKERS'
    AND waiting_tasks_count > 0;
-- Find objects involved in BU lock conflicts
SELECT 
    l.resource_database_id,
    DB_NAME(l.resource_database_id) AS database_name,
    l.resource_associated_entity_id,
    OBJECT_NAME(l.resource_associated_entity_id, l.resource_database_id) AS object_name,
    l.resource_type,
    l.resource_description,
    l.request_mode,
    l.request_type,
    l.request_session_id
FROM sys.dm_tran_locks l
WHERE l.request_mode = 'BU'
    OR l.request_session_id IN (
        SELECT session_id 
        FROM sys.dm_exec_requests 
        WHERE wait_type = 'LCK_M_BU_ABORT_BLOCKERS'
    );

Fix Scripts

Increase timeout duration for ALTER operations

-- Modify existing ALTER statement to use longer timeout
-- Replace existing WAIT_AT_LOW_PRIORITY clause
-- TEST IN DEVELOPMENT FIRST - this affects blocking behavior

ALTER INDEX IX_YourIndex ON YourTable REBUILD 
WITH (ONLINE = ON, 
      WAIT_AT_LOW_PRIORITY (MAX_DURATION = 10 MINUTES, 
                           ABORT_AFTER_WAIT = BLOCKERS));
-- Increases timeout from default to 10 minutes
-- Kills blocking sessions after timeout expires
-- Monitor for killed session impacts

Kill specific blocking sessions manually

-- Identify and terminate sessions blocking BU lock acquisition
-- CAUTION: This terminates active user sessions
DECLARE @BlockingSessionId INT;

SELECT @BlockingSessionId = blocking_session_id
FROM sys.dm_exec_requests 
WHERE wait_type = 'LCK_M_BU_ABORT_BLOCKERS'
    AND blocking_session_id > 0;

IF @BlockingSessionId IS NOT NULL
BEGIN
    -- Verify session is safe to kill before executing
    SELECT 
        session_id,
        login_name,
        program_name,
        host_name,
        last_request_start_time
    FROM sys.dm_exec_sessions 
    WHERE session_id = @BlockingSessionId;
    
    -- Uncomment next line only after verification
    -- KILL @BlockingSessionId;
END

Switch to SELF termination mode

-- Change ALTER operation to kill itself instead of blockers
-- Safer option that avoids terminating user sessions
ALTER INDEX IX_YourIndex ON YourTable REBUILD 
WITH (ONLINE = ON,
      WAIT_AT_LOW_PRIORITY (MAX_DURATION = 5 MINUTES,
                           ABORT_AFTER_WAIT = SELF));
-- Operation cancels itself after timeout
-- No impact on blocking sessions
-- May require retry during low activity period

Implement retry logic with exponential backoff

-- Retry ALTER operation with increasing timeout values
DECLARE @AttemptCount INT = 1;
DECLARE @MaxAttempts INT = 3;
DECLARE @TimeoutMinutes INT = 2;

WHILE @AttemptCount <= @MaxAttempts
BEGIN
    BEGIN TRY
        DECLARE @SQL NVARCHAR(MAX) = 
            'ALTER INDEX IX_YourIndex ON YourTable REBUILD ' +
            'WITH (ONLINE = ON, ' +
            'WAIT_AT_LOW_PRIORITY (MAX_DURATION = ' + 
            CAST(@TimeoutMinutes AS VARCHAR(10)) + ' MINUTES, ' +
            'ABORT_AFTER_WAIT = SELF))';
        
        EXEC sp_executesql @SQL;
        BREAK; -- Success, exit loop
    END TRY
    BEGIN CATCH
        SET @AttemptCount = @AttemptCount + 1;
        SET @TimeoutMinutes = @TimeoutMinutes * 2; -- Double timeout
        WAITFOR DELAY '00:01:00'; -- Wait 1 minute between attempts
    END CATCH
END

AutoDBA generates fix scripts like these automatically, with impact estimates and rollback SQL included.

Prevention

Schedule ALTER TABLE and ALTER INDEX operations during maintenance windows when user activity is minimal. This reduces the likelihood of encountering blocking sessions that trigger the timeout mechanism.

Configure appropriate MAX_DURATION values based on historical blocking patterns. Query sys.dm_os_wait_stats regularly to establish baseline wait times for your workload, then set timeouts 2-3 times higher than typical blocking durations.

Use ABORT_AFTER_WAIT = SELF instead of BLOCKERS for non-critical maintenance operations. This approach prevents terminating user sessions while still providing timeout functionality. Reserve BLOCKERS mode for critical operations that must complete within specific windows.

Implement connection pooling with shorter connection timeouts to reduce long-running idle transactions that commonly cause blocking. Configure applications to use READ_COMMITTED_SNAPSHOT isolation level where appropriate to minimize reader-writer conflicts.

Monitor blocking chains proactively using Extended Events or custom monitoring solutions. Create alerts when blocking duration exceeds thresholds, allowing manual intervention before ALTER operations reach their timeout limits.

Consider partitioning strategies for large tables to reduce the scope of ALTER operations. Partition-level index rebuilds generate smaller lock footprints and shorter blocking windows compared to full table operations.

Need hands-on help?

Dealing with persistent lck_m_bu_abort_blockers issues across your environment? Samix Technology provides hands-on SQL Server performance consulting with 15+ years of production DBA experience.

Related Pages