☕ Um Café no Bellacosa Mainframe
Gostou do conteúdo? Ajude a manter o café quente, o COBOL compilando e o mainframe acordado. 😄
☕ Pague um café ao Bellacosa

Translate

Mostrar mensagens com a etiqueta idempotencia. Mostrar todas as mensagens
Mostrar mensagens com a etiqueta idempotencia. Mostrar todas as mensagens

terça-feira, 14 de maio de 2024

🕵️ Ansible sob a Tutela de Beggarman — Tinker, Tailor, Soldier, Spy e o Segredo de Automatizar sem Confiar em Ninguém

 


☕ Um Café no Bellacosa Mainframe

🕵️ Ansible sob a Tutela de Beggarman — Tinker, Tailor, Soldier, Spy e o Segredo de Automatizar sem Confiar em Ninguém

“Na infraestrutura, como na espionagem, o problema raramente é executar uma ordem. O problema é saber quem deu a ordem, com que autoridade, sobre quais máquinas — e o que acontecerá se ela estiver errada.”

Existe uma tentação perigosa quando conhecemos Ansible pela primeira vez.

Ele parece simples.

Um arquivo YAML, alguns servidores, uma conexão, algumas tarefas e pronto: aquilo que antes exigia entrar máquina por máquina começa a acontecer automaticamente.

É sedutor.

É também exatamente o tipo de situação em que George Smiley provavelmente tiraria os óculos, limparia lentamente as lentes e perguntaria:

— Quem mais tem acesso a isso?

Porque automação não elimina risco.

Automação automatiza o risco também.

E é aqui que nosso café entra numa atmosfera muito diferente.

Hoje não teremos laboratórios luminosos, gurus DevOps sorridentes ou nuvens coloridas ligadas por setinhas. Vamos entrar nos corredores cinzentos de Tinker Tailor Soldier Spy, onde ninguém sabe exatamente em quem confiar, informação vale poder e um detalhe aparentemente insignificante pode denunciar toda uma operação.

Sob a tutela de Beggarman, vamos descobrir que aprender Ansible não significa simplesmente aprender YAML.

Significa aprender a controlar uma máquina capaz de executar suas ordens em dezenas, centenas ou milhares de sistemas.

E isso muda tudo.



🕵️ 1. Tinker, Tailor, Soldier, Spy

Tinker Tailor Soldier Spy nasceu como romance de espionagem de John le Carré, publicado em 1974, e acompanha George Smiley na investigação de um agente soviético infiltrado no alto escalão do serviço secreto britânico.

Posteriormente vieram adaptações, incluindo a célebre minissérie da BBC de 1979 com Alec Guinness e o filme de 2011 dirigido por Tomas Alfredson, com Gary Oldman como Smiley.

O universo criado por le Carré está muito distante do glamour de James Bond.

Não espere:

carros esportivos
martínis
gadgets explosivos
vilões em bases subterrâneas

O verdadeiro campo de batalha é composto por:

informações
credenciais
autorizações
documentos
fontes
cadeias de confiança
compartimentalização

Curiosamente, essa também é uma excelente lista de preocupações para quem administra infraestrutura.

E os codinomes usados na investigação são perfeitos para nossa missão:

Tinker
Tailor
Soldier
Poorman
Beggarman

Beggarman é a peça que nos interessa.

Porque nossa infraestrutura também precisa descobrir:

quem pode fazer o quê?



🎯 2. A primeira missão: entender o que é Ansible

Imagine uma empresa com cem servidores.

Sem automação, um administrador poderia precisar conectar-se individualmente:

server01
server02
server03
...
server100

Executar comandos.

Copiar arquivos.

Alterar configurações.

Instalar software.

Reiniciar serviços.

Agora imagine fazer tudo novamente no mês seguinte.

E novamente.

E novamente.

É assim que pequenas diferenças começam a surgir.

No servidor 17 alguém esqueceu uma configuração.

No servidor 32 existe uma versão diferente.

No 48 um parâmetro foi digitado errado.

No 73 alguém resolveu “melhorar” alguma coisa.

Temos então o inimigo silencioso:

configuration drift.

Os servidores que deveriam ser iguais lentamente deixam de ser iguais.

Ansible entra justamente nesse cenário.

Em sua forma conceitual mais simples:

                 CONTROL NODE
                       |
                    ANSIBLE
                       |
                      SSH
                       |
          +------------+------------+
          |            |            |
       SERVER A     SERVER B     SERVER C

O operador escreve o que deseja.

Ansible executa essa automação nos sistemas apropriados.

Mas Beggarman imediatamente faria uma pergunta:

Quais são os sistemas apropriados?

Excelente.

Precisamos de um inventário.



📋 3. Inventory — a lista de agentes da operação

Todo serviço de inteligência precisa saber quem está participando da operação.

Ansible também.

Podemos ter:

[webservers]
web01
web02
web03

[databases]
db01
db02

[production]
prod01
prod02

Isso é o inventory.

Ele diz ao Ansible quais hosts existem e permite organizá-los em grupos.

Pense nele como nosso dossier operacional.

Podemos separar:

DEV
TEST
UAT
PROD

Ou:

WEB
APPLICATION
DATABASE
MONITORING

E aqui aparece uma regra Bellacosa Mainframe extremamente importante:

Nunca trate produção como simplesmente “mais um servidor”.

Produção é uma fronteira administrativa.

A existência de grupos permite limitar o alcance de uma operação.

E isso nos apresenta um conceito que Beggarman conhece muito bem:

blast radius.

Se eu errar, quantas máquinas consigo destruir?

Uma?

Dez?

Mil?

Automação multiplica produtividade.

Consequentemente, também pode multiplicar um erro.


📡 4. Ad hoc — uma mensagem rápida do Circus

Ansible permite comandos rápidos.

Por exemplo:

ansible all -m ping

Isso testa a capacidade do Ansible de alcançar os hosts e executar o módulo correspondente.

Podemos consultar uptime:

ansible web -m command -a "uptime"

São os chamados ad hoc commands.

Fantásticos para:

diagnóstico
verificação
testes
operações pontuais
troubleshooting

Mas existe um problema.

Imagine um agente recebendo uma ordem verbal:

“Vá até lá e faça isso.”

Funcionou.

Três meses depois alguém pergunta:

Quem mandou?

Qual era exatamente o comando?

Por que foi executado?

Podemos repetir?

Talvez ninguém saiba.

É exatamente por isso que operações repetíveis deveriam evoluir para playbooks.


📖 5. Playbook — o plano da operação

Um playbook descreve a automação.

Exemplo:

- name: Configurar servidores web
  hosts: webservers
  become: true

  tasks:

    - name: Instalar nginx
      ansible.builtin.package:
        name: nginx
        state: present

    - name: Iniciar nginx
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true

Observe algo interessante.

Não escrevemos simplesmente:

execute apt
execute systemctl
execute outro comando

Estamos descrevendo estados:

nginx deve existir
nginx deve estar iniciado
nginx deve estar habilitado

É uma diferença aparentemente pequena.

Conceitualmente, é gigantesca.


♻️ 6. Idempotência — Beggarman executa a mesma ordem duas vezes

Chegamos talvez ao conceito mais importante desta conversa.

Imagine executar o playbook.

Primeira vez:

TASK Install nginx
changed

TASK Start nginx
changed

Execute novamente.

TASK Install nginx
ok

TASK Start nginx
ok

Por quê?

Porque o estado desejado já existe.

Esse é o coração da idempotência.

De forma simplificada:

executar novamente uma automação idempotente não deveria produzir mudanças desnecessárias quando o sistema já está no estado desejado.

