Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 Merge Replication, ошибка перехода Identity Range при непоследовательных рейнджах.  [new]
slowwalker
Member

Откуда:
Сообщений: 7
Есть сервер-паблишер и много серверов-подписчиков. Почти во всех таблицах используется интовое Identity. Во время репликации подписчикам выдается 2 рейнджа. Если текущий айдентити таблицы "находится" во втором рейндже - он становится первым рейнджем, и выдается новый 2-ой.
Ошибка возникает при переходе текущего адентити из 1-го рейнджа во второй, если эти рейнджи не последовательны. Приведу примеры:

Таблицы tbl выдались такие Identity Range: 1001..2000 и 2001..3000
Добавляем в нее новые записи. ID 1998... 1999... 2000... 2001... 2002
Мы успешно перешли во второй рейндж, теперь во время репликации будет выдан новый.

Таблицы tbl выдались такие Identity Range: 1001..2000 и 4001..5000
(айдишники с 2000 по 4000 были выданы другому подписчику)
Добавляем в нее новые записи. ID 1998... 1999... 2000... ERROR
Теперь при попытке добавить новую запись у нас всегда будет падать ошибка "The INSERT statement conflicted with the CHECK constraint "repl_identity_range..."
Что бы ее устранить, требуется начать репликацию с подписчиком - тогда 4001...5000 становится первым рейнжем и ID новой записи будет уже 4001.

Вот полный текст ошибки:
error
System.Data.SqlClient.SqlException : Error 547, Level 16, State 0, Procedure spEtnNewCustomer, Line 39, Message: The INSERT statement conflicted with the CHECK constraint "repl_identity_range_97D55D40_C5D1_4469_9A06_5F395CA13B99". The conflict occurred in database "db0018", table "dbo.tblEturnLogs", column 'EturnLogID'.


SQL Server 2012, "переехали" буквально неделю назад, но ошибка имела место быть на SQL Server 2008. При том появилась спонтанно примерно осенью 2011 года, нам не удается связать ее с каким-либо значимым событием, вроде установки обновления на сервер. Надеялись, что с переходом на 2012 сервер проблема будет решена (во время перехода были переинициализированы все подписки) - не помогло. До этого же все работало успешно, идентити благополучно переходил из рейнджа в рейндж.

Главная проблема ошибки, что возникает она спонтанно (накладно отслеживать все идентити во всех таблицах) и требует вмешательство оператора для ее разрешения. Может, кто сталкивался с таким и знает способы устранения или причины?
19 июн 12, 14:04    [12738398]     Ответить | Цитировать Сообщить модератору
 Re: Merge Replication, ошибка перехода Identity Range при непоследовательных рейнджах.  [new]
slowwalker
Member

Откуда:
Сообщений: 7
Воспроизвел ошибку на тестовых БД.
Вот текст ошибки самого SQL:
error
Msg 548, Level 16, State 2, Line 1
The insert failed. It conflicted with an identity range check constraint in database '(1)TestSubscr', replicated table 'dbo.tblReplTest', column 'TestID'. If the identity column is automatically managed by replication, update the range as follows: for the Publisher, execute sp_adjustpublisheridentityrange; for the Subscriber, run the Distribution Agent or the Merge Agent.
The statement has been terminated.

Выборка из таблицы перед попыткой произвести в нее Insert:
TestIDTestDateTestNamerowguid
392012-06-19 15:53:22.600Jun 19 2012 3:53PMD3FB355E-05BA-E111-A082-00E081412B06
402012-06-19 15:53:28.420Jun 19 2012 3:53PMD4FB355E-05BA-E111-A082-00E081412B06
412012-06-19 15:53:31.007Jun 19 2012 3:53PMD5FB355E-05BA-E111-A082-00E081412B06

На таблице есть автоматически созданный констрейинт "repl_identity_range_73C639B1_BB79_4993_A0E6_69C051F037A4":
ALTER TABLE [dbo].[tblReplTest]  WITH NOCHECK ADD  CONSTRAINT [repl_identity_range_73C639B1_BB79_4993_A0E6_69C051F037A4]
	CHECK NOT FOR REPLICATION (([TestID]>(31) AND [TestID]<=(41) OR [TestID]>(61) AND [TestID]<=(71)))
GO

ALTER TABLE [dbo].[tblReplTest] CHECK CONSTRAINT [repl_identity_range_73C639B1_BB79_4993_A0E6_69C051F037A4]
GO

