Как реплицировать тупик SCH_S, SCH_M - PullRequest
2 голосов
/ 17 января 2020

У меня есть большой сценарий миграции (около 2000 строк), который завершается сбоем из-за тупиковой ситуации с некоторыми метаданными, и типы блокировки - SCH_S и SCH_M в соответствии с некоторыми данными xEvents, которые я записал на нем. Сценарий довольно сложный, поэтому я пытаюсь воссоздать тот же сценарий, используя минимальные сценарии, чтобы я мог исследовать его дальше. Одно из различий между моим сценарием и некоторыми из тех, которые я обнаружил в Интернете, заключается в том, что мой сценарий миграции выполняется в одном процессе, в отличие от многих примеров в Интернете, в которых одно окно используется для внесения некоторых изменений схемы, а другое - для запроса.

Следующий скрипт немного запутан, но это моя попытка воссоздать проблему, но она не работает.

SET NOEXEC OFF;
DROP FUNCTION IF EXISTS dbo.testfunc;
DROP TABLE IF EXISTS dbo.test;

-- Create some objects.
BEGIN TRANSACTION;
CREATE TABLE test
(
    testid INT
);
GO
IF @@error <> 0
   AND @@trancount > 0
    SET NOEXEC ON;
GO
CREATE FUNCTION testfunc
()
RETURNS INT
WITH SCHEMABINDING
AS
BEGIN
    RETURN
    (
        SELECT 2 * SUM(testid) FROM dbo.test
    );
END;
GO
IF @@error <> 0
   AND @@trancount > 0
    SET NOEXEC ON;
GO
IF @@error <> 0
   AND @@trancount > 0
    ROLLBACK TRANSACTION;
ELSE IF @@trancount > 0
    COMMIT TRANSACTION;

GO

-- Try to induce a SCH_S x SCH_M deadlock.
BEGIN TRANSACTION;
GO
IF @@error <> 0
   AND @@trancount > 0
    SET NOEXEC ON;
GO
ALTER FUNCTION dbo.testfunc
()
RETURNS INT
AS
BEGIN
    RETURN
    (
        SELECT 2 * SUM(testid) FROM dbo.test
    );
END;
GO
EXECUTE sp_lock
IF @@error <> 0
   AND @@trancount > 0
    ROLLBACK TRANSACTION;
GO
ALTER TABLE dbo.test ADD teststring VARCHAR(12) NULL;
GO
IF @@error <> 0
   AND @@trancount > 0
    SET NOEXEC ON;
GO
SELECT TOP (1)
       testid
FROM test;
GO
EXECUTE sp_lock
go
ALTER FUNCTION testfunc
()
RETURNS INT
WITH SCHEMABINDING
AS
BEGIN
    RETURN
    (
        SELECT TOP 1 testid FROM dbo.test ORDER BY testid DESC
    );
END;
GO
EXECUTE sp_lock
IF @@error <> 0
   AND @@trancount > 0
    SET NOEXEC ON;
GO
ALTER TABLE dbo.test ADD test_bit BIT NULL;
GO
EXECUTE sp_lock
IF @@error <> 0
   AND @@trancount > 0
    ROLLBACK TRANSACTION;
ELSE IF @@trancount > 0
    COMMIT TRANSACTION;
GO

Может кто-нибудь помочь мне воссоздать такой сценарий в одном процессе ?

Вот тупик xml отчет:

<deadlock>
 <victim-list>
  <victimProcess id="process1fbf61b8ca8" />
 </victim-list>
 <process-list>
  <process id="process1fbf61b8ca8" taskpriority="0" logused="0" waitresource="METADATA: database_id = 5 USER_TYPE(user_type_id = 264), lockPartitionId = 11" waittime="2517" ownerId="46927562" transactionname="@OptionIDs" lasttranstarted="2020-01-16T13:03:44.790" XDES="0x1fb0b790490" lockMode="Sch-S" schedulerid="12" kpid="20032" status="suspended" spid="63" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2020-01-16T13:03:44.787" lastbatchcompleted="2020-01-16T13:03:44.783" lastattention="1900-01-01T00:00:00.783" clientapp="Microsoft SQL Server Management Studio - Query" hostname="userhost" hostpid="11492" loginname="CORP\user" isolationlevel="serializable (4)" xactid="46926609" currentdb="5" lockTimeout="4294967295" clientoption1="671287392" clientoption2="390200">
   <executionStack>
    <frame procname="unknown" line="39" stmtstart="-1" sqlhandle="0x0300050048c3ee382a43d70044ab000000000000000000000000000000000000000000000000000000000000">
unknown    </frame>
    <frame procname="adhoc" line="66" stmtstart="-1" sqlhandle="0x010005009a7e7609c0be4a35fb01000000000000000000000000000000000000000000000000000000000000">
