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.