В нем репликация прописала рейндж "магическими" числами. Как видно - второй рейндж есть. Но попасть мы в него не можем.

Вот этот же констрейнт, только созданный ранее, в нем рейнджи последовательны.
ALTER TABLE [dbo].[tblReplTest]  WITH NOCHECK ADD  CONSTRAINT [repl_identity_range_73C639B1_BB79_4993_A0E6_69C051F037A4] 
	CHECK NOT FOR REPLICATION (([TestID]>(21) AND [TestID]<=(31) OR [TestID]>(31) AND [TestID]<=(41)))
GO

ALTER TABLE [dbo].[tblReplTest] CHECK CONSTRAINT [repl_identity_range_73C639B1_BB79_4993_A0E6_69C051F037A4]
GO

Вставить записи "на границе" получилось без проблем и ошибок:
TestIDTestDateTestNamerowguid
292012-06-19 15:52:12.947Jun 19 2012 3:52PM00BBB134-05BA-E111-A082-00E081412B06
302012-06-19 15:52:20.453Jun 19 2012 3:52PM01BBB134-05BA-E111-A082-00E081412B06
312012-06-19 15:52:21.083Jun 19 2012 3:52PM02BBB134-05BA-E111-A082-00E081412B06
322012-06-19 15:52:22.163Jun 19 2012 3:52PM03BBB134-05BA-E111-A082-00E081412B06
19 июн 12, 16:17    [12739857]     Ответить | Цитировать Сообщить модератору
 Re: Merge Replication, ошибка перехода Identity Range при непоследовательных рейнджах.  [new]
slowwalker
Member

