Cenário:
Nós escrevemos software de banco on-line e, por enquanto (devido a decisões de design herdadas), estamos usando o Quartz 2.2 agora como um sistema de filas para executar algumas operações de longo prazo. [Vamos deixar de lado a discussão sobre se é a ferramenta certa, funcionou bem por muitos anos até atualizarmos para 2.2]
Parte desse quartz.net possui as seguintes tabelas (relacionadas) para esquema: https://gist.github.com/jcolebrand/8695603
Portanto, o processo é inserir registros nesta tabela, digamos, 80 mil registros. (Tenho três replicações do quartz.net atendendo a três configurações diferentes, uma com 80k de registros, outra com 50k, outra com 280k, então varia). Nós os inserimos de alguma outra tabela que é a tabela de registro, então reconstruir esta tabela não é uma perda terrível. O problema é descrito abaixo.
Eu tenho uma ferramenta que pode agendar essas tarefas em massa (a maioria das quais é única e acontecerá de duas semanas a vinte anos no futuro, novamente, trata-se do banco de dados e não das opções de arquitetura) e posso depurar a etapa completamente, veja as linhas serem inseridas na tabela sem preocupação. Posso monitorar a tabela e ver se eles são inseridos. Eu então volto 30 minutos depois (bem, esta parte varia. Não é determinístico quando eles desaparecem) e verifico se os registros estão faltando.
De 80k em uma instância, estou perdendo aproximadamente 2.700 registros. E quando executo minha ferramenta, vejo todos eles sincronizados e, depois de algum tempo, eles evaporaram novamente.
Aqui estão as coisas que eu tentei:
- verificando o relatório de todas as transações no SSMS
- verificando o relatório de todas as transações de bloqueio no SSMS
- deixar meu aplicativo de agendamento aberto por muito tempo (caso, por alguma mágica, houvesse uma transação aberta e não confirmada)
- reiniciando o aplicativo de serviço do Windows Quartz.net mantendo o banco de dados
- inserindo registros com o serviço windows quartz.net desabilitado
Coisas que não experimentei:
- reiniciando o servidor SQL
- minhas consultas não usam "with(nolock)" [ouvi dizer que isso é ruim para o dia-a-dia]
Coisas que acho que devo fazer e não sei como:
- execute o SQL Profiler na instância e monitore as instruções "excluir"
- mágica para determinar se há uma transação não confirmada
- sacrificar unicórnios
- telefone para um amigo
- duplo diário
@@versão:
Microsoft SQL Server 2008 R2 (SP2) - 10.50.4000.0 (X64)
28 de junho de 2012 08:36:30
Copyright (c) Microsoft Corporation
Standard Edition (64 bits) no Windows NT 6.1 (Build 7601: Service Pack 1) (Hypervisor )
EDITAR 2014-01-30
É imperativo, por razões internas, que eu possa demonstrar precisamente por que os registros estão desaparecendo, incluindo, entre outros:
- exclusão por um usuário
- transação não confirmada
- deleção pelo próprio Quartz
- alguns outros fenômenos inexplicáveis
- efeito Borboleta
- efeito de gatilho
- unicórnios
Sempre que possível, preciso da instrução específica envolvida na exclusão.
TL;DR
Então, basicamente, o TL;DR é: os registros são inseridos. Por algum período de tempo de até pelo menos 30 minutos, o registro é definitivamente exibido em consultas na tabela. Depois de algum tempo, os registros não são mais exibidos em consultas na tabela.
O que da? O que estou deixando passar? Como você perfilaria isso EM PRODUÇÃO para ver o que está acontecendo aqui?
Se você não espera que nenhuma linha seja excluída dessa tabela, a maneira mais simples e menos impactante de capturar alguém executando exclusões seria um gatilho de exclusão. Você pode usar um gatilho INSTEAD OF se quiser registrar e impedir, ou um gatilho posterior se quiser apenas registrar. Primeiro, uma tabela de registro:
Agora, um gatilho INSTEAD OF INSERT, poderia registrar os DELETEs, mas não realizar:
Mude
INSTEAD OF
paraFOR
e agora você tem um gatilho AFTER que ainda permitirá que a exclusão aconteça, mas registrará todos eles de qualquer maneira. Isso pode ser melhor se você tiver algumas exclusões legais acontecendo, mas quiser determinar quem está excluindo as coisas erradas.A beleza é que, em ambos os casos, isso só tem algum impacto se e quando uma exclusão for realmente emitida na tabela.
Eu depuraria isso usando um rastreamento do lado do servidor . Essencialmente, o motivo pelo qual o Profiler é problemático na produção é porque ele é um aplicativo cliente. Se você tentar rastrear muito e o SQL Server estiver tentando gerenciar essa comunicação entre o servidor e o cliente, isso poderá atrapalhar as coisas. Então, em vez disso, você pode criar o script do rastreamento como um script T-SQL e executá-lo no próprio servidor. Ele irá capturar as informações para um arquivo de rastreamento, então você pode levar esse arquivo de rastreamento para outro lugar e revisá-lo.
Para fazer o script do rastreamento, abra o Profiler e selecione seus eventos. Como você está preocupado com a possibilidade de as declarações não serem confirmadas/concluídas, eu recomendaria
Colunas relevantes incluídas. Filtre conforme necessário (como nome do banco de dados, talvez duração)
Em seguida, clique em 'Executar', mas pare imediatamente. Neste ponto, vá para a barra de ferramentas e selecione: Arquivo->Exportar->Definição de rastreamento de script->Para SQL Server... Isso solicitará que você salve um script em algum lugar. Depois de salvá-lo, abra-o.
Antes de executá-lo, especifique um nome de arquivo e um caminho (substitua InsertFileNameHere). Em seguida, execute para iniciar o rastreamento. O rastreamento continuará a ser executado até que você o interrompa. Para pará-lo, certifique-se de saber o ID de rastreamento, que é retornado quando você executa o script. Se você não consegue se lembrar, veja
sys.traces
. Depois de obter a ID de rastreamento, você pode interrompê-lo usando sp_settracestatus para definir o status como 0 para interrompê-lo. Uma vez definido como 0, você pode defini-lo como 2 para excluí-lo.Depois de pará-lo, você deve ter um arquivo de rastreamento para revisar e identificar o que está acontecendo com seus processos. O arquivo de rastreio pode ser visualizado por meio do aplicativo Profiler.