Casos do dia a dia – O dia em que a Replicação quebrou por causa de um ALTER TABLE

Olá pessoal,
Espero que estejam todos bem!
Hoje vou compartilhar com vocês um caso real que atendi em um cliente. Foi uma situação interessante envolvendo replicação que quebrou por causa de um ALTER TABLE aparentemente simples. Vou contar exatamente como foi e como resolvemos o problema.
Era uma linda manhã de uma terça-feira ensolarada quando começaram a chegar os alertas automáticos de falha na replicação. Você conhece essa sensação né? Aquele friozinho na barriga quando vê que algo crítico parou de funcionar.

Imediatamente solicitamos acesso ao ambiente junto ao cliente para investigar o que havia acontecido. Primeira coisa que fiz foi partir direto para os logs da replicação para entender qual era o problema.
Nos logs do Distribution Agent, encontramos esta mensagem de erro:

Erro :
Command attempted:
if @@trancount > 0 rollback tran
(Transaction sequence number: 0x0009FC52000340E8000100000000, Command ID: 1)
Error messages:
The index ‘NonClusteredIndex-20160701-165634’ is dependent on column ‘data’.
(Source: MSSQLServer, Error number: 5074)
The index ‘NonClusteredIndex-20160701-165634’ is dependent on column ‘data’.
(Source: MSSQLServer, Error number: 5074)
ALTER TABLE ALTER COLUMN data failed because one or more objects access this column.
(Source: MSSQLServer, Error number: 4922)
Olhando essa mensagem, já dava pra ter uma ideia do que havia acontecido. Alguém havia feito um ALTER TABLE em produção tentando alterar uma coluna chamada “data”, mas já existia um índice dependente dessa coluna no assinante.
Conversando com a equipe de desenvolvimento do cliente, descobrimos que um usuário havia executado um ALTER TABLE em produção, alterando o tipo da coluna “data” de uma tabela de DATETIME para DATE.
Até aí, tudo bem. O comando funcionou perfeitamente no servidor Publisher. O problema surgiu quando a replicação tentou aplicar o mesmo comando no Subscriber, onde já existia um índice dependente dessa coluna associado a tipagem de dados antiga (DATETIME).
Basicamente foi isso que aconteceu:
- No Publisher: ALTER TABLE funcionou sem problemas
- No Subscriber: Comando falhou porque existia um índice na coluna
- Replicação parou porque não conseguiu aplicar o comando
Neste caso, estava mais fácil, pois, conversando com o cliente, ele me informou a tabela que havia sido alterada, mas se você não tiver essa mesma sorte, ai vai algumas dicas para encontrar a tabela de origem do problema.
Para identificar exatamente qual tabela estava causando o problema, podemos fazer uma consulta nas tabelas de sistema:
1 2 3 4 |
-- Primeiro, encontramos o índice pelo nome SELECT object_id, name FROM sys.indexes WHERE name = 'NonClusteredIndex-20160701-165634' |
1 2 3 4 |
-- Depois, identificamos a tabela usando o object_id SELECT name, schema_name(schema_id) as schema_name FROM sys.tables WHERE object_id = [object_id_retornado_na_consulta_anterior] |
A solução no cliente foi bem direta, removemos o índice problemático e depois o recriamos com o novo tipo de dados da coluna.
1 2 3 |
-- Primeiro, dropamos o índice DROP INDEX [NonClusteredIndex-20160701-165634] ON [dbo].[tabela_problematica] GO |
1 2 3 4 5 6 7 8 9 10 |
-- Depois, recriamos o índice CREATE NONCLUSTERED INDEX [NonClusteredIndex-20160701-165634] ON [dbo].[tabela_problematica] ( [col1] ASC, [data] DESC ) INCLUDE ([col3]) WITH (DATA_COMPRESSION = PAGE) ON [PRIMARY] GO |
Após executar esses comandos, a replicação voltou a funcionar normalmente.

Esse caso me ensinou algumas coisas importantes:
Primeiro, sempre que for fazer ALTER TABLE em colunas que participam de replicação, é fundamental verificar se existem objetos dependentes nos Subscribers. Uma consulta simples pode evitar muita dor de cabeça:
1 2 3 4 5 6 7 |
-- Verificar índices dependentes de uma coluna SELECT i.name as index_name, t.name as table_name FROM sys.indexes i INNER JOIN sys.tables t ON i.object_id = t.object_id INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id WHERE c.name = 'nome_da_coluna' |
Segundo, em ambientes replicados, mudanças estruturais precisam ser planejadas considerando todos os servidores envolvidos. O ideal seria ter feito o ALTER TABLE primeiro no Subscriber, depois no Publisher.
Terceiro, sempre manter backups dos scripts de criação de índices. Isso facilita muito na hora de recriar objetos quando necessário.
Conclusão
Esse foi mais um daqueles casos que mostra como a replicação pode ser sensível a mudanças estruturais. O importante é não entrar em pânico quando essas situações acontecem e seguir uma metodologia de investigação.
A replicação transacional é robusta, mas exige cuidado especial com operações DDL . Sempre que possível, planeje essas mudanças considerando toda a topologia de replicação.
E você? Já passou por alguma situação similar? Compartilhe nos comentários sua experiência com problemas de replicação!
Até a próxima pessoal!
Gustavo Larocca
Consultor SQL Server