Откуда:
Сообщений: 7
Обнаружили, что на подписчике совершенно иной, чем на паблишере репликационный тригер:
+ На паблишере
ALTER trigger [dbo].[MSmerge_ins_73C639B1BB794993A0E669C051F037A4] on [dbTest].[dbo].[tblReplTest] for insert   as 
    declare @is_mergeagent bit, @at_publisher bit, @retcode smallint 

    set rowcount 0
    set transaction isolation level read committed

        exec @retcode = sys.sp_MSisreplmergeagent @is_mergeagent output, @at_publisher output
        if @@error <> 0 or @retcode <> 0
        begin
            rollback tran
            return
        end  
    if is_member('db_owner') = 1
    begin
        -- select the range values from the MSmerge_identity_range table
        -- this can be hardcoded if performance is a problem
        declare @range_begin numeric(38,0)
        declare @range_end numeric(38,0)
        declare @next_range_begin numeric(38,0)
        declare @next_range_end numeric(38,0)

        select @range_begin = range_begin,
               @range_end = range_end,
               @next_range_begin = next_range_begin,
               @next_range_end = next_range_end
            from dbo.MSmerge_identity_range where artid='73C639B1-BB79-4993-A0E6-69C051F037A4' and subid='AAD198BE-BAE7-4A71-A9F0-8C320184D73E' and is_pub_range=0

        if @range_begin is not null and @range_end is not NULL and @next_range_begin is not null and @next_range_end is not NULL
        begin
            if IDENT_CURRENT('[dbo].[tblReplTest]') = @range_end
            begin
                DBCC CHECKIDENT ('[dbo].[tblReplTest]', RESEED, @next_range_begin) with no_infomsgs
            end
            else if IDENT_CURRENT('[dbo].[tblReplTest]') >= @next_range_end
            begin
                exec sys.sp_MSrefresh_publisher_idrange '[dbo].[tblReplTest]', 'AAD198BE-BAE7-4A71-A9F0-8C320184D73E', '73C639B1-BB79-4993-A0E6-69C051F037A4', 2, 1
                if @@error<>0 or @retcode<>0
                    goto FAILURE
            end
        end
    end 
    declare @article_rows_inserted int
    select @article_rows_inserted =  count(*) from inserted 
    if @article_rows_inserted = 0 
        return
    declare @tablenick int, @rowguid uniqueidentifier
    , @replnick binary(6), @lineage varbinary(311), @colv1 varbinary(1), @cv varbinary(1)
    , @ccols int, @newgen bigint, @version int, @curversion int
    , @oldmaxversion int, @child_newgen bigint, @child_oldmaxversion int, @child_metadatarows_updated int 
    , @logical_record_parent_rowguid uniqueidentifier 
    , @num_parent_rows int, @parent_row_inserted bit, @ts_rows_exist bit, @marker uniqueidentifier 
    declare @dt datetime
    declare @nickbin varbinary(8)
    declare @error int 
    set nocount on
    set @tablenick = 4450003    
    set @lineage = 0x0
    set @retcode = 0
    select @oldmaxversion= maxversion_at_cleanup from dbo.sysmergearticles where nickname = @tablenick
    select @dt = getdate()

    select @replnick = 0xbae7aad198be 
    set @nickbin= @replnick + 0xFF

    select @newgen = NULL
        select top 1 @newgen = generation from [dbo].[MSmerge_genvw_73C639B1BB794993A0E669C051F037A4] with (rowlock, updlock, readpast) 
        where art_nick = 4450003     and genstatus = 0
            and  changecount <= (1000 - isnull(@article_rows_inserted,0))
    if @newgen is NULL
    begin
        insert into [dbo].[MSmerge_genvw_73C639B1BB794993A0E669C051F037A4] with (rowlock)
            (guidsrc, genstatus, art_nick, nicknames, coldate, changecount)
             values   (newid(), 0, @tablenick, @nickbin, @dt, @article_rows_inserted)
        select @error = @@error, @newgen = @@identity    
        if @error<>0 or @newgen is NULL
            goto FAILURE
    end
    else
    begin
        -- now update the changecount of the generation we go to reflect the number of rows we put in this generation
        update [dbo].[MSmerge_genvw_73C639B1BB794993A0E669C051F037A4]  with (rowlock)
            set changecount = changecount + @article_rows_inserted
            where generation = @newgen
        if @@error<>0 goto FAILURE
    end
    set @lineage = { fn UPDATELINEAGE (0x0, @replnick, 1) }
            set @colv1 = NULL
    if (@@error <> 0)
    begin
        goto FAILURE
    end

    select @ts_rows_exist = 0 
        select @ts_rows_exist = 1 where exists (select ts.rowguid from inserted i, [dbo].[MSmerge_tsvw_73C639B1BB794993A0E669C051F037A4] ts with (rowlock) where ts.tablenick = @tablenick and ts.rowguid = i.rowguidcol)     
    if @ts_rows_exist = 1
    begin    
        select @version = max({fn GETMAXVERSION(lineage)}) from [dbo].[MSmerge_tsvw_73C639B1BB794993A0E669C051F037A4] where 
            tablenick = @tablenick and rowguid in (select rowguidcol from inserted) 

        if @version is not null
        begin
            -- reset lineage and colv to higher version...
            set @curversion = 0
            while (@curversion <= @version)
            begin
                set @lineage = { fn UPDATELINEAGE (@lineage, @replnick, @oldmaxversion+1) }
                set @curversion= { fn GETMAXVERSION(@lineage) }
            end

            if (@colv1 IS NOT NULL)
                set @colv1 = { fn UPDATECOLVBM(@colv1, @replnick, 0x01, 0x00, { fn GETMAXVERSION(@lineage) }) }    
                delete from [dbo].[MSmerge_tsvw_73C639B1BB794993A0E669C051F037A4] with (rowlock) where tablenick = @tablenick and rowguid in
                    (select rowguidcol from inserted) 
        end
    end 
    select @marker = newid()  
        insert into [dbo].[MSmerge_ctsv_73C639B1BB794993A0E669C051F037A4] with (rowlock) (tablenick, rowguid, lineage, colv1, generation, partchangegen, marker) 
        select @tablenick, rowguidcol, @lineage, @colv1, @newgen, (-@newgen), @marker
        from inserted i where not exists
        (select rowguid from [dbo].[MSmerge_ctsv_73C639B1BB794993A0E669C051F037A4] with (readcommitted, rowlock, readpast) where tablenick = @tablenick and rowguid = i.rowguidcol)  
    if @@error <> 0 
        goto FAILURE   
            insert into [dbo].[MSmerge_cpmv_73C639B1BB794993A0E669C051F037A4] with (rowlock) (publication_number, tablenick, rowguid, partition_id)
            select distinct 1, @tablenick, v.[rowguid], v.partition_id
            from (  select [tblReplTest].[rowguid], partition_id = -1 from inserted [tblReplTest]   ) as v   

    return
FAILURE:
    if @@trancount > 0
        rollback tran
    raiserror (20041, 16, -1)
    return     

