I am working on rewriting a Stored procedure in SQL Server 2008 that is used in POS billing process. First step is to get Bill Number which is an auto-rotate sequence number with added prefix.
As the number of concurrent terminal grows ~10-30, we have faced number of issues like duplicate number, dead lock. Optimization that I am currently working on derived from here, which improved to an extend (no duplicates). But when I tried to test with SQLQueryStress based on this suggestion using 10 iteration and 10 concurrent thread, I see dead lock in few tests and consistent failure getting the prefix from the same table for the first(few iterations)
Procudure has these currently
- SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
- get the prefix from the same table with (nolock) where type=@type and terminalid=@terminalid
- Begin Tran
- UPDATE Numbering with (serializable) SET @nextval=nextnumber, nextnumber = nextnumber + 1
- if @@rowcount = 0 insert
- Commit tran
- output Format prefix + @nextval
Note, This code snippet will not be Ideal as, I was going back and forth restructuring this. This is only a section of code of an if else, where the else has the same block repeating with different where condition(for four different scenarios).
ALTER PROCEDURE [dbo].[GetnextNumberTest] 
        @transType varchar(50), -- 
        @terminalaname varchar(50)='',  --will be empty if it is not terminal wise
        @nextvalue varchar(10) output
AS
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
declare @ResetBasedOn int =0
        ,@Prefix varchar(50)=''
        ,@nextvalint int=0
select  @ResetBasedOn=RestartOn,
        @Prefix=((SELECT Prefix FROM TerminalMaster(nolock) WHERE TerminalName=@terminalaname)+'/'+ prefix)
                        from NumberingSchemeTest (nolock)
    where  TRSType= @transType 
        and (TerminalName=@terminalaname or TerminalName='') and active=1
if(@ResetBasedOn=0) --daily
begin
    Begin Tran
        UPDATE Numberingschemetest with (serializable)      
            SET @nextvalint=nextnumber,
            -- 
                nextnumber = nextnumber + 1
        WHERE TRSType= @transType 
            and (TerminalName=@terminalaname or TerminalName='') 
            and yearlydate =convert(varchar, GETDATE()) 
            and active=1 
       if @@rowcount = 0
       begin
            UPDATE Numberingschemetest with (serializable)  
                set active=0
            where  TRSType= @transType 
                and (TerminalName=@terminalaname or TerminalName='') 
                and yearlydate =convert(varchar, GETDATE() -1) 
                and active=1
            insert into  Numberingschemetest (
                TRSType,  
                Prefix,    
                TPrefix,   
                TerminalName,  
                RestartOn, 
                YearlyDate,  
                StartingFrom, 
                NextNumber, 
                active,PreparedBy
                )
            (select top 1  
                TRSType,  
                Prefix,    
                TPrefix,   
                TerminalName,  
                RestartOn,   
                convert(varchar, GETDATE()),  
                StartingFrom, 
                StartingFrom+1, 
                1  ,@terminalaname
            from Numberingschemetest 
                where TRSType= @transType 
                    and (TerminalName=@terminalaname or TerminalName='') 
                    and active=0 )
        End
    Commit Tran
    if(@nextvalint=0)
        set  @nextvalue=@Prefix+'0001' 
    else
    set @nextvalue =@Prefix+RIGHT('000'+ CAST(@nextvalint AS VARCHAR(50)),Case when LEN(@nextvalint)>4 then LEN(@nextvalint) else 4 end)
End
All these based on several similar question from this site. At this stage, I could only think of two things. One is split the prefix to a separate table and the other is to try a different tool to do the load testing.
I can understand that there will be some trade off, but want to know the best route forward.