Para quem vem de COBOL e batch, isso deveria imediatamente acender uma luz vermelha.

Imagine:

JOB
 |
STEP01
 |
STEP02
 |
STEP03
 |
ABEND

Agora são 03:17.

Sim, meu caro leitor.

Você encontrou o easter egg.

☎️ O telefone toca.

Alguém pergunta:

“Pode dar rerun?”

E essa pequena frase pode significar quinze minutos ou cinco horas de investigação.

STEP01 pode executar novamente?

Vai duplicar registros?

STEP02 já atualizou alguma tabela?

Há checkpoint?

Existe restart?

O processamento é recuperável?

É por isso que um mainframer consegue compreender intuitivamente por que idempotência é tão importante.

O problema não é apenas:

“funciona?”

É:

“posso executar novamente com segurança?”

Essa é uma pergunta muito mais madura.


🧱 7. Modules — nossos agentes especializados

Ansible possui módulos para operações específicas:

package
service
copy
file
template
user
uri
debug

O iniciante frequentemente tenta transformar Ansible em um executor remoto de shell.

Algo como:

shell: apt-get install nginx

Pode funcionar.

Mas um módulo apropriado normalmente expressa melhor a intenção:

ansible.builtin.package:
  name: nginx
  state: present

Observe novamente a diferença.

No primeiro:

execute este comando.

No segundo:

garanta este estado.

É a diferença entre procedimento e intenção operacional.


🧠 8. Variables — ninguém deveria esconder valores dentro da missão

Agora imagine que nosso playbook contém:

porta 8080
servidor X
diretório Y
usuário Z

Tudo escrito diretamente no código.

Funciona até precisarmos de:

DEV
TEST
UAT
PROD

Então começamos a copiar arquivos.

Nasce:

playbook-dev.yml
playbook-test.yml
playbook-uat.yml
playbook-prod.yml

Pouco depois:

playbook-prod-final.yml
playbook-prod-final2.yml
playbook-prod-final-CERTO.yml

E finalmente:

playbook-prod-final-CERTO-NAO-APAGAR.yml

😂

Beggarman desapareceria voluntariamente antes de investigar isso.

A solução são variáveis.

app_name: nginx
port: 80
environment: production

Agora lógica e dados começam a se separar.


🔎 9. Facts — interrogando o próprio servidor

Mais interessante ainda: nem toda informação precisa ser fornecida manualmente.

Ansible pode coletar facts.

Podemos descobrir características do host:

sistema operacional
hostname
endereços
interfaces
memória
arquitetura

Isso permite decisões.

Conceitualmente:

          HOST
           |
        FACTS
           |
      +----+----+
      |         |
    RedHat    Debian
      |         |
   tarefa A   tarefa B

Nossa automação deixou de ser uma sequência rígida.

Ela começou a observar o ambiente.


🔀 10. when, loop e register — finalmente aparece lógica

Agora entram condições:

when: feature_enabled == true

Repetições:

loop:
  - nginx
  - git
  - vim

E captura de resultados:

register: disk_output

Para o programador COBOL iniciante, podemos traduzir mentalmente:

when       → IF / EVALUATE
loop       → PERFORM
register   → guardar resultado para decisão posterior

Não são equivalências formais.

São pontes cognitivas.

E pontes são extremamente úteis quando estamos aprendendo algo novo.


📝 11. Templates — uma identidade, muitos disfarces

Beggarman aprovaria Jinja2.

Temos um template:

server {
    listen {{ port }};
    server_name {{ domain }};
}

Para DEV:

port = 8080
domain = dev.example

Para PROD:

port = 443
domain = production.example

Um modelo.

Múltiplas configurações.

Isso elimina dezenas de arquivos quase iguais e reduz divergências acidentais.

É uma ideia antiga e poderosa:

separe aquilo que permanece daquilo que varia.


🔔 12. Handlers — não reinicie o serviço sem motivo

Esta é uma das minhas partes favoritas.

Imagine que alteramos uma configuração do nginx.

Então notificamos:

notify: Restart nginx

O handler executará a ação apropriada quando necessário.

Mentalmente:

CONFIGURAÇÃO
      |
    mudou?
    /    \
  NÃO    SIM
   |      |
  fim   HANDLER
          |
       restart

Isso parece trivial até lembrarmos que estamos falando de produção.

Reiniciar serviços indiscriminadamente é criar indisponibilidade porque sim.

Um bom operador pergunta:

mudou alguma coisa?

Depois:

reload resolve?

Somente depois:

preciso realmente de restart?

Automação madura tenta produzir a menor perturbação necessária.

George Smiley chamaria isso de discrição.

Nós chamamos de engenharia.


🔐 13. become — quem autorizou este agente?

Chegamos à parte que deveria fazer todo mainframer prestar atenção.

become: true

Permite executar tarefas utilizando escalada de privilégio conforme o mecanismo configurado.

Mas o verdadeiro assunto não é a keyword.

É:

autoridade.

Quem executa?

Como autentica?

Qual identidade utiliza?

Que privilégios possui?

Durante quanto tempo?

Sobre quais sistemas?

Para executar quais tarefas?

Quem audita?

De repente, alguém que trabalha com RACF começa a sorrir.

Porque o mundo distribuído descobriu uma velha verdade:

identidade sem controle de autorização é uma bomba esperando um operador distraído.

O princípio correto é least privilege.

Não dê root porque é conveniente.

Dê apenas a autoridade necessária.


🗝️ 14. Ansible Vault — há informações que nem Tinker deve conhecer

Passwords.

API keys.

Tokens.

Credenciais.

Certificados e material sensível.

Nunca deveriam ser alegremente colocados num playbook:

password: supersecret123

e depois:

git push

Parabéns.

Você acabou de publicar a chave da sala secreta.

Ansible Vault permite proteger conteúdo sensível criptografando dados que precisam permanecer armazenados.

Mas existe uma sutileza fundamental:

criptografado em repouso não significa protegido durante toda a execução.

Em algum momento o segredo precisa ser utilizado.

Portanto, gestão de segredos envolve muito mais:

armazenamento
acesso
rotação
auditoria
exposição em logs
CI/CD
permissões
revogação

Em ambientes maiores entram secret managers dedicados.

Porque a pergunta nunca deveria ser apenas:

“Está criptografado?”

Deveria ser:

“Quem consegue descriptografar?”

Beggarman certamente faria essa pergunta.


📦 15. Roles — compartimentalização

E chegamos à página que fecha magistralmente o conjunto.

Um playbook cresce.

E cresce.

E cresce.

Até virar aquele velho programa COBOL de 18 mil linhas que ninguém quer alterar porque:

“Tem uma coisa lá no parágrafo 7420 que ninguém sabe para que serve, mas quando tiraram em 2007 fechou a folha errada.”

😎

Ansible possui Roles.

Podemos organizar:

webserver/
├── tasks/
├── handlers/
├── templates/
├── files/
├── defaults/
├── vars/
└── meta/

Então:

                 PLAYBOOK
                    |
       +------------+------------+
       |            |            |
      WEB          DB         MONITORING
       |            |            |
      ROLE         ROLE          ROLE

Isso cria:

modularidade
reutilização
padronização
manutenção
testabilidade
colaboração

E aqui Beggarman finalmente fecha o dossier.

Porque serviços de inteligência conhecem muito bem outro princípio:

compartimentalização.

Cada componente deve possuir responsabilidade claramente definida.


🏛️ 16. O mainframe estava esperando por esta conversa

Agora retire as marcas comerciais.

Esqueça por alguns segundos Linux, nginx, YAML e cloud.

Observe apenas:

automação
procedimentos
parâmetros
condições
reutilização
segurança
autoridade
restart
controle
auditoria
recuperação
padronização