+ На подписчике
ALTER trigger [dbo].[MSmerge_ins_73C639B1BB794993A0E669C051F037A4] on [(1)TestSubscr].[dbo].[tblReplTest] for insert   as 
    declare @is_mergeagent bit, @at_publisher bit, @retcode smallint 

    set rowcount 0
    set transaction isolation level read committed

        select @is_mergeagent = convert(bit, sessionproperty('replication_agent'))
        select @at_publisher = 0 
    if (select trigger_nestlevel()) = 1 and @is_mergeagent = 1
        return  
    declare @article_rows_inserted int
    select @article_rows_inserted =  count(*) from inserted 
    if @article_rows_inserted = 0 
        return
    declare @tablenick int, @rowguid uniqueidentifier
    , @replnick binary(6), @lineage varbinary(311), @colv1 varbinary(1), @cv varbinary(1)
    , @ccols int, @newgen bigint, @version int, @curversion int
    , @oldmaxversion int, @child_newgen bigint, @child_oldmaxversion int, @child_metadatarows_updated int 
    , @logical_record_parent_rowguid uniqueidentifier 
    , @num_parent_rows int, @parent_row_inserted bit, @ts_rows_exist bit, @marker uniqueidentifier 
    declare @dt datetime
    declare @nickbin varbinary(8)
    declare @error int 
    set nocount on
    set @tablenick = 4450003    
    set @lineage = 0x0
    set @retcode = 0
    select @oldmaxversion= maxversion_at_cleanup from dbo.sysmergearticles where nickname = @tablenick
    select @dt = getdate()

    select @replnick = 0x10e8829f4d02 
    set @nickbin= @replnick + 0xFF

    select @newgen = NULL
        select top 1 @newgen = generation from [dbo].[MSmerge_genvw_73C639B1BB794993A0E669C051F037A4] with (rowlock, updlock, readpast) 
        where art_nick = 4450003     and genstatus = 0
    if @newgen is NULL
    begin
        insert into [dbo].[MSmerge_genvw_73C639B1BB794993A0E669C051F037A4] with (rowlock)
            (guidsrc, genstatus, art_nick, nicknames, coldate, changecount)
             values   (newid(), 0, @tablenick, @nickbin, @dt, @article_rows_inserted)
        select @error = @@error, @newgen = @@identity    
        if @error<>0 or @newgen is NULL
            goto FAILURE
    end
    set @lineage = { fn UPDATELINEAGE (0x0, @replnick, 1) }
            set @colv1 = NULL
    if (@@error <> 0)
    begin
        goto FAILURE
    end

    select @ts_rows_exist = 0 
        select @ts_rows_exist = 1 where exists (select ts.rowguid from inserted i, [dbo].[MSmerge_tsvw_73C639B1BB794993A0E669C051F037A4] ts with (rowlock) where ts.tablenick = @tablenick and ts.rowguid = i.rowguidcol)     
    if @ts_rows_exist = 1
    begin    
        select @version = max({fn GETMAXVERSION(lineage)}) from [dbo].[MSmerge_tsvw_73C639B1BB794993A0E669C051F037A4] where 
            tablenick = @tablenick and rowguid in (select rowguidcol from inserted) 

        if @version is not null
        begin
            -- reset lineage and colv to higher version...
            set @curversion = 0
            while (@curversion <= @version)
            begin
                set @lineage = { fn UPDATELINEAGE (@lineage, @replnick, @oldmaxversion+1) }
                set @curversion= { fn GETMAXVERSION(@lineage) }
            end

            if (@colv1 IS NOT NULL)
                set @colv1 = { fn UPDATECOLVBM(@colv1, @replnick, 0x01, 0x00, { fn GETMAXVERSION(@lineage) }) }    
                delete from [dbo].[MSmerge_tsvw_73C639B1BB794993A0E669C051F037A4] with (rowlock) where tablenick = @tablenick and rowguid in
                    (select rowguidcol from inserted) 
        end
    end 
    select @marker = newid()  
        insert into [dbo].[MSmerge_ctsv_73C639B1BB794993A0E669C051F037A4] with (rowlock) (tablenick, rowguid, lineage, colv1, generation, partchangegen, marker) 
        select @tablenick, rowguidcol, @lineage, @colv1, @newgen, (-@newgen), @marker
        from inserted i where not exists
        (select rowguid from [dbo].[MSmerge_ctsv_73C639B1BB794993A0E669C051F037A4] with (readcommitted, rowlock, readpast) where tablenick = @tablenick and rowguid = i.rowguidcol)  
    if @@error <> 0 
        goto FAILURE   

    return
FAILURE:
    if @@trancount > 0
        rollback tran
    raiserror (20041, 16, -1)
    return     

