Estou observando uma estranha diferença entre os planos de consulta que estou recebendo na minha máquina local e no Azure SQL. Estou tentando implementar segurança em nível de linha, onde leio o identificador do usuário de SESSION_CONTEXT e depois no TVF verifico se o usuário tem acesso.
Na minha máquina local - edição SQL Server 2019 Developer, DB no nível de compatibilidade 150, os planos de consulta são os esperados. Mas quando o executo no Azure DB, que também tem nível de compatibilidade de 150, só recebo planos de consulta não paralelos com NonParallelPlanReason="NonParallelizableIntrinsicFunction"
. Eu tentei um banco de dados Hyperscale, bem como um banco de dados que está no Elastic Pool e o resultado é o mesmo em ambos os bancos de dados.
Você pode reproduzir isso com o seguinte código:
CREATE TABLE Users (
UserIdentifier nvarchar(100) PRIMARY KEY CLUSTERED
)
INSERT INTO Users (UserIdentifier) VALUES ('MyUserIdentifier')
CREATE TABLE TableWithRLS (
Id int NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
DataColumn nvarchar(100) NULL
)
INSERT INTO TableWithRLS (DataColumn)
SELECT TOP 10000000 A.[name] FROM sys.all_columns AS A
CROSS JOIN sys.all_columns AS B
CROSS JOIN sys.all_columns AS C
CREATE OR ALTER FUNCTION CheckAccess (@userIdentifier varchar(100))
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
SELECT TOP 1 1 AS HasAccess FROM dbo.Users WHERE UserIdentifier = @userIdentifier
EXEC sp_set_session_context N'UserIdentifier', N'MyUserIdentifier', 1
-- This query gets always non-parallel query plan on Azure
SELECT MAX(DataColumn) FROM TableWithRLS AS X
CROSS APPLY CheckAccess(CAST(SESSION_CONTEXT(N'UserIdentifier') AS nvarchar(100)))
Quando seleciono o valor do contexto da sessão em uma variável primeiro, ele gera um plano de consulta paralelizável mesmo no Azure.
DECLARE @userIdentifier AS nvarchar(100) = CAST(SESSION_CONTEXT(N'UserIdentifier') AS nvarchar(100))
SELECT MAX(DataColumn) FROM TableWithRLS AS X
CROSS APPLY CheckAccess(@userIdentifier)
Infelizmente não posso fazer isso (ou pelo menos não sei como fazer isso) porque preciso de um TVF inline.
Plano de consulta do Azure: https://www.brentozar.com/pastetheplan/?id=ByxZm45e9
Plano de consulta do local: https://www.brentozar.com/pastetheplan/?id=BylHXV9lc
Existe alguma diferença na implementação de SESSION_CONTEXT no Azure que possa estar causando isso? Ou alguém tem alguma outra idéia do que poderia ser o problema?
Sim. Embora o Banco de Dados SQL do Azure e o SQL Server sejam criados a partir de uma base de código comum, não é incomum que um esteja à frente do outro. Apesar do marketing da Microsoft nos últimos anos, nem sempre é a edição do Azure que está à frente.
Não permitir planos paralelos quando
SESSION_CONTEXT
é usado é um sinalizador de recurso ( DisableSessionContextParallelPlan ) que está atualmente habilitado no Azure, sem nenhuma maneira de desativá-lo. Ele pode ser habilitado no SQL Server com o sinalizador de rastreamento não documentado 11042 no nível de consulta, sessão, global e de inicialização.Usando um banco de dados de demonstração do Stack Overflow (qualquer consulta paralela serve):
A consulta é paralela sem o sinalizador de rastreamento no SQL Server.
Essas opções geralmente são adicionadas aos recursos de visualização, como resposta a condições de bug raras ou onde existe um risco de segurança.
Uma correção de bug para resultados incorretos ao usar
SESSION_CONTEXT
em planos paralelos foi lançada no SQL Server 2019 CU14.Ocorreram problemas com a correção, relatados na documentação do CU16 :
Do XML do plano AzureDB:
NonParallelPlanReason="NonParallelizableIntrinsicFunction"
Isso ocorre porque o contexto intrínseco está sendo usado e o banco de dados SQL do Azure configurado especificamente para desabilitar o paralelismo nesses intrínsecos (mas esse não é o caso da caixa, a menos que itens específicos estejam habilitados). Existem maneiras de fazer o produto da caixa (on-prem) funcionar da mesma forma, mas acredito que você gostaria do contrário.
Essencialmente, é assim que o banco de dados SQL do Azure está configurado atualmente e não sei se há maneiras de contornar isso em sua assinatura (geralmente não trabalho com o banco de dados SQL do Azure). Não consegui encontrar nada na documentação atual para descrever esse comportamento.