Meu amigo...

isso soa familiar.

Muito familiar.

O mainframe vive há décadas cercado por conceitos semelhantes:

JCL
PROC
symbolic parameters
RACF
scheduler
REXX
CLIST
SMP/E
libraries
change management
restart/recovery

Não significa que Ansible seja “JCL moderno”.

Essa comparação seria tecnicamente pobre.

Mas ambos pertencem a uma longa tradição da computação corporativa:

transformar operações complexas em procedimentos controlados, reproduzíveis e auditáveis.


🧬 17. O mapa mental do programador COBOL

Para quem está chegando agora, eu usaria esta ponte:

MainframeAnsibleIdeia
JOBPlaybookoperação coordenada
PROCRolereutilização
STEPTaskunidade de trabalho
PARM / symbolicVariableparametrização
condiçãowhendecisão
repetiçãoloopprocessamento repetitivo
RCregistered resultresultado utilizado posteriormente
RACFidentidade / becomeautoridade
bibliotecarepositoryconteúdo controlado
restart/recoveryidempotência + tratamentoexecução segura

Mais uma vez:

mapa mental, não equivalência técnica.

Mas funciona maravilhosamente para quebrar a barreira inicial.


🚨 18. O que as páginas não mostram

Agora Beggarman coloca outra pasta sobre a mesa.

Está escrito:

PRODUCTION.

É aí que a brincadeira muda.

Porque não basta saber:

inventory
playbook
task
module
role
vault

Precisamos começar a perguntar:

Quem aprovou?

Quem executou?

Qual versão?

Quais hosts?

Quantos simultaneamente?

Existe dry run?

Existe backup?

Existe rollback?

Como monitoramos?

Como interrompemos?

O que acontece se 37 funcionarem e o 38 falhar?

Podemos continuar?

Podemos rerodar?

Qual é o blast radius?

Essas são perguntas que transformam um estudante de Ansible em alguém capaz de pensar em produção.


💥 19. A verdadeira missão: failure engineering

Imagine:

100 servidores

001 OK
002 OK
003 OK
...
037 OK

038 FAILED

Silêncio.

Agora temos:

039
040
041
...
100

O que fazemos?

Continuamos?

Paramos?

Rollback?

Investigamos?

E o servidor 38?

A operação nele falhou antes ou depois da alteração crítica?

Existe estado intermediário?

Esta é uma verdade importante:

automação não elimina incidentes; muda a velocidade e a escala com que eles podem acontecer.

Um humano talvez configure dez máquinas incorretamente durante uma manhã.

Uma automação pode configurar mil incorretamente antes do café esfriar.

Portanto:

velocidade sem controle não é maturidade.

É apenas velocidade.


🕵️ 20. A lição de Beggarman

No final de Tinker Tailor Soldier Spy, aquilo que importa não é uma perseguição cinematográfica.

É confiança.

Quem possui informação?

Quem controla acesso?

Quem conhece a operação?

Quem pode alterar alguma coisa?

Quem deixou rastros?

Quem está dizendo a verdade?

Curiosamente, são excelentes perguntas para DevSecOps.

Nossa infraestrutura possui seus próprios agentes:

usuários
service accounts
pipelines
APIs
tokens
SSH keys
playbooks
roles
secret managers
automation controllers

Cada um possui determinada capacidade.

Portanto, segurança de automação começa por uma pergunta profundamente simples:

Em quem estamos confiando?

E continua:

Quanto estamos confiando?

Essa segunda pergunta é ainda melhor.


☕ Epílogo — O espião que entrou no datacenter

Imagine Beggarman caminhando lentamente pelo corredor do datacenter.

Luzes piscando.

Ventiladores.

Milhares de processos trabalhando.

Ele observa um Automation Controller capaz de modificar centenas de servidores.

Pergunta:

— Quem pode executar isto?

O engenheiro responde:

— A equipe de infraestrutura.

Beggarman olha novamente.

— Toda a equipe?

Silêncio.

— Bem... sim.

Ele continua:

— E quem pode alterar os playbooks?

— Os desenvolvedores.

— Todos?

Outro silêncio.

— Onde estão as credenciais?

— No pipeline.

— Quem pode alterar o pipeline?

Agora ninguém mais toca no café.

E é justamente aí que percebemos a grande lição desta história.

Ansible não é simplesmente uma ferramenta para executar comandos mais rapidamente.

É uma plataforma capaz de transformar intenção humana em alteração de infraestrutura em escala.

Isso merece disciplina.

Inventories definem onde.

Playbooks definem o quê.

Variables definem com quais valores.

Conditions definem quando.

Modules definem como.

Handlers controlam as consequências das mudanças.

Roles organizam responsabilidades.

Vault ajuda a proteger segredos.

become participa da definição de autoridade.

Idempotência ajuda a garantir que repetir uma ordem não transforme uma operação rotineira numa investigação às 03:17 da manhã.

E Git, revisão, CI/CD, observabilidade, segregação de funções e auditoria ajudam a responder à pergunta que inevitavelmente aparecerá quando alguma coisa der errado:

“Quem mudou o quê, onde, quando, por quê — e podemos colocar tudo de volta?”

Essa pergunta não nasceu com DevOps.

Não nasceu com cloud.

Muito menos com YAML.

Mainframes já conviviam com versões dela quando muitos dos atuais engenheiros ainda nem haviam nascido.

Talvez seja essa a maior ironia de nossa missão.

Depois de décadas distribuindo processamento, criando milhares de servidores, microsserviços, containers, clouds e APIs, a indústria começou novamente a procurar desesperadamente por:

controle, padronização, segurança, repetibilidade e previsibilidade.

Em algum lugar, dentro de uma sala refrigerada, um velho mainframe continua processando tranquilamente seus JOBs.

Beggarman fecha a pasta.

Olha para nós.

E talvez diga apenas:

“Automatize tudo o que puder. Mas nunca automatize aquilo que você ainda não entende.”

☕ Um Café no Bellacosa Mainframe

Porque em produção, como no Circus, confiança nunca deveria ser implícita.

quarta-feira, 6 de julho de 2022

Viagem ao Fundo do Banco — O Dia em que o Seaview Mergulhou no Db2 para Descobrir Quem Cobrou o Mesmo Pedido Duas Vezes

 

Bellacosa Mainframe analisando querys sob a otica de um QA

☕ Um Café no Bellacosa Mainframe

Viagem ao Fundo do Banco — O Dia em que o Seaview Mergulhou no Db2 para Descobrir Quem Cobrou o Mesmo Pedido Duas Vezes

Ou: como SELECT, JOIN, GROUP BY, HAVING, Window Functions, timestamps, logs, APIs, retries, idempotência, observabilidade e um pouco de desconfiança transformam SQL de “consulta ao banco” em instrumento forense — e por que o Almirante Nelson jamais aceitaria “acho que tem dado duplicado” como relatório de missão

Imagine a cena.

O relógio marca 02:17 da madrugada.

No Centro de Processamento de Dados, alguém acabou de descobrir que clientes podem estar sendo cobrados duas vezes. O telefone do suporte toca. O monitor de produção pisca. O café da máquina já passou da classificação “bebida” para “reagente químico”.

E, estacionado metaforicamente no fundo do oceano de dados, encontra-se o Seaview, o submarino de Viagem ao Fundo do Mar.

O Almirante Nelson olha para o painel.

O Capitão Crane olha para Nelson.

Chip Morton olha para os instrumentos.

E Kowalski, que provavelmente teve a infelicidade de ficar com o plantão daquela madrugada, pergunta:

— Almirante, encontramos doze registros estranhos. Posso abrir o incidente?