Такое ощущение, что они перепутаны местами или на подписчике что-то не то. Пытаемся понять, почему так.
19 июн 12, 18:15    [12740886]     Ответить | Цитировать Сообщить модератору
 Re: Merge Replication, ошибка перехода Identity Range при непоследовательных рейнджах.  [new]
slowwalker
Member

Откуда:
Сообщений: 7
Гм. Детальное рассмотрение выявило, что один из данных триггеров не корректен из-за ошибочных настроек репликации на "тестовом стенде".

Если все настроить "как на боевом" - то сообщение об ошибке другое:
Error
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the CHECK constraint "repl_identity_range_97D55D40_C5D1_4469_9A06_5F395CA13B99". The conflict occurred in database "dbAPT0011", table "dbo.tblEturnLogs", column 'EturnLogID'.
The statement has been terminated.


Триггеры так же различаются, но меньше:
+ Паблишер
ALTER trigger [dbo].[MSmerge_ins_17014E41651446CA95A777A9552DA9D8] on [dbTestPub].[dbo].[tblTest] for insert   as 
    declare @is_mergeagent bit, @at_publisher bit, @retcode smallint 

    set rowcount 0
    set transaction isolation level read committed

        select @is_mergeagent = convert(bit, sessionproperty('replication_agent'))
        select @at_publisher = 0 
    if (select trigger_nestlevel()) = 1 and @is_mergeagent = 1
        return  
    if is_member('db_owner') = 1
    begin
        -- select the range values from the MSmerge_identity_range table
        -- this can be hardcoded if performance is a problem
        declare @range_begin numeric(38,0)
        declare @range_end numeric(38,0)
        declare @next_range_begin numeric(38,0)
        declare @next_range_end numeric(38,0)

        select @range_begin = range_begin,
               @range_end = range_end,
               @next_range_begin = next_range_begin,
               @next_range_end = next_range_end
            from dbo.MSmerge_identity_range where artid='17014E41-6514-46CA-95A7-77A9552DA9D8' and subid='E94A381C-6376-4D73-8D50-AEC87383A0F6' and is_pub_range=0

        if @range_begin is not null and @range_end is not NULL and @next_range_begin is not null and @next_range_end is not NULL
        begin
            if IDENT_CURRENT('[dbo].[tblTest]') = @range_end
            begin
                DBCC CHECKIDENT ('[dbo].[tblTest]', RESEED, @next_range_begin) with no_infomsgs
            end
            else if IDENT_CURRENT('[dbo].[tblTest]') >= @next_range_end
            begin
                exec sys.sp_MSrefresh_publisher_idrange '[dbo].[tblTest]', 'E94A381C-6376-4D73-8D50-AEC87383A0F6', '17014E41-6514-46CA-95A7-77A9552DA9D8', 2, 1
                if @@error<>0 or @retcode<>0
                    goto FAILURE
            end
        end
    end 
    declare @article_rows_inserted int
    select @article_rows_inserted =  count(*) from inserted 
    if @article_rows_inserted = 0 
        return
    declare @tablenick int, @rowguid uniqueidentifier
    , @replnick binary(6), @lineage varbinary(311), @colv1 varbinary(1), @cv varbinary(1)
    , @ccols int, @newgen bigint, @version int, @curversion int
    , @oldmaxversion int, @child_newgen bigint, @child_oldmaxversion int, @child_metadatarows_updated int 
    , @logical_record_parent_rowguid uniqueidentifier 
    , @num_parent_rows int, @parent_row_inserted bit, @ts_rows_exist bit, @marker uniqueidentifier 
    declare @dt datetime
    declare @nickbin varbinary(8)
    declare @error int 
    set nocount on
    set @tablenick = 1940000    
    set @lineage = 0x0
    set @retcode = 0
    select @oldmaxversion= maxversion_at_cleanup from dbo.sysmergearticles where nickname = @tablenick
    select @dt = getdate()

    select @replnick = 0x6376e94a381c 
    set @nickbin= @replnick + 0xFF

    select @newgen = NULL
        select top 1 @newgen = generation from [dbo].[MSmerge_genvw_17014E41651446CA95A777A9552DA9D8] with (rowlock, updlock, readpast) 
        where art_nick = 1940000     and genstatus = 0
            and  changecount <= (1000 - isnull(@article_rows_inserted,0))
    if @newgen is NULL
    begin
        insert into [dbo].[MSmerge_genvw_17014E41651446CA95A777A9552DA9D8] with (rowlock)
            (guidsrc, genstatus, art_nick, nicknames, coldate, changecount)
             values   (newid(), 0, @tablenick, @nickbin, @dt, @article_rows_inserted)
        select @error = @@error, @newgen = @@identity    
        if @error<>0 or @newgen is NULL
            goto FAILURE
    end
    else
    begin
        -- now update the changecount of the generation we go to reflect the number of rows we put in this generation
        update [dbo].[MSmerge_genvw_17014E41651446CA95A777A9552DA9D8]  with (rowlock)
            set changecount = changecount + @article_rows_inserted
            where generation = @newgen
        if @@error<>0 goto FAILURE
    end
    set @lineage = { fn UPDATELINEAGE (0x0, @replnick, 1) }
            set @colv1 = NULL
    if (@@error <> 0)
    begin
        goto FAILURE
    end

    select @ts_rows_exist = 0 
        select @ts_rows_exist = 1 where exists (select ts.rowguid from inserted i, [dbo].[MSmerge_tsvw_17014E41651446CA95A777A9552DA9D8] ts with (rowlock) where ts.tablenick = @tablenick and ts.rowguid = i.rowguidcol)     
    if @ts_rows_exist = 1
    begin    
        select @version = max({fn GETMAXVERSION(lineage)}) from [dbo].[MSmerge_tsvw_17014E41651446CA95A777A9552DA9D8] where 
            tablenick = @tablenick and rowguid in (select rowguidcol from inserted) 

        if @version is not null
        begin
            -- reset lineage and colv to higher version...
            set @curversion = 0
            while (@curversion <= @version)
            begin
                set @lineage = { fn UPDATELINEAGE (@lineage, @replnick, @oldmaxversion+1) }
                set @curversion= { fn GETMAXVERSION(@lineage) }
            end

            if (@colv1 IS NOT NULL)
                set @colv1 = { fn UPDATECOLVBM(@colv1, @replnick, 0x01, 0x00, { fn GETMAXVERSION(@lineage) }) }    
                delete from [dbo].[MSmerge_tsvw_17014E41651446CA95A777A9552DA9D8] with (rowlock) where tablenick = @tablenick and rowguid in
                    (select rowguidcol from inserted) 
        end
    end 
    select @marker = newid()  
        insert into [dbo].[MSmerge_ctsv_17014E41651446CA95A777A9552DA9D8] with (rowlock) (tablenick, rowguid, lineage, colv1, generation, partchangegen, marker) 
        select @tablenick, rowguidcol, @lineage, @colv1, @newgen, (-@newgen), @marker
        from inserted i where not exists
        (select rowguid from [dbo].[MSmerge_ctsv_17014E41651446CA95A777A9552DA9D8] with (readcommitted, rowlock, readpast) where tablenick = @tablenick and rowguid = i.rowguidcol)  
    if @@error <> 0 
        goto FAILURE   

    return
