2
votes

I cant find anything to explain why when calling a SP that does a insert or update depending on if record already exists on a temporal table i get

Data modification failed on system-versioned table 'MYDB.dbo.TemporalExample' because transaction time was earlier than period start time for affected records.

what dose that mean ? It only seems to happen some of the time , i wonder if its because im running multi threaded code and azure sql just doesn't like mutual connections to the same table when its a temporal one ? Im going though entity framework (latest version) but i doubt that's the issue

my sp just this


create PROCEDURE mysp 
    @ID bigint,
    @a FLOAT,
    @b NVARCHAR(10),
    @c  DECIMAL(19, 4)
AS
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED    

    SET NOCOUNT ON    

    BEGIN TRY

        IF EXISTS ( SELECT TOP 1 
                        Id 
                    FROM 
                        my_Temporal_Table WITH (NOLOCK) 
                    WHERE 
                        id = @ID 
                        AND a = @a
                        AND b = @b)
            BEGIN

                UPDATE 
                    my_Temporal_Table
                SET
                    Id = @ID,
                    a = @a,
                    b = @b
                    c = @c
                    DateModified =  GETUTCDATE()
                WHERE 
                    Id = @Id

            END
        ELSE    
            BEGIN

                INSERT INTO 
                    my_Temporal_Table
                        (Id, a, b, c, DateModified)
                VALUES 
                        (@ID, @a, @b, @c , GETUTCDATE())        
            END

    END TRY
    BEGIN CATCH
        DECLARE @ErrorMessage   NVARCHAR(4000),
                @ErrorSeverity  INT,
                @ErrorState     INT

        SELECT 
                @ErrorMessage = ERROR_MESSAGE(),        
                @ErrorSeverity = ERROR_SEVERITY(),
                @ErrorState = ERROR_STATE()

        -- Use RAISERROR inside the CATCH block to return error
        -- information about the original error that caused
        -- execution to jump to the CATCH block.
        RAISERROR (@ErrorMessage, -- Message text.
                   @ErrorSeverity, -- Severity.
                   @ErrorState -- State.
                   )

    END CATCH

Update my temporal table creation script :


    CREATE TABLE [Temporal](
    [TemporalId] [bigint] IDENTITY(1,1) NOT NULL,
    [Payment] [decimal](19, 4) 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 = [Car2].[TemporalHistory] )
    )

can someone explain why i might see this issue, what it means and more importantly how i can fix it ?

thanks

2
why are you updating temporal tablesTheGameiswar
I'm not sure why i wouldn't update it ? It is a normal table after all, im not updating the history table, it is as per MS docs link still just a normal update method that you use when you need to update your data in your tableRawdon Hume
oh ok got it,i thought it was history tableTheGameiswar
Aren't you using partitiong?Deadsheep39
i added the creation script for my temporal table, no im not using partitioning for the update as i dont want to limit the update to a set time period, according to the MS docs i should have to either ?Rawdon Hume

2 Answers

2
votes

So i worked it out ...it seems that temporal tables don't play well with threading logic. I suspect that because im doing multiple concurrent updates to the table in parallel; the history table that's linked is lagging in its update enough that the time linkage causes a failure. Making my code single threaded fixed the issue. It seems odd that temporal table would be subject to what seems to be almost a race condition ? I know its not my code as the same code works fine for other tables. So I guess I will have to stick to single threaded logic until MS fix it

0
votes

This is a hackish workaround and is not ideal in most cases, however, if you want to serialize access to a critical section when dealing with sql server then you can use the built in locking mechanism to grant access to that critical section via SP_GETAPPLOCK, however, you just might be moving a bottleneck to another location depending on the situation.

CREATE PROC MyCriticalWork(@MyParam INT)      
AS
    DECLARE @LockRequestResult INT=0    
    DECLARE @MyTimeoutMiliseconds INT=5000--Wait only five seconds max then timeouit

    BEGIN TRAN

    EXEC @LockRequestResult=SP_GETAPPLOCK 'MyCriticalWork','Exclusive','Transaction',@MyTimeoutMiliseconds
    IF(@LockRequestResult>=0)BEGIN

            /*
            DO YOUR CRITICAL READS AND WRITES HERE
            */

        COMMIT TRAN--Releases the lock
    END ELSE
        ROLLBACK TRAN--Releases the lock