Nelson responde:

— Encontrou doze registros ou descobriu o que aconteceu?

Silêncio no submarino.

E é exatamente aqui que começa nossa viagem.

Porque muita gente aprende SQL como se fosse apenas isto:

SELECT *
FROM CLIENTES;

Depois aprende:

WHERE
ORDER BY
GROUP BY
JOIN

faz algumas provas, ganha um certificado e conclui:

“Eu sei SQL.”

Mas saber SQL não é apenas conhecer sua gramática.

Da mesma maneira que conhecer COBOL não significa saber investigar um S0C7, conhecer SELECT não significa saber investigar produção.

A diferença aparece quando existe um incidente real.

E neste artigo vamos descer lentamente até o fundo desse oceano.



1. Primeiro mergulho — o chamado chegou da superfície

Nossa história começa com uma reclamação:

“Alguns clientes parecem ter sido cobrados duas vezes.”

Observe a palavra perigosa:

parecem.

Não temos ainda um bug.

Temos uma suspeita.

O primeiro erro de um investigador inexperiente é tentar provar imediatamente que sua primeira ideia está correta.

O investigador experiente faz algo diferente.

Ele transforma suspeitas em perguntas.

Por exemplo:

Existem realmente duas cobranças?

Quantos clientes foram afetados?

Quando aconteceu?

Os valores são iguais?

Os casos possuem algo em comum?

Houve deploy?

Houve timeout?

Houve retry?

A primeira cobrança já tinha sido processada quando o retry aconteceu?

Esse é o verdadeiro começo da investigação.

Não é SQL.

É pensamento investigativo.

SQL virá depois como sonar.



2. O QA Jr liga o sonar

Nosso primeiro tripulante executa:

SELECT *
FROM PEDIDOS
WHERE STATUS = 'ERROR';

Resultado:

12 linhas

E abre o ticket:

“Existem 12 pedidos com erro no banco.”

Isso está errado?

Não necessariamente.

É uma observação válida.

O problema é que ela praticamente não responde à reclamação original.

Pedidos com erro podem existir por dezenas de motivos:

endereço inválido
estoque indisponível
cartão recusado
timeout
cancelamento
erro de integração
falha de cadastro

E nenhum deles necessariamente implica cobrança duplicada.

O QA Jr encontrou peixes no oceano.

Ainda não encontrou o submarino desaparecido.


3. Um SELECT pode responder perfeitamente à pergunta errada

Esta é uma das lições mais importantes de SQL.

Imagine:

SELECT *
FROM PAGAMENTOS
WHERE STATUS = 'APROVADO';

O banco retorna:

427.816 linhas

Excelente.

Agora sabemos que existem pagamentos aprovados.

E daí?

Nada.

O fato de uma consulta retornar dados não significa que ela produziu evidência útil.

Um bom investigador aprende a perguntar:

Qual pergunta exatamente esta query está respondendo?

Se não conseguir responder em português simples, provavelmente ainda não sabe por que está executando aquela consulta.

Por exemplo:

SELECT *
FROM PAGAMENTOS
WHERE PEDIDO_ID = 9;

Agora temos uma pergunta clara:

“Quais pagamentos pertencem ao pedido 9?”

Resultado imaginário:

PAGAMENTO_ID   PEDIDO_ID   VALOR    STATUS      HORARIO

88341          9           999.99   APROVADO    14:32:08
88352          9           999.99   APROVADO    14:32:11

Interessante.

Agora temos dois pagamentos aprovados para o mesmo pedido.

O Seaview acabou de detectar algo grande no sonar.


4. O QA Pleno pergunta: isso aconteceu só uma vez?

Esta é a primeira grande mudança de mentalidade.

O iniciante normalmente encontra um caso.

O profissional mais experiente procura um padrão.

Em vez de investigar apenas o pedido 9:

SELECT
    PEDIDO_ID,
    COUNT(*) AS QTD
FROM PAGAMENTOS
WHERE STATUS = 'APROVADO'
GROUP BY PEDIDO_ID
HAVING COUNT(*) > 1;

Agora estamos dizendo ao banco:

“Agrupe os pagamentos por pedido e mostre apenas aqueles que possuem mais de uma ocorrência aprovada.”

E aparecem:

PEDIDO_ID    QTD

9            2
17           2
28           2
31           2
...

Doze pedidos.

Agora a história mudou.

Não parece mais um registro corrompido isolado.

Existe um padrão.

Mas cuidado.

O Almirante Nelson ainda não autorizaria tocar o alarme vermelho.


5. COUNT(*) > 1 não significa automaticamente bug

Este detalhe é importantíssimo.

Imagine um pedido de R$ 1.000.

O sistema pode permitir:

Cartão A: R$ 500
Cartão B: R$ 500

Duas transações aprovadas.

Perfeitamente legítimo.

Ou pode existir:

Autorização
Captura

duas operações associadas ao mesmo pedido.

Ou:

pagamento
estorno
nova cobrança

Portanto:

HAVING COUNT(*) > 1

não significa:

“Achei o bug!”

Significa:

“Encontrei algo que merece investigação.”

Esta diferença parece pequena, mas separa SQL mecânico de SQL investigativo.


6. Descendo mais fundo: precisamos conhecer a regra de negócio

Antes de perguntar ao banco se algo está errado, precisamos saber o que significa certo.

Suponha que a regra seja:

Para cada pedido, deve existir no máximo uma captura financeira aprovada no valor total do pedido.

Agora conseguimos escrever uma consulta melhor:

SELECT
    PEDIDO_ID,
    COUNT(*) AS QTD_CAPTURAS,
    SUM(VALOR) AS TOTAL_CAPTURADO
FROM PAGAMENTOS
WHERE STATUS = 'APROVADO'
  AND TIPO = 'CAPTURE'
GROUP BY PEDIDO_ID
HAVING COUNT(*) > 1;

Essa consulta já conhece mais do negócio.

Mas ainda podemos melhorar.


7. O verdadeiro problema talvez não seja duplicidade — seja dinheiro a mais

Imagine:

Pedido = R$ 999,99

Captura 1 = R$ 999,99
Captura 2 = R$ 999,99

Se executarmos:

SUM(VALOR)

obteremos:

R$ 1.999,98

Mas o prejuízo excedente não é R$ 1.999,98.

Uma cobrança era legítima.

O excedente é:

R$ 999,99

Então uma consulta mais inteligente seria:

SELECT
    P.ID AS PEDIDO_ID,
    P.VALOR_TOTAL,
    COUNT(PG.ID) AS QTD_CAPTURAS,
    SUM(PG.VALOR) AS TOTAL_CAPTURADO,
    SUM(PG.VALOR) - P.VALOR_TOTAL AS VALOR_EXCEDENTE
FROM PEDIDOS P
JOIN PAGAMENTOS PG
    ON PG.PEDIDO_ID = P.ID
WHERE PG.STATUS = 'APROVADO'
  AND PG.TIPO = 'CAPTURE'
GROUP BY
    P.ID,
    P.VALOR_TOTAL
HAVING SUM(PG.VALOR) > P.VALOR_TOTAL;

Agora a pergunta mudou novamente.

Não estamos procurando apenas:

“Quem possui dois pagamentos?”

Estamos perguntando:

“Quais pedidos receberam mais dinheiro do que deveriam?”

Isso é muito mais próximo do problema real.


8. Easter egg Bellacosa nº 1 — SQL não é VARIG, mas também exige saber para onde você está indo

Um SELECT sem pergunta bem definida lembra aquele passageiro que chega ao aeroporto e diz:

“Quero viajar.”

Excelente.

Para onde?