FAILURE:
    if @@trancount > 0
        rollback tran
    raiserror (20041, 16, -1)
    return     

+ Подписчик
ALTER trigger [dbo].[MSmerge_ins_17014E41651446CA95A777A9552DA9D8] on [dbTestSub1].[dbo].[tblTest] for insert   as 
    declare @is_mergeagent bit, @at_publisher bit, @retcode smallint 

    set rowcount 0
    set transaction isolation level read committed

        select @is_mergeagent = convert(bit, sessionproperty('replication_agent'))
        select @at_publisher = 0 
    if (select trigger_nestlevel()) = 1 and @is_mergeagent = 1
        return  
    if is_member('db_owner') = 1
    begin
        -- select the range values from the MSmerge_identity_range table
        -- this can be hardcoded if performance is a problem
        declare @range_begin numeric(38,0)
        declare @range_end numeric(38,0)
        declare @next_range_begin numeric(38,0)
        declare @next_range_end numeric(38,0)

        select @range_begin = range_begin,
               @range_end = range_end,
               @next_range_begin = next_range_begin,
               @next_range_end = next_range_end
            from dbo.MSmerge_identity_range where artid='17014E41-6514-46CA-95A7-77A9552DA9D8' and subid='91CA73FF-43E3-415E-82D6-535B79AC968F' and is_pub_range=0

        if @range_begin is not null and @range_end is not NULL and @next_range_begin is not null and @next_range_end is not NULL
        begin
            if IDENT_CURRENT('[dbo].[tblTest]') = @range_end
            begin
                DBCC CHECKIDENT ('[dbo].[tblTest]', RESEED, @next_range_begin) with no_infomsgs
            end
        end
    end 
    declare @article_rows_inserted int
    select @article_rows_inserted =  count(*) from inserted 
    if @article_rows_inserted = 0 
        return
    declare @tablenick int, @rowguid uniqueidentifier
    , @replnick binary(6), @lineage varbinary(311), @colv1 varbinary(1), @cv varbinary(1)
    , @ccols int, @newgen bigint, @version int, @curversion int
    , @oldmaxversion int, @child_newgen bigint, @child_oldmaxversion int, @child_metadatarows_updated int 
    , @logical_record_parent_rowguid uniqueidentifier 
    , @num_parent_rows int, @parent_row_inserted bit, @ts_rows_exist bit, @marker uniqueidentifier 
    declare @dt datetime
    declare @nickbin varbinary(8)
    declare @error int 
    set nocount on
    set @tablenick = 1940000    
    set @lineage = 0x0
    set @retcode = 0
    select @oldmaxversion= maxversion_at_cleanup from dbo.sysmergearticles where nickname = @tablenick
    select @dt = getdate()

    select @replnick = 0x43e391ca73ff 
    set @nickbin= @replnick + 0xFF

    select @newgen = NULL
        select top 1 @newgen = generation from [dbo].[MSmerge_genvw_17014E41651446CA95A777A9552DA9D8] with (rowlock, updlock, readpast) 
        where art_nick = 1940000     and genstatus = 0
    if @newgen is NULL
    begin
        insert into [dbo].[MSmerge_genvw_17014E41651446CA95A777A9552DA9D8] with (rowlock)
            (guidsrc, genstatus, art_nick, nicknames, coldate, changecount)
             values   (newid(), 0, @tablenick, @nickbin, @dt, @article_rows_inserted)
        select @error = @@error, @newgen = @@identity    
        if @error<>0 or @newgen is NULL
            goto FAILURE
    end
    set @lineage = { fn UPDATELINEAGE (0x0, @replnick, 1) }
            set @colv1 = NULL
    if (@@error <> 0)
    begin
        goto FAILURE
    end

    select @ts_rows_exist = 0 
        select @ts_rows_exist = 1 where exists (select ts.rowguid from inserted i, [dbo].[MSmerge_tsvw_17014E41651446CA95A777A9552DA9D8] ts with (rowlock) where ts.tablenick = @tablenick and ts.rowguid = i.rowguidcol)     
    if @ts_rows_exist = 1
    begin    
        select @version = max({fn GETMAXVERSION(lineage)}) from [dbo].[MSmerge_tsvw_17014E41651446CA95A777A9552DA9D8] where 
            tablenick = @tablenick and rowguid in (select rowguidcol from inserted) 

        if @version is not null
        begin
            -- reset lineage and colv to higher version...
            set @curversion = 0
            while (@curversion <= @version)
            begin
                set @lineage = { fn UPDATELINEAGE (@lineage, @replnick, @oldmaxversion+1) }
                set @curversion= { fn GETMAXVERSION(@lineage) }
            end

            if (@colv1 IS NOT NULL)
                set @colv1 = { fn UPDATECOLVBM(@colv1, @replnick, 0x01, 0x00, { fn GETMAXVERSION(@lineage) }) }    
                delete from [dbo].[MSmerge_tsvw_17014E41651446CA95A777A9552DA9D8] with (rowlock) where tablenick = @tablenick and rowguid in
                    (select rowguidcol from inserted) 
        end
    end 
    select @marker = newid()  
        insert into [dbo].[MSmerge_ctsv_17014E41651446CA95A777A9552DA9D8] with (rowlock) (tablenick, rowguid, lineage, colv1, generation, partchangegen, marker) 
        select @tablenick, rowguidcol, @lineage, @colv1, @newgen, (-@newgen), @marker
        from inserted i where not exists
        (select rowguid from [dbo].[MSmerge_ctsv_17014E41651446CA95A777A9552DA9D8] with (readcommitted, rowlock, readpast) where tablenick = @tablenick and rowguid = i.rowguidcol)  
    if @@error <> 0 
        goto FAILURE   

    return
