Para um projeto .Net maduro que apoio, estamos atualmente explorando opções para remover o Entity Framework Core do projeto, mantendo algumas das funcionalidades do EF nas quais confiamos.
No topo desta lista está a nossa capacidade de acompanhar exatamente quais valores foram alterados como resultado de uma solicitação de atualização que um sistema externo nos envia - não temos controle sobre esse sistema e não é incomum recebermos atualizações solicitações que contêm dados idênticos várias vezes.
Atualmente, usamos o rastreador de alterações da EF para verificar se a atualização que estamos processando realmente faz alguma alteração em um conjunto de colunas específicas, para que possamos saber se devemos ou não informar nossos usuários sobre essas alterações.
Analisar como poderíamos conseguir isso sem ter o EF e sua sobrecarga envolvidos me levou à cláusula OUTPUT do SQL Server , que teoricamente nos permitiria realizar uma atualização em uma tabela e retornar uma visão do estado anterior e atualizado das colunas-chave que use para gatilhos de notificações.
Até aí tudo bem, porém, há um aviso na seção Paralelismo desse documento que diz que "[uma] OUTPUT
cláusula que retorna resultados ao cliente, ou variável de tabela, sempre usará um plano serial". Receio que meu conhecimento de SQL não seja forte o suficiente neste momento para saber se isso é um problema ou não.
Isso deveria me preocupar do ponto de vista de desempenho ou confiabilidade? Cada atualização que realizamos é codificada em um conjunto de colunas que formam (em quase todos os casos) uma chave composta exclusiva, portanto, mesmo que a parte da consulta da atualização seja executada em série, haveria um impacto perceptível?
Tomemos como exemplo o seguinte esquema:
CREATE TABLE user_profile
(
[id] INT IDENTITY (1,1) NOT NULL PRIMARY KEY
[username] NVARCHAR(100) NOT NULL,
[tenancy] NVARCHAR(20) NOT NULL,
[city] NVARCHAR(50) NULL,
[state] NVARCHAR(50) NULL,
[first_name] NVARCHAR(50) NULL,
[last_name] NVARCHAR(50) NULL,
[external_system_access_key] NVARCHAR(200) NULL,
CONSTRAINT [UX_user] UNIQUE ([username], [tenancy])
)
Neste exemplo, um usuário gerencia seus próprios valores city
, state
e , mas um sistema externo gerencia por meio de uma solicitação ao nosso serviço first_name
, comolast_name
external_system_access_key
POST /{tenancyId}/user/{username}/profile/external
{
"accessKey": "1224567890"
}
Se recebermos a mesma atualização várias vezes sem que o valor seja accessKey
alterado, queremos saber se o valor difere antes e depois da execução da atualização, para sabermos se devemos ou não informar ao usuário que a chave foi alterada. Cada solicitação resultaria em uma atualização como esta:
DECLARE @accessKey NVARCHAR(200) = '1234567890';
DECLARE @username NVARCHAR(100) = 'username';
DECLARE @tenancy NVARCHAR(20) = 'tenancy';
UPDATE [user_profile]
SET [remote_system_access_key] = @accessKey
OUTPUT INSERTED.id, DELETED.[remote_system_access_key] AS OLD_remote_system_access_key, INSERTED.[remote_system_access_key] AS NEW_remote_system_access_key
WHERE
[username] = @username AND [tenancy] = @tenancy;
No caso em que a solicitação nos fornecesse um novo valor para essa coluna, haveria valores diferentes para cada uma das colunas de saída OLD_
e NEW_
, se for uma solicitação que tivemos anteriormente, eles corresponderão, permitindo-nos avaliar quaisquer alterações após a solicitação. a inserção está concluída.
Mas a documentação do SQL Server diz que isso sempre resultará em um plano de execução serial. O que preciso saber é: isso é um problema? Qualquer assistência que eu pudesse obter para entender isso e seus impactos potenciais seria muito apreciada.
Geralmente, cada atualização atingirá apenas uma linha por vez, pois sua cláusula where usa uma chave composta exclusiva. Estamos apenas tentando evitar o que o EF faz e exigir a consulta dos dados primeiro - em vez disso, como o SQL Server já fornece um mecanismo para saber qual era o estado antes e depois da atualização, gostaríamos de recuperar esses dados depois se isso não causar problemas de desempenho.
O exemplo que usei acima é bastante simplista comparado aos nossos casos reais, onde atualizaremos um grande número de colunas em cada instrução - todas em uma tabela, mas múltiplas colunas em cada uma. Seria proibitivamente complexo tentar cobrir todas as permutações possíveis do que poderia ser atualizado em cada solicitação.
Quase certamente não neste caso.
Pelo que vi (das perguntas anteriores do EF), o EF não tem vergonha de adicionar a
OUTPUT
cláusula às consultas de qualquer maneira, então você pode descobrir que ela já foi executada com isso se você rastrear as consultas.Independentemente disso, é improvável que uma atualização singleton onde a linha possa ser encontrada com uma busca
UX_user
se beneficie do paralelismo ou tenha um custo de plano próximo ao limite de custo para paralelismo onde um plano paralelo seria considerado.Talvez fosse possível projetar tal caso fornecendo perversamente chaves estrangeiras para validação sem nenhuma indexação útil (ou seja, onde outra tabela faz referência à coluna que você está atualizando), mas você está em condições de examinar seus planos de execução e ver se isso é o caso.
Se você quiser aplicar isso onde houver uma possibilidade legítima de que o plano seja paralelo, a solução alternativa será bastante simples. Você pode criar uma
#temp
tabela eOUTPUT INTO
aquela e depoisSELECT
a partir da#temp
tabela.