Um banco de produção pode conter bilhões de registros.

Você precisa de destino.

tabela
período
cliente
endpoint
status
versão
produto
transação

Caso contrário, seu maravilhoso:

SELECT *

é praticamente um bilhete de volta para o DBA perguntar por que você resolveu fazer turismo pelo tablespace inteiro às 14:30.


9. O relógio do Seaview começa a revelar o assassino

Os doze pedidos duplicados possuem outro detalhe.

Veja:

Pedido 9

14:32:08 primeira cobrança
14:32:11 segunda cobrança

Pedido 17:

14:33:24
14:33:27

Pedido 28:

14:34:01
14:34:04

Estranho.

A diferença é quase sempre três segundos.

Esse tipo de repetição é extremamente valioso.

O investigador agora pergunta:

“Existe alguma configuração do sistema que também seja de três segundos?”

Resposta:

HTTP timeout = 3 segundos

O sonar começou a apitar com vontade.


10. Window Functions — quando SQL ganha uma máquina do tempo

Aqui entram funções que assustam iniciantes:

LAG()
LEAD()
ROW_NUMBER()
RANK()
SUM() OVER()

Mas elas são maravilhosas para investigação.

Imagine:

SELECT
    PEDIDO_ID,
    PAGAMENTO_ID,
    CRIADO_EM,
    LAG(CRIADO_EM) OVER (
        PARTITION BY PEDIDO_ID
        ORDER BY CRIADO_EM
    ) AS PAGAMENTO_ANTERIOR
FROM PAGAMENTOS
WHERE STATUS = 'APROVADO';

LAG() basicamente pergunta:

“Qual era o registro anterior dentro desse grupo?”

Para cada pedido, conseguimos olhar para trás.

Algo quase digno do Seaview atravessando uma fenda temporal.

Podemos encontrar:

Pedido 9
Pagamento 1001     14:32:08
Pagamento 1002     14:32:11

E então calcular o intervalo.

Se todos os incidentes apresentam aproximadamente:

3 segundos

temos uma assinatura.


11. Mas SQL ainda não provou a causa

Aqui devemos ser cuidadosos.

Encontramos:

duas cobranças
+
intervalo de 3 segundos
+
timeout configurado em 3 segundos

Isso é uma excelente hipótese.

Mas ainda não significa:

“Provado: retry sem idempotência.”

Para provar o mecanismo, precisamos subir do fundo do banco e consultar outros instrumentos.

Logs.

Tracing.

Histórico de deploy.

Configuração.

Código.

É aqui que uma investigação realmente Sênior começa a parecer uma sala de controle.


12. Logs são a caixa-preta do submarino

Imagine o log:

14:32:08.104
POST /checkout/payment
pedido_id=9
request_id=ABC123

14:32:11.107
TIMEOUT esperando resposta

14:32:11.110
RETRY POST /checkout/payment
pedido_id=9
request_id=DEF456

E o gateway financeiro registra:

14:32:08.350
payment_authorized
transaction=TX8811

14:32:11.340
payment_authorized
transaction=TX8819

Agora conseguimos reconstruir a sequência.

Aplicação
   |
   | cobrança
   v
Gateway
   |
   | aprova
   |
   v
Dinheiro capturado

Resposta demora

Aplicação
   |
   | timeout
   |
   | retry
   v
Gateway
   |
   | aprova novamente
   v
Segunda cobrança

O ponto decisivo é este:

timeout não significa necessariamente que a operação falhou.

Significa apenas:

“Não recebi a resposta no intervalo esperado.”

A operação remota pode ter funcionado perfeitamente.


13. Bem-vindo ao maravilhoso mundo dos sistemas distribuídos

Aqui está um conceito que programadores COBOL acostumados a processamento transacional precisam entender cada vez mais.

Imagine:

Sistema A envia mensagem para Sistema B.

Sistema B processa.

Sistema B responde.

A resposta se perde.

O Sistema A sabe que enviou.

Mas não sabe se B processou.

Então tenta novamente.

Esse é um problema clássico:

“at least once delivery”

ou seja:

a operação pode chegar mais de uma vez.

Se aquilo que será executado envolve dinheiro, estoque, emissão de nota ou alguma alteração irreversível, precisamos de proteção.


14. Entra em cena a idempotência

Uma operação idempotente pode ser repetida sem provocar efeitos adicionais depois da primeira aplicação.

Por exemplo:

PUT /cliente/123

Conteúdo:

{
  "cidade": "Itatiba"
}

Executar dez vezes continua resultando em:

cidade = Itatiba

Agora compare com:

POST /cobrar

Primeira execução:

cobra R$ 999,99

Segunda execução:

cobra mais R$ 999,99

Não é exatamente o tipo de repetição que queremos.


15. A chave mágica: Idempotency-Key

Uma API pode receber:

Idempotency-Key: PEDIDO-9-PAGAMENTO

Na primeira tentativa:

chave não existe
        ↓
processa
        ↓
grava resultado

No retry:

chave já existe
        ↓
não processa novamente
        ↓
retorna resultado anterior

Podemos ainda reforçar isso no banco:

CREATE UNIQUE INDEX UX_PAYMENT_IDEMPOTENCY
ON PAGAMENTOS (IDEMPOTENCY_KEY);

Agora temos duas camadas de defesa.

Aplicação.

Banco.

Se uma falhar, a outra pode impedir o desastre.


16. O mainframe olha para isso e pergunta: “vocês descobriram integridade?”

Quem trabalha com Db2, CICS e sistemas transacionais antigos provavelmente está sorrindo.

Muitos “novos problemas da computação distribuída” têm parentes bem velhos.

Integridade.

Commit.

Rollback.

Locks.

Uniqueness.

Recovery.

Auditoria.

Exatamente as coisas que o mundo mainframe vem discutindo há décadas.

A tecnologia muda.

A física da transação continua bastante teimosa.


17. O QA Sênior pergunta quando começou

Agora encontramos doze casos.

Hora de perguntar:

“Eles sempre existiram?”

Agrupamos por horário.

Exemplo PostgreSQL:

SELECT
    DATE_TRUNC('hour', CRIADO_EM) AS HORA,
    COUNT(*) AS QTD
FROM PAGAMENTOS
GROUP BY DATE_TRUNC('hour', CRIADO_EM)
ORDER BY HORA;

Resultado imaginário:

10h    normal
11h    normal
12h    normal
13h    normal
14h    12 duplicidades
15h    normal

Consultamos deploy:

14:21 nova versão entrou em produção

Primeira ocorrência:

14:32

O deploy entra imediatamente na lista de suspeitos.

Mas atenção.

Correlação temporal ainda não é causalidade.


18. “Depois do deploy” não significa necessariamente “por causa do deploy”

Talvez às 14:30 tenha acontecido:

degradação da rede
latência no gateway
falha no DNS
mudança de feature flag
fila congestionada
mudança de configuração

Portanto um profissional experiente escreve:

“O problema passou a ocorrer após o deploy.”

Em vez de:

“O deploy causou o problema.”

Até encontrar evidência.

Palavras importam.

Em incidentes, elas podem separar investigação de caça às bruxas.


19. Git entra no submarino

Então alguém compara o código.

E encontra:

for tentativa in range(2):
    try:
        return cobrar(pedido)
    except Timeout:
        continue

A intenção era boa.

Resiliência.

Mas existe uma pergunta fundamental:

“Podemos repetir cobrar(pedido) com segurança?”

Se não existir idempotência, o retry pode transformar uma falha de disponibilidade em problema financeiro.

E aqui aparece algo interessante.

O bug não é:

retry

O bug pode ser:

retry + operação não idempotente

Dois componentes perfeitamente razoáveis isoladamente criaram um problema juntos.


20. Causa raiz frequentemente é uma cadeia, não um ponto

Veja uma RCA mais completa:

Deploy muda uma consulta
        ↓
consulta fica mais lenta
        ↓
resposta ultrapassa 3 segundos
        ↓
timeout
        ↓
retry automático
        ↓
primeira chamada continua processando
        ↓
segunda chamada entra
        ↓
não existe idempotência
        ↓
duas capturas

Agora aparecem várias disciplinas:

SQL
performance
API
HTTP
resiliência
banco
observabilidade
arquitetura
negócio

É por isso que bugs interessantes raramente cabem dentro de uma única tecnologia.


21. Onde EXPLAIN ANALYZE entra na história?

Imagine que antes do deploy uma consulta levasse:

200 ms

Depois:

4.200 ms

E o timeout da aplicação seja:

3.000 ms

Podemos investigar o plano.

Em alguns bancos:

EXPLAIN ANALYZE
SELECT ...

Talvez encontremos:

antes:
Index Scan

depois:
Sequential Scan

ou um JOIN produzindo milhões de linhas intermediárias.

Então o aparente incidente:

“clientes cobrados duas vezes”

pode ter começado com:

“uma query perdeu um índice adequado.”

Bem-vindo à engenharia de produção.


22. Easter egg nº 2 — o monstro marinho talvez fosse um FULL TABLE SCAN

No seriado, vez ou outra surgia alguma coisa enorme pela janela do submarino.

Se Viagem ao Fundo do Mar fosse produzido dentro de um CPD, provavelmente a tripulação gritaria:

— Almirante! Há algo gigantesco vindo em nossa direção!

Nelson olharia o monitor:

TABLESPACE SCAN
2.4 bilhões de linhas

— Fechem as comportas e chamem o DBA.


23. O Sênior mede o raio de explosão

Encontrar o bug ainda não basta.

Precisamos saber:

“Quantos foram afetados?”

Suponha:

12 pedidos duplicados

Parece muito?

Pouco?

Não sabemos.

Se existiram:

4.000 pedidos no período

então:

12 / 4000 = 0,003

ou:

0,3%

Isso é o chamado blast radius.

Mas percentual sozinho também engana.

0,3% de usuários pode representar:

R$ 500

ou:

R$ 5 milhões

Por isso precisamos combinar quantidade e impacto.


24. SQL deve calcular impacto com semântica, não só matemática

Um erro muito comum é executar:

SUM(VALOR)

e chamar aquilo de prejuízo.

Nem sempre.

Imagine:

cobrança legítima    R$ 100
cobrança duplicada   R$ 100

SUM:

R$ 200

Impacto excedente:

R$ 100

Agora adicione:

estorno
chargeback
taxas
parcelamento
moeda
captura parcial

e ficará ainda mais delicado.

Dados financeiros exigem entendimento de negócio.


25. Window Functions são excelentes para reconstruir eventos

Vamos usar ROW_NUMBER():

SELECT
    PEDIDO_ID,
    PAGAMENTO_ID,
    CRIADO_EM,
    ROW_NUMBER() OVER (
        PARTITION BY PEDIDO_ID
        ORDER BY CRIADO_EM
    ) AS ORDEM
FROM PAGAMENTOS
WHERE STATUS = 'APROVADO';

Temos:

Pedido 9     Pagamento A     ordem 1
Pedido 9     Pagamento B     ordem 2

Então podemos procurar:

ORDEM > 1

Ou seja:

pagamentos posteriores ao primeiro.

Isso é bastante útil para auditoria.


26. LAG() também pode revelar estados impossíveis

Imagine uma tabela histórica de pedidos:

PEDIDO_ID
STATUS
DATA_EVENTO

Fluxo esperado:

CRIADO
   ↓
PAGO
   ↓
SEPARADO
   ↓
ENVIADO

Agora aparece:

CANCELADO
   ↓
ENVIADO

Opa.

Ou:

CANCELADO
   ↓
PAGO

Podemos usar funções analíticas para enxergar transições.

O SQL deixa de ser apenas fotografia.

Passa a reconstruir filme.


27. Aqui nasce outro conceito poderoso: invariantes

Uma invariante é algo que deveria permanecer verdadeiro.

Exemplo:

estoque não pode ficar negativo.

Consulta:

SELECT *
FROM ESTOQUE
WHERE QUANTIDADE < 0;

Resultado esperado:

0 linhas

Outra:

pedido cancelado não deve receber captura posterior.

SELECT
    P.ID
FROM PEDIDOS P
JOIN PAGAMENTOS PG
    ON PG.PEDIDO_ID = P.ID
WHERE P.STATUS = 'CANCELADO'
  AND PG.STATUS = 'APROVADO'
  AND PG.CRIADO_EM > P.CANCELADO_EM;

Esperado:

0 linhas

Agora SQL tornou-se uma ferramenta de validação de regras.


28. Procure também aquilo que deveria existir e não existe

LEFT JOIN é maravilhoso para isso.

Pagamentos órfãos:

SELECT PG.*
FROM PAGAMENTOS PG
LEFT JOIN PEDIDOS P
       ON P.ID = PG.PEDIDO_ID
WHERE P.ID IS NULL;

Tradução:

“Mostre pagamentos cujo pedido correspondente não existe.”

Pedidos sem pagamento:

SELECT P.*
FROM PEDIDOS P
LEFT JOIN PAGAMENTOS PG
       ON PG.PEDIDO_ID = P.ID
WHERE PG.ID IS NULL;

Isso muda a pergunta.

Você não procura apenas dados estranhos.

Procura ausências estranhas.


29. Cardinalidade: o recife onde muita query naufraga

Existe um problema traiçoeiro.

Suponha:

Pedido
 ├── 2 pagamentos
 └── 5 itens

Faça:

PEDIDOS
JOIN PAGAMENTOS
JOIN ITENS

Pode resultar em:

2 × 5 = 10 linhas

Então você executa:

SUM(PAGAMENTOS.VALOR)

e multiplica dinheiro sem perceber.

Parabéns.

Você acaba de descobrir uma nova modalidade de inflação.

😄

Por isso é fundamental conhecer cardinalidade:

1:1
1:N
N:N

Antes de agregar.

Muitas vezes devemos agregar previamente:

WITH PAGAMENTOS_AGG AS (
    SELECT
        PEDIDO_ID,
        SUM(VALOR) AS TOTAL
    FROM PAGAMENTOS
    GROUP BY PEDIDO_ID
)
SELECT ...

Isso evita explosões causadas pelos JOINs.


30. Uma query pode estar sintaticamente perfeita e semanticamente errada

Este é um conceito que vale guardar.

O banco pode dizer:

SQLCODE = 0

E você ainda assim estar completamente errado.

O SQL executou.

A pergunta estava errada.

No Db2, Oracle, PostgreSQL ou qualquer outro banco, sucesso de execução não significa sucesso de raciocínio.

Um resultado pode ser:

matematicamente correto

e:

conceitualmente inútil

ao mesmo tempo.


31. O QA Sênior procura características discriminantes

Suponha que os doze casos tenham algo em comum:

endpoint = /checkout/payment

Ótimo.

Vamos verificar outro atributo.

gateway = XPTO

Todos os doze.

Outro:

versão = 4.12.7

Todos.

Outro:

retry_count = 1

Todos.

Agora reduzimos enormemente o espaço de busca.

Começamos:

4.000 pedidos

Depois:

12 duplicados

Depois:

1 endpoint

Depois:

1 gateway

Depois:

1 versão

Depois:

retry

Depois:

sem idempotency key

Esse é um método poderoso.

Você está fechando o cerco.


32. Mas agora vem a pergunta que separa investigação de confirmação de viés

Se todos os bugs têm retry, talvez retry seja a causa.

Certo?

Calma.

Pergunte:

Quantos retries ocorreram sem duplicidade?

Isso é importantíssimo.

Imagine:

40.000 retries
12 duplicidades

Então retry sozinho claramente não explica tudo.

Talvez a condição seja:

retry
+
versão X
+
endpoint Y
+
gateway Z
+
ausência de idempotência

O investigador bom não procura somente evidência que confirma sua hipótese.

Ele procura evidência capaz de destruí-la.

Isso é ciência aplicada.


33. Uma curiosidade: correlação pode nos enganar lindamente

Imagine que todos os doze clientes afetados usam Chrome.

Ticket:

“Bug ocorre no Chrome.”

Então alguém consulta a população normal:

98% dos clientes usam Chrome.

Bem...

O Chrome acabou de deixar de ser um suspeito particularmente interessante.

Esse tipo de comparação é essencial.

Pergunte não apenas:

“Os casos ruins possuem X?”

Pergunte também:

“Quantos casos bons possuem X?”


34. O passo a passo de uma investigação madura

Em vez de decorar cinquenta comandos, pense assim.

Comece confirmando existência.

Depois quantifique.

Depois encontre período.

Depois segmente.

Depois procure diferenças entre casos afetados e normais.

Depois correlacione com deploy, configuração e eventos externos.

Depois use logs e tracing para reconstruir sequência.

Depois verifique código.

Depois tente reproduzir.

Depois calcule impacto.

Por fim, valide a correção e crie defesa contra recorrência.

Este é o ciclo completo.


35. O ticket muda completamente

Um ticket inicial pode dizer:

“Há pedidos duplicados.”

Um melhor:

“Encontramos 12 pedidos com duas capturas aprovadas entre 14:32 e 14:57.”

Um ticket realmente útil:

Entre 14:32 e 14:57 foram identificados 12 pedidos com duas capturas financeiras aprovadas, correspondendo a 0,3% dos pedidos do período e R$ 12.430 de cobrança excedente. Todos foram processados pelo endpoint /checkout/payment após o deploy X. As segundas chamadas ocorreram aproximadamente três segundos após as primeiras, valor compatível com o timeout configurado. Os logs mostram retry após timeout enquanto a primeira captura já havia sido concluída pelo gateway. As requisições não apresentavam mecanismo de idempotência. O comportamento foi reproduzido em ambiente controlado.

Isto já não é:

“Tem algo estranho.”

É praticamente um relatório de investigação.


36. Mas nem o Sênior deveria escrever “provei sozinho com SQL”

Esse é um detalhe importante.

SQL é excelente para provar:

existência
quantidade
padrão
período
impacto

Mas causa raiz frequentemente exige:

logs
traces
configuração
código
deploy
reprodução

Portanto, em vez de:

“SQL provou que retry causou o problema.”

Melhor:

“SQL demonstrou o padrão e o impacto; logs e tracing demonstraram o mecanismo de retry; código e reprodução confirmaram a ausência de idempotência.”

Muito mais preciso.


37. O melhor investigador mantém humildade epistemológica

Palavra grande.

Ideia simples.

Não diga:

“Tenho certeza porque minha query voltou 12 linhas.”

Diga:

“Os dados atualmente sustentam esta hipótese.”

Isso permite que uma nova evidência mude sua conclusão.

Produção adora humilhar excesso de confiança.

Ela tem talento para isso.


38. SQL investigativo também precisa respeitar produção

Aqui entra outra lição essencial.

Você está investigando um incidente.

Não deve criar outro.

Evite executar sem necessidade:

SELECT *
FROM PAGAMENTOS;

sobre bilhões de linhas.

Prefira filtros:

WHERE CRIADO_EM BETWEEN ...

Escolha colunas.

Conheça índices.

Use ambiente read-only quando disponível.

Tenha cuidado com dados pessoais.

E nunca confunda:

investigar

com:

“já que estou aqui vou dar um UPDATE rapidinho...”

É assim que nasce a segunda temporada do incidente.


39. No mainframe, RACF estaria observando

Em um ambiente z/OS bem administrado, acesso não deveria significar:

“Faça qualquer coisa.”

Existem permissões.

Auditoria.

Perfis.

Separação de funções.

O equivalente moderno continua sendo:

least privilege
read-only
audit trail
mascaramento
roles
timeouts
resource limits

Um QA não precisa necessariamente possuir poderes para alterar produção.

Ele precisa de visibilidade suficiente para investigar com segurança.


40. Timestamps — as correntes marítimas invisíveis

Outro assassino de investigações:

timezone

Imagine:

Banco: America/Sao_Paulo
API: UTC
Observabilidade: UTC
Deploy: horário local

Banco:

14:32

Log:

17:32

Alguém conclui:

“Não corresponde.”

Mas é o mesmo instante.

Por isso qualquer investigação temporal deve entender:

timezone
precisão
sincronização de relógio
formato

Caso contrário você pode passar três horas procurando um fantasma.


41. Consistência também importa

Banco de produção está vivo.

Você roda:

SELECT COUNT(*)

Às 14:00:00.

Depois:

SELECT SUM(VALOR)

Às 14:01:00.

Enquanto isso, milhares de transações aconteceram.

Talvez você esteja comparando dois universos diferentes.

Dependendo do banco e da investigação, conceitos como:

READ COMMITTED
REPEATABLE READ
SNAPSHOT
MVCC

podem importar.

Até um SELECT tem contexto transacional.


42. Do SQL para Db2 for z/OS

Agora chegamos ao nosso CPD.

Um programador COBOL pode encontrar algo muito parecido em Db2.

Por exemplo:

SELECT PEDIDO_ID,
       COUNT(*) AS QTD
FROM PAGAMENTO
WHERE STATUS = 'A'
GROUP BY PEDIDO_ID
HAVING COUNT(*) > 1;

Nada conceitualmente diferente.

Você pode executar consultas por ferramentas autorizadas como SPUFI ou outras interfaces corporativas.

Depois correlacionar com:

CICS
Db2
MQ
JES
SMF
logs da aplicação
APIs

Imagine um programa COBOL/CICS emitindo uma solicitação.

Depois MQ entrega.

Depois uma API externa processa.

Depois ocorre timeout.

Depois há nova mensagem.

Você pode ter criado no século XXI um problema cuja solução exige disciplina transacional conhecida desde os dinossauros do CPD.


43. Easter egg nº 3 — o Seaview encontrou um SQLCODE

Alarmes.

Luzes vermelhas.

Kowalski:

— Capitão! SQLCODE negativo!

Crane:

— Qual?

— -811, senhor!

Nelson entra correndo.

— Então a query que esperava uma linha encontrou várias. Finalmente o banco resolveu colaborar com a investigação.

Quem trabalha com Db2 conhece o drama.

Uma aplicação espera unicidade.

O banco entrega duas linhas.

De repente uma regra implícita se materializa em erro.

Às vezes o bug esteve ali durante anos, esperando os dados certos para aparecer.


44. O SQL da investigação pode virar teste

Isto é belíssimo.

Você encontra o incidente com:

SELECT PEDIDO_ID
FROM PAGAMENTOS
WHERE STATUS = 'APROVADO'
GROUP BY PEDIDO_ID
HAVING COUNT(*) > 1;

Depois da correção, essa mesma consulta pode se tornar parte de uma validação.

Esperado:

0 rows

Teste:

envia requisição
simula timeout
executa retry

Nova consulta:

0 rows

Excelente.

O incidente se tornou teste de regressão.


45. Melhor ainda: a query pode virar monitoramento

Agora execute periodicamente uma consulta equivalente.

Se:

resultado = 0

normal.

Se:

resultado > 0

alerta.

O fluxo mudou de:

cliente reclama
        ↓
investigamos

para:

sistema detecta
        ↓
investigamos antes de dezenas de clientes reclamarem

Isso é evolução operacional.


46. Sanity check pós-deploy

Antes do deploy você conhece certos números normais:

duplicidades = 0
erros = 0,02%
aprovação = 98,7%
pedidos órfãos = 0

Depois do deploy executa verificações equivalentes.

Se aparece:

erro = 4,8%

não espere o telefone tocar.

Esta é uma aplicação prática excelente de SQL para QA.

SQL deixa de ser apenas “consulta durante defeito”.

Vira instrumento de observabilidade.


47. Migrations também merecem investigação

Imagine:

ALTER TABLE PEDIDOS
ADD COLUMN STATUS_V2 VARCHAR(20);

QA iniciante verifica:

“A coluna existe.”

Bom começo.

QA mais maduro verifica:

SELECT COUNT(*)
FROM PEDIDOS
WHERE STATUS_V2 IS NULL;

Depois:

SELECT
    STATUS,
    STATUS_V2,
    COUNT(*)
FROM PEDIDOS
GROUP BY STATUS, STATUS_V2;

Agora estamos verificando:

estrutura
+
conteúdo
+
coerência

Uma migration pode funcionar tecnicamente e falhar semanticamente.


48. Jr, Pleno e Sênior não são comandos SQL diferentes

Existe uma caricatura comum:

Jr sabe SELECT

Pleno sabe JOIN

Sênior sabe Window Function

Não.

Um Jr pode dominar Window Functions.

Um Sênior pode solucionar um incidente inteiro usando:

COUNT(*)

A diferença é mais interessante.

O Jr pergunta:

“O que existe?”

O Pleno pergunta:

“Que padrão existe?”

O Sênior pergunta:

“Que mecanismo produziria exatamente esse padrão e como posso provar que minha hipótese está errada?”

Isto é maturidade investigativa.


49. E depois do Sênior existe outra pergunta

Imagine alguém no nível Staff, Principal ou arquitetura.

Ele olha para toda a investigação e pergunta:

“Por que nosso sistema permitiu que isso acontecesse?”

Não:

“Quem escreveu esse retry?”

Mas:

Por que uma API financeira não exigia idempotência?

Por que o banco permitia a duplicidade?

Por que não havia alerta?

Por que não existia teste de timeout?

Por que o gateway não possuía reconciliação?

Por que o deploy não tinha sanity check?

Por que demoramos para detectar?

Agora estamos corrigindo o sistema, não apenas o código.


50. Defesa em profundidade — ou quantas comportas possui o Seaview?

Uma arquitetura madura pode colocar várias barreiras:

cliente
   ↓
idempotency key
   ↓
API
   ↓
controle de duplicidade
   ↓
serviço
   ↓
regra de negócio
   ↓
banco
   ↓
unique constraint
   ↓
gateway
   ↓
reconciliação
   ↓
monitoramento

Se uma proteção falhar, outra ainda pode ajudar.

É a mesma filosofia de compartimentos estanques de um submarino.

Uma seção pode sofrer danos sem afundar a embarcação inteira.


51. O verdadeiro papel do SQL

Depois de toda esta viagem, chegamos ao fundo do oceano.

SQL não é apenas:

SELECT
INSERT
UPDATE
DELETE

Para um investigador, SQL permite perguntar:

Onde a realidade deixou de obedecer ao modelo que construímos?

Uma query de QA pode dizer:

mostre pedidos impossíveis
mostre transições inválidas
mostre valores inconsistentes
mostre relacionamentos quebrados
mostre duplicidades
mostre ausências
mostre anomalias temporais

Isso é extraordinariamente poderoso.


52. Uma regra Bellacosa para levar para casa

Antes de executar uma consulta durante um incidente, pergunte:

“Que hipótese estou tentando testar?”

Depois da consulta:

“Este resultado poderia ter outra explicação?”

Depois de encontrar um padrão:

“Os casos normais também possuem esse padrão?”

Depois de encontrar correlação:

“Consigo demonstrar o mecanismo?”

Depois de encontrar a causa:

“Como impedimos que volte?”

Se você incorporar apenas isso à sua maneira de trabalhar, seu SQL ficará muito melhor sem aprender um único comando novo.


Epílogo — Emergindo do fundo do banco

O Seaview finalmente volta à superfície.

Missão concluída.

No relatório inicial havia:

“Acho que tem cobrança duplicada.”

No relatório final temos:

12 pedidos afetados
0,3% da população do período
R$ 12.430 de cobrança excedente
mesmo endpoint
mesmo intervalo
mesma versão
segunda requisição ~3 segundos depois
timeout configurado em 3 segundos
retry confirmado pelos logs
primeira captura já havia sido concluída
ausência de idempotência
problema reproduzido
correção aplicada
constraint adicionada
teste de regressão criado
monitoramento implementado

Almirante Nelson fecha a pasta.

Crane pergunta:

— Então SQL resolveu o caso?

Nelson olha pela janela do submarino.

— Não. SQL nos mostrou onde procurar e quanto dano havia. Logs mostraram a sequência. Tracing mostrou o caminho. Código explicou o mecanismo. O teste provou que conseguimos reproduzir. E a arquitetura nos ensinou por que aquilo jamais deveria ter sido permitido.

No fundo da sala alguém executa:

SELECT COUNT(*)
FROM PAGAMENTOS_DUPLICADOS;

Resultado:

0

Nelson sorri.

Até que Kowalski aparece correndo:

— Almirante...

— O que foi agora?

— Encontramos um pedido cancelado que foi enviado três horas depois.

Nelson olha para Crane.

Crane olha para o café.

O Seaview começa a mergulhar novamente.

Porque em produção, meu caro programador COBOL, todo fundo do mar possui outro fundo logo abaixo.

E talvez essa seja a verdadeira lição da nossa viagem:

o profissional iniciante usa SQL para encontrar registros; o profissional experiente usa SQL para testar hipóteses; o profissional Sênior combina dados, logs, arquitetura e conhecimento de negócio até transformar suspeita em evidência.

Não é o SELECT complicado que encerra o caso.

É a pergunta certa.

E, como diria qualquer veterano do CPD diante de um incidente às duas da madrugada:

antes de culpar o programa, consulte os dados. Antes de culpar os dados, confira o JOIN. Antes de culpar o JOIN, olhe o timestamp. E antes de executar um UPDATE em produção... chame o DBA.

☕ Um Café no Bellacosa Mainframe

Onde às vezes descemos ao fundo do oceano apenas para descobrir que o monstro marinho era um retry de três segundos sem Idempotency-Key.

Vagner Renato Bellacosa, IBM Champion e especialista em IBM Mainframe
IBM Z17 SYSTEM ONLINE
Sobre o autor

Vagner Renato Bellacosa

IBM Champion 2026 • Especialista em IBM Mainframe

Vagner Renato Bellacosa trabalha com IBM Mainframe desde 1988 , é IBM Champion e especialista em COBOL, CICS, Db2, z/OS e IBM Z. Compartilha experiências profissionais, conhecimento técnico, história da computação e práticas do universo mainframe para aproximar novas gerações das tecnologias que sustentam empresas, bancos e governos ao redor do mundo.

IBM Champion IBM Z COBOL CICS Db2 z/OS Mainframe desde 1988
GitHub LinkedIn
Inicializando conteúdo...