CREATE FUNCTION [dbo].[tvf_GetRawPOLineDataRelatedToOption]
(
    @CommunityID INT,
    @FloorPlanID INT,
    @OptionID INT,
    @RelatedIncludedOptionIDToRemove INT
)
RETURNS @ReturnData TABLE
(
    [POTemplateID] INT NOT NULL,
    [POTemplateItemID] INT NOT NULL,
    [POTemplateItemQuantityTypeID] INT NOT NULL,
    [Quantity] DECIMAL(12, 5) NULL,
    [FloorPlanQuantityTypeID] INT NULL,
    [DynamicQuantityPercentage] INT NULL,
    [QuantityForFlooringMaterialTypeID] INT NULL,
    [HomesiteQuantityTypeID] INT NULL
)
WITH SCHEMABINDING
AS
BEGIN
    DECLARE @DependencyData dbo.DependencyDataKeyValueTable;
    DECLARE @POTemplateDependencyFilterData dbo.DependencyFilterKeyValueTable;
    DECLARE @POTemplateItemDependencyFilterData dbo.DependencyFilterKeyValueTable;
    DECLARE @POTemplateIDs dbo.IndexedIntTable;

    /*
     *  Get the default dependency data for this floor plan in this community.
     */
    INSERT INTO @DependencyData
    SELECT [Key],
           [Value]
    FROM dbo.    </frame>
   </executionStack>
   <inputbuf>
CREATE FUNCTION [dbo].[tvf_GetRawPOLineDataRelatedToOption]
(
    @CommunityID INT,
    @FloorPlanID INT,
    @OptionID INT,
    @RelatedIncludedOptionIDToRemove INT
)
RETURNS @ReturnData TABLE
(
    [POTemplateID] INT NOT NULL,
    [POTemplateItemID] INT NOT NULL,
    [POTemplateItemQuantityTypeID] INT NOT NULL,
    [Quantity] DECIMAL(12, 5) NULL,
    [FloorPlanQuantityTypeID] INT NULL,
    [DynamicQuantityPercentage] INT NULL,
    [QuantityForFlooringMaterialTypeID] INT NULL,
    [HomesiteQuantityTypeID] INT NULL
)
WITH SCHEMABINDING
AS
BEGIN
    DECLARE @DependencyData dbo.DependencyDataKeyValueTable;
    DECLARE @POTemplateDependencyFilterData dbo.DependencyFilterKeyValueTable;
    DECLARE @POTemplateItemDependencyFilterData dbo.DependencyFilterKeyValueTable;
    DECLARE @POTemplateIDs dbo.IndexedIntTable;

    /*
     *  Get the default dependency data for this floor plan in this community.
     */
    INSERT INTO @DependencyData
    SELECT [Key],
           [Value]
    FROM dbo   </inputbuf>
  </process>
 </process-list>
 <resource-list>
  <metadatalock subresource="USER_TYPE" classid="user_type_id = 264" dbid="5" lockPartition="11" id="lock1fb8a56df00" mode="Sch-M">
   <owner-list>
    <owner id="process1fbf61b8ca8" mode="Sch-M" />
    <owner id="process1fbf61b8ca8" mode="Sch-S" requestType="wait" />
   </owner-list>
   <waiter-list>
    <waiter id="process1fbf61b8ca8" mode="Sch-S" requestType="wait" />
   </waiter-list>
  </metadatalock>
 </resource-list>
</deadlock>

deadlockgraph

1 Ответ

1 голос
/ 18 января 2020

Может ли кто-нибудь помочь мне воссоздать этот вид сценария в одном процессе?

Существует простая демонстрация этой проблемы здесь .

BEGIN TRAN

go

CREATE TYPE dbo.OptionIDs AS TABLE( OptionID INT PRIMARY KEY )

go

DECLARE @OptionIDs dbo.OptionIDs;

go

ROLLBACK 

Конечно, необычно иметь взаимоблокировку, включающую один процесс и один ресурс.

Ресурс metadatalock subresource="USER_TYPE" classid="user_type_id = 264"

Сессия уже содержит блокировку метаданных SCH-M для пользователя определенный тип (вероятно, как вы создали его ранее в скрипте), и это блокирует попытку получить блокировку SCH-S для того же объекта к тому же сеансу позже.

Имя транзакции, отображаемое на графике взаимоблокировок: @OptionIDs - это внутренняя системная транзакция, которая записывает в журнал транзакций tempdb при создании экземпляра типа таблицы, соответствующего DECLARE @OptionIDs выше. Это отдельная транзакция от транзакции окружающего пользователя, поэтому сеанс неожиданно не может получить блокировку SCH-S, несмотря на то, что уже удерживает блокировку SCH-M.

Об этом сообщил Аарон Бертран здесь

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...