FAILURE:
    if @@trancount > 0
        rollback tran
    raiserror (20041, 16, -1)
    return     


За "перескок" в новый рейндж отвечает этот кусок триггера:
        select @range_begin = range_begin,
               @range_end = range_end,
               @next_range_begin = next_range_begin,
               @next_range_end = next_range_end
            from dbo.MSmerge_identity_range where artid='17014E41-6514-46CA-95A7-77A9552DA9D8' and subid='91CA73FF-43E3-415E-82D6-535B79AC968F' and is_pub_range=0

        if @range_begin is not null and @range_end is not NULL and @next_range_begin is not null and @next_range_end is not NULL
        begin
            if IDENT_CURRENT('[dbo].[tblTest]') = @range_end
            begin
                DBCC CHECKIDENT ('[dbo].[tblTest]', RESEED, @next_range_begin) with no_infomsgs
            end
        end

Особенно примечательна строчка:
 if IDENT_CURRENT('[dbo].[tblTest]') = @range_end

Если вставлять в таблицу сразу много строчек так, что бы идентити последней получился больше, чем конец первого рейнджа или откатить транзакцию, в которой пытались вставить запись с идентити == @range_end - то следующий иентити будет превышать @range_end, что не позволит отработать этому условию и "перескочить" в новый диапазон. Ну, при построчном добавлении все ОК.
По идее, если модифицировать эту строчку триггера так:
if (IDENT_CURRENT('[dbo].[tblTest]') >= @range_end AND IDENT_CURRENT('[dbo].[tblTest]') < @next_range_begin)

