Em outro aplicativo, fui atingido por um design ruim: vários threads executam um EnsureDatabaseSchemaExists()
método simultaneamente, que se parece basicamente com isso:
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'MyTable') AND type = N'U') BEGIN
CREATE TABLE MyTable ( ... );
END
No entanto, mesmo se executado em uma transação SERIALIZABLE, esse código não parece ser thread-safe (ou seja, o código paralelo tenta criar a tabela várias vezes). Existe alguma chance de forçar a instrução SELECT a adquirir um bloqueio que impeça outro thread de fazer a mesma instrução SELECT?
Existe um padrão melhor para métodos EnsureSchemaExists() multi-threaded?
Sua melhor aposta é usar uma transação de conteúdo explícito e adquirir um bloqueio exclusivo personalizado para proteger toda a operação (
SELECT
eCREATE TABLE
) usando sp_getapplock . Os objetos do sistema não respeitam as solicitações de nível de isolamento e usam bloqueios da mesma forma que as tabelas de usuário, por padrão.A condição de corrida no código original é que vários threads podem concluir que a tabela não existe antes que qualquer thread chegue até a
CREATE TABLE
instrução.Minha recomendação seria tentar / pegar o melhor esforço. Lide com o caso duplicado explicitamente, conforme apropriado, por exemplo. ignore isto...
A verdadeira questão: por que o DDL está sendo executado sob demanda, a partir de vários xacts? Normalmente atualização e migração são um assunto sério, tratado em janelas de tempo dedicadas... Você não quer que sua migração (code-first?) -operações de dados...)