Eu tenho uma função MS Sql que tem alguma lógica para processar o próximo ID de um elemento (concats letras com números com base em alguma lógica de negócios, também com base em um select count()).
De alguma forma, durante os últimos dias (em ambiente de produção), já que o sistema está sendo usado por mais usuários simultâneos, conseguimos algumas duplicatas.
A lógica existente é muito simples:
insert into XXXX (A,B,C,D,E)
select dbo.GiveMeNewID(getdate()), @ParamB, @ParamC, @ParamD, @ParamE
De alguma forma, o GiveMeNewId devolveu o mesmo ID em duas solicitações paralelas.
Esta função é utilizada no momento em diferentes lugares, dentro de um Stored Procedure e também a partir de uma construção SQL "dinâmica" de um "cliente C#".
Eu não quero implementar uma camada "singleton" em algum software intermediário.
Alguma ideia de como resolver este problema? Obrigado.
PS: para testar eu tinha as seguintes linhas dentro da função SQL para poder rodar diferente o insert em diferentes janelas.
/*********************************************************/
declare @dtStart datetime=getutcdate()
while datediff(second,@dtStart,getutcdate()) < 10
begin
set @dtStart = @dtStart
end
/*********************************************************/
No meu cenário de teste, acho que sou capaz de resolver esse problema escrevendo:
Pelo menos agora não consigo replicar entradas duplicadas, mas não tenho certeza se estou criando alguns problemas dos quais não estou ciente.
Se você acha que isso não é uma solução, por favor me avise.
Além de lidar com o acesso exclusivo à própria tabela, como você mencionou, você também pode lidar com o acesso às chamadas de função com
sp_getapplock
.Exemplo sem o uso de transações, travando no nível da sessão.
Ou
Exemplo fictício com transações, travando no nível da transação.
Execute esta consulta em uma sessão (lembre-se de que uma transação permanecerá aberta)
Execute o mesmo em outra sessão
A segunda sessão será bloqueada até que a transação seja concluída.
Não se esqueça de correr
COMMIT TRAN
nas duas sessões.Além disso
Dependendo da lógica da função, se você quiser diminuir as chances de duplicatas ao usar os dados de data e hora como parâmetro, você pode começar a usar o tipo de
datetime2
data eSYSDATETIME()
as chamadas de função para obter intervalos de tempo maiores .