То "перескок" будет происходить даже при "сбойных" или "массовых" вставках. Но это репликационный триггер, да и тем более подобные находятся на всех таблицах, участвующих в репликации. Если изменить его на паблишере - репликация вовсе перестанет работать - он там другой, и при распространении на точки столкнется с проблемой отсутствия некоторых объектов.
Думаем, почему так и что с этим делать.
20 июн 12, 12:25    [12744846]     Ответить | Цитировать Сообщить модератору
 Re: Merge Replication, ошибка перехода Identity Range при непоследовательных рейнджах.  [new]
slowwalker
Member

Откуда:
Сообщений: 7
 if IDENT_CURRENT('[dbo].[tblTest]') = @range_end

Равенство здесь обусловлено тем, что если IDENT_CURRENT('[dbo].[tblTest]') будет больше @range_end до триггера дело так и не дойдет, т.к. insert откатится ошибкой констрейнта на этой же таблице. Если же сделать свой INSTEAD OF INSERT триггер, то возникает проблема сохранения данных - их надо добавлять "в ручную" в теле триггера. Написать свой INSTEAD OF INSERT триггер на каждую таблицу в репликации - нереальная для реализации и, что более критично, для дальнейшей поддержки.
Переход же на uniqueidentifier так же не реален на этой огромной БД и ее окружении (ПО).
20 июн 12, 14:00    [12745771]     Ответить | Цитировать Сообщить модератору
 Re: Merge Replication, ошибка перехода Identity Range при непоследовательных рейнджах.  [new]
slowwalker
Member

Откуда:
Сообщений: 7
Так и не получилось пока что починить это. Вставил костыль. Сделал процедуру, которая с помощью sysmergearticles и MSmerge_identity_range таблиц проверяет у каждой таблицы в репликации близость к аварийной ситуации или ее наступление и переносит текущий идентити во второй диапазон, с помощью DBCC CHECKIDENT. Процедура вызывается софтом, который отлавливает ошибку. Это позволяет минимизировать работу оператора и "чиниться" с отсутствующим коннектом с основной базой.

Неужели никто не сталкивался с такой проблемой? Интересно узнать другие варианты решения, более красивые.
26 июн 12, 14:24    [12776696]     Ответить | Цитировать Сообщить модератору
 Re: Merge Replication, ошибка перехода Identity Range при непоследовательных рейнджах.  [new]
aleonov
Member

Откуда:
Сообщений: 96
slowwalker,

насколько я помню есть несколько способов и правил чтобы починить идентити.

1. если вставка на подписчиках происходит довольно интенсивно, то следует увеличить диапазон значений, чтобы до следующий репликации он не был исчерпан.
2. следующий диапазон выделяется когда текущий исчерпан на 90%, насколько я помню это значение можно задавать.
3. используйте процедуру sp_adjustpublisheridentityrange которая чинит идентити для всех подписчиков, посмотрите в документации.
26 июн 12, 21:28    [12779253]     Ответить | Цитировать Сообщить модератору
 Re: Merge Replication, ошибка перехода Identity Range при непоследовательных рейнджах.  [new]
aleonov
Member

Откуда:
Сообщений: 96
aleonov
2. следующий диапазон выделяется когда текущий исчерпан на 90%, насколько я помню это значение можно задавать.



нашел - Range threshold percentage
26 июн 12, 21:32    [12779264]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить