Blog Post

SQL Server Blog
2 MIN READ

Temporal: Encountering Error 13535 Data modification failed on system-versioned table...

BobDorr's avatar
BobDorr
Icon for Microsoft rankMicrosoft
Jun 13, 2022

When 2 or more workers are modifying the same row, it is possible to encounter 13535.  The time of the begin transaction and the modification are the defining properties.  When the transaction begin time is before the latest row modification, error 13535 is encountered.

 

 

  1. T1 starts a transaction
    T1 is context switched or client has not submitted the modification request

  2. T2 starts a transaction, just after T1
    T2 updates the row in the table

  3. T1 attempts to update row and is blocked by T2 or T2 commits transaction before T1 issues the modification

  4. T2 commits the transaction

  5. T1 is assigned the lock
    T1 detects the temporal update for the row was at 12:00:01 but T1 started its transaction before 12:00:01 and the error is raised.

For such an issue the application needs to determine the desired behavior.  If T1 must complete before T2 then the application must sequence and complete the T1 query before the T2 query.  If serialization is not required, the application can perform a retry.

 

DEMO

 

set nocount on

go

 

use master

go

 

drop database temporalTest

go

create database temporalTest

go

 

use temporalTest

go

 

create schema Test

go

 

CREATE TABLE [Temporal](

    [TemporalId] [bigint] IDENTITY(1,1) NOT NULL,

    [DateModified] [datetime2](7) NOT NULL,

       [SysStartTime] [datetime2](7) GENERATED ALWAYS AS ROW START NOT NULL,

    [SysEndTime] [datetime2](7) GENERATED ALWAYS AS ROW END NOT NULL,

 

       CONSTRAINT [TemporalId] PRIMARY KEY CLUSTERED ([TemporalId] ASC)

        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON),

    PERIOD FOR SYSTEM_TIME ([SysStartTime], [SysEndTime])

    )WITH(

    SYSTEM_VERSIONING = ON ( HISTORY_TABLE = [Test].[TemporalHistory] )

    )

go

 

create PROCEDURE mysp

    @ID bigint

AS

        UPDATE

            Temporal

        SET

            DateModified =  GETUTCDATE()

        WHERE

            TemporalId = @Id

go

 

 

INSERT INTO Temporal

    (DateModified)  VALUES (GETUTCDATE())     

go

 

 

select * from Temporal

go

 

select * from Test.TemporalHistory

go

 

-- ostress -E -S.\sql19 -Q"temporalTest..mysp 1" -r999999999 -n4 -q

 

Updated Jun 13, 2022
Version 1.0
  • AbairM's avatar
    AbairM
    Copper Contributor

    Thanks again for sharing the knowledge.  I keep forgetting how handy OStress can be for testing / reproducing race conditions.  After reading a couple of the issues you've been troubleshooting I know now to keep that handy in the toolbox. 

     

    Gone are the days of me opening 2 multiple query windows in SSMS adding a "GO 5000" to the bottom of each query hoping that the issue will repro before SSMS crashes from too many result panes being returned!! :lol:

     

    Keep it up!

    -Mike

  • wfvdijk's avatar
    wfvdijk
    Copper Contributor

    can this prevented if T1 was doing a select "WITH (UPDLOCK)"?

  • DanrCarollo's avatar
    DanrCarollo
    Copper Contributor

    We just deployed Temporal Tables (SQL Server 2019) and didn't even make it out of our DEV environment without running into this error!   I did find a workaround using TRY/CATCH logic.  Unfortunately, the client code is using SQL Alchemy, generating SQL on the fly, which means we can't just wrap this into a stored proc and trying to re-work the code would be too expensive.   In the end, we decided to go with Change Data Capture to build an audit history.  Although querying historical data in CDC is not as elegant as with TT, it seems to work for our scenario.  

    It's been seven years since this feature was released -- would have liked to see a fix for this by now.