 |
| Bellacosa Mainframe e as mensagens em ia |
☕ UM CAFÉ NO BELLACOSA MAINFRAME
📨 ALAN TURING E O MQ DOS AGENTES — A MENSAGEM CHEGOU DUAS VEZES
IBM MQ, agentes de IA, mensageria, filas, producers, consumers, delivery semantics, acknowledgements, commit, rollback, duplicate delivery, idempotência, ordering, correlation ID, message ID, poison messages, backout queues, dead-letter queues, retry, persistence, request/reply, COBOL, CICS, Db2 — e o dia em que Alan Turing descobriu que uma mensagem pode chegar duas vezes sem que ninguém tenha cometido o mesmo erro duas vezes.
🎬 PRÓLOGO — MAS EU JÁ FIZ ISSO!
03:17 da madrugada.
O telefone toca.
Nunca existe boa notícia às 03:17.
O operador atende.
— Produção.
Silêncio.
Depois:
— Temos dois pagamentos iguais.
O operador abre o terminal.
PAYMENT-ID.... P88271
VALUE......... 850.00
STATUS........ PROCESSED
Outra linha:
PAYMENT-ID.... P88271
VALUE......... 850.00
STATUS........ PROCESSED
O pequeno agente aparece na tela.
— Não fui eu!
Alan Turing entra no CPD carregando uma caneca de café.
— Interessante defesa.
— Professor, eu processei a mensagem uma única vez!
O operador consulta os logs.
03:16:51 GET MESSAGE
03:16:52 PROCESS PAYMENT
03:16:52 DB2 COMMIT OK
03:16:52 ...
03:16:53 AGENT RESTART
03:16:54 GET MESSAGE
Turing aponta para o intervalo.
— O que aconteceu entre o commit e a confirmação do consumo da mensagem?
Silêncio.
O administrador MQ olha para o administrador Db2.
O administrador Db2 olha para o programador COBOL.
O programador COBOL olha para o robô.
O robô olha para a porta.
Turing sorri.
— Acho que encontramos nosso próximo capítulo.
No quadro escreve:
PROCESSAR UMA MENSAGEM E CONFIRMAR QUE ELA FOI PROCESSADA NÃO SÃO NECESSARIAMENTE O MESMO EVENTO.
Bem-vindo ao MQ dos Agentes.
📨 CAPÍTULO 1 — MENSAGERIA COMEÇA COM UMA IDEIA MUITO SIMPLES
Imagine dois programas.
PROGRAMA-A
precisa pedir alguma coisa para:
PROGRAMA-B
Uma possibilidade seria A chamar B diretamente.
A ───────────────► B
Mas isso cria algumas dependências.
B precisa estar disponível.
A talvez precise esperar.
A precisa saber onde B está.
Se B estiver sobrecarregado, A sofre junto.
Mensageria introduz um intermediário.
A
│
▼
QUEUE
│
▼
B
A produz uma mensagem.
B consome depois.
Temos dois papéis clássicos:
PRODUCER
e:
CONSUMER.
O producer produz.
O consumer consome.
No meio:
QUEUE.
Simples.
Até produção começar.
📬 CAPÍTULO 2 — UMA FILA É UMA CAIXA POSTAL
Para o programador COBOL iniciante, imagine uma caixa postal.
O remetente coloca:
PEDIDO 12345
na caixa.
Ele não precisa esperar o funcionário responsável aparecer.
Depois o consumidor pega a mensagem.
PUT
↓
QUEUE
↓
GET
No universo IBM MQ podemos imaginar conceitualmente operações como:
MQPUT
e:
MQGET.
O produtor coloca.
O consumidor recupera.
Isso permite:
desacoplamento
processamento assíncrono
absorção de picos
integração entre plataformas
resiliência
Nosso agente pode agora receber trabalho através de mensagens.
🤖 CAPÍTULO 3 — O AGENTE VIROU CONSUMIDOR
Imagine:
CUSTOMER.EVENTS
recebendo:
CUSTOMER_UPDATED
Nosso agente escuta essa fila.
Chega:
CUSTOMER_ID = 12345
EVENT = ADDRESS_CHANGED
O agente:
1. recebe
2. interpreta
3. consulta Db2
4. aplica regras
5. talvez chama ferramentas
6. produz resultado
Perfeito.
Mas aparece a primeira pergunta importante:
Quando podemos considerar essa mensagem realmente processada?
Quando o agente começou?
Não.
Quando terminou o raciocínio?
Talvez não.
Quando atualizou o Db2?
Ainda precisamos pensar.
Quando confirmou o consumo?
Agora estamos chegando perto do problema.
✉️ CAPÍTULO 4 — TODA MENSAGEM PRECISA DE IDENTIDADE
No JES2 dos Agentes aprendemos:
RUN-ID.
Agora precisamos identificar mensagens.
Conceitualmente:
MESSAGE-ID
Pode existir também:
CORRELATION-ID.
Imagine:
MESSAGE-ID..... M000881
CORRELATION-ID. C000042
RUN-ID......... A004821
Esses identificadores respondem perguntas diferentes.
MESSAGE-ID
Qual mensagem é esta?
CORRELATION-ID
A qual conversa, fluxo ou solicitação ela pertence?
RUN-ID
Qual execução do agente está processando?
Isso será ouro para observabilidade.
🔗 CAPÍTULO 5 — CORRELATION ID: ENCONTRE A CONVERSA NO MEIO DO CAOS
Imagine:
REQUEST
MESSAGE-ID M100
CORREL-ID C500
O serviço responde:
RESPONSE
MESSAGE-ID M101
CORREL-ID C500
São mensagens diferentes.
Mas pertencem à mesma conversa.
Em sistemas distribuídos isso é extremamente útil.
Podemos seguir:
USER REQUEST
↓
CICS
↓
MQ
↓
AGENT
↓
Db2
↓
ANOTHER QUEUE
Tudo associado a:
CORRELATION-ID C500.
Quando alguém pergunta:
O que aconteceu com a solicitação do cliente?
não precisamos procurar agulha no palheiro.
📦 CAPÍTULO 6 — A MENSAGEM PODE SOBREVIVER AO CONSUMIDOR
Aqui está uma das grandes vantagens de mensageria confiável.
O producer envia.
PUT MESSAGE
O consumer está desligado.
A mensagem pode permanecer na fila conforme sua configuração e características.
Depois:
CONSUMER START
Ele processa.
Isso é muito diferente de uma chamada síncrona simples.
No mundo dos agentes isso é poderoso.
O LLM pode estar indisponível.
O worker pode reiniciar.
A aplicação pode sofrer manutenção.
A mensagem continua aguardando.
Mas agora surge uma pergunta:
Quanto tempo?
⏰ CAPÍTULO 7 — MENSAGENS TAMBÉM ENVELHECEM
Olha quem voltou.
Nosso amigo do Db2:
TEMPO.
Imagine:
MESSAGE CREATED:
10:00
O consumidor consegue processar:
14:00.
Tecnicamente a mensagem chegou.
Mas ainda faz sentido?
Talvez seja:
GENERATE MONTHLY REPORT
Sem problema.
Talvez seja:
CUSTOMER IS CURRENTLY LOGGED IN
Quatro horas depois?
Provavelmente inútil.
Logo mensagens também possuem:
AGE
EXPIRY
BUSINESS VALIDITY.
O MQ dos Agentes encontra o Db2 dos Agentes.
Não basta perguntar:
A mensagem chegou?
Pergunte:
Ela ainda significa alguma coisa quando chegou?
💥 CAPÍTULO 8 — A MENSAGEM CHEGOU DUAS VEZES
Finalmente chegamos ao monstro.
Imagine:
GET M001
O agente processa.
INSERT PAYMENT
COMMIT
Tudo certo.
Mas antes de completar adequadamente o ciclo de consumo:
CRASH.
Quando volta, dependendo da arquitetura e semântica empregada, a mensagem pode estar novamente disponível.
GET M001
O agente grita:
— Eu já processei isso!
A infraestrutura responde:
— Prove.
😂
Bem-vindo à possibilidade de:
REDELIVERY.
🧠 CAPÍTULO 9 — POR QUE DUPLICATAS EXISTEM?
Porque sistemas distribuídos possuem pontos de falha.
Considere:
MESSAGE
↓
PROCESS
↓
DATABASE UPDATE
↓
ACKNOWLEDGE
Agora imagine falha exatamente aqui:
DATABASE UPDATE
↓
COMMIT
↓
💀 CRASH
↓
ACKNOWLEDGE
Do ponto de vista do banco:
SUCCESS.
Do ponto de vista da mensageria:
NÃO SEI SE ELE TERMINOU.
O sistema possui duas escolhas ruins.
Escolha A
Assumir que processou.
Pode perder mensagem.
Escolha B
Entregar novamente.
Pode duplicar processamento.
Sistemas confiáveis frequentemente preferem não perder trabalho.
Então seu consumidor precisa estar preparado para duplicatas quando a semântica e arquitetura permitirem redelivery.
🔁 CAPÍTULO 10 — AT-LEAST-ONCE
Uma semântica comum em sistemas de mensageria e processamento distribuído é:
AT-LEAST-ONCE
Ou seja:
A mensagem será processada pelo menos uma vez.
Isso pode significar:
1
ou:
2
ou, em situações problemáticas:
mais.
Então:
AT-LEAST-ONCE
não significa:
EXACTLY-ONCE.
Se o efeito da mensagem não puder acontecer duas vezes, precisamos projetar proteção.
🎯 CAPÍTULO 11 — EXACTLY-ONCE É O UNICÓRNIO DA DUNGEON
Todo mundo quer:
EXACTLY ONCE.
A mensagem chega uma vez.
É processada uma vez.
O efeito acontece uma vez.
Perfeito.
Mas sistemas distribuídos possuem:
falhas
timeouts
restarts
partições
retries
E cada fronteira transacional complica o problema.
Se MQ e Db2 participam de uma unidade coordenada adequadamente, podemos obter garantias transacionais fortes em cenários específicos.
Mas quando nosso agente chama:
Db2
REST API
email
LLM
CRM SaaS
payment service
a conversa muda.
Nem todos participam da mesma transação.
Por isso é perigoso jogar:
EXACTLY-ONCE
num slide como se fosse magia.
🛡️ CAPÍTULO 12 — IDEMPOTÊNCIA ENTRA COM UMA ESPADA
A solução prática frequentemente passa por:
IDEMPOTÊNCIA.
Uma operação idempotente pode ser repetida sem produzir efeitos adicionais indesejados depois da primeira aplicação bem-sucedida.
Exemplo perigoso:
ADD R$ 100 TO BALANCE
Execute duas vezes:
+100
+100
Resultado diferente.
Agora imagine:
SET PAYMENT P123 STATUS = PROCESSED
Executar novamente pode ser inofensivo dependendo da regra.
Ou:
PROCESS PAYMENT
WHERE PAYMENT_ID = P123
AND STATUS = PENDING
Depois da primeira execução:
STATUS = PROCESSED.
A segunda encontra:
NOT PENDING.
Nada acontece.
Muito melhor.
🪪 CAPÍTULO 13 — IDEMPOTENCY KEY
Outra técnica:
IDEMPOTENCY-KEY.
Exemplo:
IDEMPOTENCY-KEY = ORDER-9981-PAYMENT
Antes de executar:
SELECT STATUS
FROM PROCESSED_MESSAGES
WHERE IDEMPOTENCY_KEY = :KEY;
Se já existe:
ALREADY PROCESSED
não repetimos o efeito.
Se não existe:
PROCESS
STORE KEY
COMMIT
O detalhe crucial é tornar essa proteção suficientemente atômica para impedir dois consumidores concorrentes de passarem pela verificação simultaneamente.
Porque senão:
AGENT-A: não existe!
AGENT-B: não existe!
AGENT-A: processa!
AGENT-B: processa!
Parabéns.
Construímos uma deduplicação que duplica.
😂
🔐 CAPÍTULO 14 — MQ E Db2 PRECISAM CONVERSAR SOBRE COMMIT
Agora chegamos ao terreno favorito do mainframe.
Imagine:
MQGET
↓
UPDATE Db2
↓
COMMIT
Queremos que o consumo da mensagem e a alteração no banco tenham comportamento consistente.
Idealmente, dentro de um desenho transacional apropriado:
GET MESSAGE
+
UPDATE DATABASE
+
COMMIT
fazem parte de uma unidade lógica coerente.
Se falhar:
ROLLBACK.
A alteração do banco não fica pela metade e a mensagem pode permanecer disponível conforme a unidade de trabalho.
Esse é exatamente o tipo de problema que mainframes resolvem há décadas.
🧱 CAPÍTULO 15 — UNIT OF WORK VOLTOU
Nosso velho amigo:
UOW
Unit of Work.
Conceitualmente:
BEGIN UOW
GET MESSAGE
UPDATE DB2
INSERT AUDIT
COMMIT UOW
Se:
UPDATE DB2
falhar:
ROLLBACK.
O importante é definir claramente:
Quais efeitos pertencem à mesma unidade de consistência?
Para agentes, isso é essencial.
Porque eles tendem a fazer muitas coisas.
☠️ CAPÍTULO 16 — O PROBLEMA DAS AÇÕES EXTERNAS
Nosso agente:
GET MESSAGE
↓
UPDATE Db2
↓
SEND EMAIL
↓
CALL PAYMENT API
↓
POST MESSAGE
Rollback?
Db2:
— Posso ajudar.
MQ:
— Também.
E-mail:
— Já enviei.
API externa:
— Já cobrei.
Internet:
— Boa sorte.
😂
Essa é a fronteira entre transações locais/coordenadas e efeitos distribuídos externos.
Você precisa pensar em:
idempotência
compensação
outbox
inbox
deduplicação
reconciliação
e não simplesmente:
ROLLBACK EVERYTHING.
📤 CAPÍTULO 17 — O OUTBOX PATTERN
Imagine que precisamos:
UPDATE ORDER
e depois publicar:
ORDER_COMPLETED.
Problema clássico:
UPDATE Db2
COMMIT
CRASH
SEND MESSAGE
O pedido mudou.
A mensagem nunca saiu.
Ou:
SEND MESSAGE
CRASH
UPDATE Db2
Mensagem diz que aconteceu.
Banco diz que não.
Uma abordagem conhecida é o:
TRANSACTIONAL OUTBOX.
Dentro da mesma transação do banco:
UPDATE ORDER
INSERT INTO OUTBOX
ORDER_COMPLETED
COMMIT
Depois outro processo publica registros da outbox.
Assim a intenção de publicar fica persistida junto com a alteração de negócio.
Ainda precisamos tratar duplicatas na publicação.
Mas removemos uma janela perigosa.
📥 CAPÍTULO 18 — INBOX PATTERN
Do outro lado podemos manter registro das mensagens consumidas.
INBOX
────────────────
MESSAGE_ID
PROCESSED_AT
STATUS
Chega:
M001.
Verificamos.
Não existe.
Processamos e registramos.
Chega novamente:
M001.
Existe.
DUPLICATE
Ignoramos ou retornamos o resultado anterior, conforme a aplicação.
Temos então:
OUTBOX
para publicação confiável e:
INBOX
para consumo controlado.
🧪 CAPÍTULO 19 — A MENSAGEM VENENOSA
Imagine:
MESSAGE M666
Chega.
Consumer tenta.
FAIL.
Volta.
Tenta.
FAIL.
Volta.
FAIL.
E continua:
FAIL
FAIL
FAIL
FAIL
FAIL
Talvez a mensagem tenha:
formato inválido
campo impossível
versão incompatível
regra não suportada
dados corrompidos
Ela nunca funcionará sem intervenção.
Chamamos informalmente esse tipo de caso de:
POISON MESSAGE.
Nosso agente não deveria ficar mastigando veneno eternamente.
☠️ CAPÍTULO 20 — BACKOUT QUEUE
Em ambientes IBM MQ, podemos projetar tratamento para mensagens que repetidamente causam rollback/backout.
Depois de determinado limite:
BACKOUT COUNT > THRESHOLD
a aplicação pode encaminhar a mensagem para uma:
BACKOUT QUEUE.
Assim:
NORMAL QUEUE
continua processando mensagens saudáveis.
E a problemática vai para investigação.
Conceitualmente:
QUEUE
│
▼
CONSUMER
│
├── SUCCESS → DONE
│
└── FAIL
│
▼
RETRY
│
▼
THRESHOLD?
│ │
NO YES
│ │
retry ▼
BACKOUT QUEUE
Isso impede que uma única mensagem bloqueie eternamente o fluxo.
⚰️ CAPÍTULO 21 — DEAD-LETTER QUEUE NÃO É EXATAMENTE A MESMA COISA
Aqui precisamos separar conceitos.
Uma:
DEAD-LETTER QUEUE
é tradicionalmente associada a mensagens que o sistema não consegue entregar ou encaminhar ao destino esperado por determinados problemas de entrega.
Uma:
BACKOUT QUEUE
pode ser usada pela aplicação para mensagens que foram entregues, mas falham repetidamente durante processamento e sofrem backout.
No discurso moderno muita gente usa “DLQ” genericamente para qualquer fila de mensagens problemáticas.
Mas no universo IBM MQ a distinção operacional é útil.
O mainframeiro olha e diz:
Nome correto evita reunião de duas horas.
🔁 CAPÍTULO 22 — RETRY PRECISA TER CÉREBRO
Falhou.
Tentamos novamente?
Depende.
Erro:
HTTP 503
Talvez.
Erro:
INVALID CUSTOMER ID
Provavelmente não.
Erro:
TIMEOUT
Talvez.
Erro:
SCHEMA VERSION UNSUPPORTED
Repetir cem vezes provavelmente produzirá cem fracassos.
Portanto:
RETRYABLE ERROR
e:
NON-RETRYABLE ERROR
deveriam ser tratados diferentemente.
Retry não é religião.
É política.
⏳ CAPÍTULO 23 — EXPONENTIAL BACKOFF
Imagine 10.000 agentes.
Serviço externo cai.
Todos tentam novamente:
NOW.
O serviço tenta recuperar.
Dez mil chamadas chegam.
Ele cai novamente.
Chamamos isso de:
RETRY STORM.
Uma estratégia:
1s
2s
4s
8s
16s
é exponential backoff.
Adicione:
JITTER
para não fazer todos acordarem exatamente juntos.
4.1s
3.7s
4.8s
Agora reduzimos o efeito manada.
WLM agradece.
🚂 CAPÍTULO 24 — E SE AS MENSAGENS CHEGAREM FORA DE ORDEM?
Imagine:
M1:
CUSTOMER_CREATED
depois:
M2:
CUSTOMER_BLOCKED.
Queremos:
M1 → M2.
Mas o consumidor observa:
M2 → M1.
Agora pode concluir:
Não existe cliente para bloquear.
Depois cria o cliente.
Resultado:
ACTIVE.
Mas deveria estar:
BLOCKED.
O conteúdo estava correto.
As mensagens estavam corretas.
A ordem estava errada.
Mais um monstro.
🔢 CAPÍTULO 25 — SEQUÊNCIA TAMBÉM É DADO
Podemos adicionar:
ENTITY-ID...... CUSTOMER-123
SEQUENCE....... 42
Depois:
ENTITY-ID...... CUSTOMER-123
SEQUENCE....... 43.
O consumidor recebe 43 antes de 42.
Pode:
esperar
reordenar
rejeitar
reconciliar
dependendo da arquitetura.
Mas precisa saber que existe ordem.
Sem informação de sequência, talvez seja impossível perceber o problema.
🧩 CAPÍTULO 26 — ORDEM GLOBAL É CARA E MUITAS VEZES DESNECESSÁRIA
Precisamos de todas as mensagens do sistema inteiro em ordem?
Provavelmente não.
Talvez precisemos apenas manter ordem por:
CUSTOMER-ID
ou:
ORDER-ID
ou:
ACCOUNT-ID.
Isso permite paralelismo entre entidades diferentes.
CUSTOMER A → ordered
CUSTOMER B → ordered
CUSTOMER C → ordered
mas A, B e C podem ser processados simultaneamente.
O segredo é perguntar:
Qual é a menor fronteira dentro da qual a ordem realmente importa?
🕰️ CAPÍTULO 27 — ATRASADA NÃO SIGNIFICA FORA DE ORDEM
Outra distinção.
Uma mensagem pode chegar:
30 minutos atrasada
mas ainda na ordem correta.
Ou:
100 ms depois
mas fora de ordem em relação a outra.
Temos conceitos diferentes:
LATENCY
ORDERING
FRESHNESS.
Não misture.
O Db2 dos Agentes nos ensinou:
dado tem idade.
O MQ acrescenta:
a notícia sobre o dado também tem idade.
📰 CAPÍTULO 28 — EVENT TIME E PROCESSING TIME
Imagine evento:
EVENT_TIME:
10:00:01
Chega ao consumidor:
PROCESSING_TIME:
10:00:09.
São tempos diferentes.
Isso pode ser extremamente relevante.
Se o agente analisa:
O que aconteceu às 10:00?
precisa considerar:
EVENT TIME.
Se pergunta:
Quando processamos?
usa:
PROCESSING TIME.
Misturar os dois pode gerar análises absurdas.
🤖 CAPÍTULO 29 — O AGENTE PRECISA SABER QUE O EVENTO NÃO É O ESTADO ATUAL
Chega mensagem:
10:00
CUSTOMER_STATUS_CHANGED
ACTIVE → BLOCKED
Nosso agente recebe às:
10:10.
Antes de executar ação crítica, talvez precise consultar Db2.
Porque às:
10:05
o cliente pode ter sido:
UNBLOCKED.
Então a mensagem descreve:
ALGO QUE ACONTECEU
e não necessariamente:
COMO O MUNDO ESTÁ AGORA.
Essa distinção é gigantesca.
EVENT ≠ CURRENT STATE.
🗄️ CAPÍTULO 30 — MQ E Db2 CONTAM HISTÓRIAS DIFERENTES
Db2 frequentemente responde:
Qual é o estado armazenado?
MQ frequentemente transporta:
Algo aconteceu.
Exemplo:
Db2:
STATUS = ACTIVE
Evento antigo:
CUSTOMER_BLOCKED.
Se o agente tratar o evento antigo como estado atual:
problema.
Por isso, para ações críticas:
EVENT
↓
INTERPRET
↓
READ CURRENT STATE
↓
REVALIDATE
↓
ACT.
Nosso princípio do Db2 dos Agentes volta novamente.
🔄 CAPÍTULO 31 — EVENTUAL CONSISTENCY
Em sistemas distribuídos, diferentes componentes podem não refletir a mesma mudança exatamente no mesmo instante.
Imagine:
SYSTEM A
STATUS = BLOCKED
Evento viaja.
Enquanto isso:
SYSTEM B
STATUS = ACTIVE.
Alguns milissegundos ou segundos depois:
SYSTEM B
STATUS = BLOCKED.
Durante esse intervalo:
A ≠ B.
Isso não significa necessariamente corrupção.
Pode ser uma consequência do modelo de propagação.
Agentes precisam compreender isso.
Dois sistemas podem temporariamente apresentar visões diferentes.
Lembra dos dois agentes do Db2?
Eles voltaram.
📊 CAPÍTULO 32 — OBSERVABILIDADE DA FILA
Nosso SDSF dos Agentes ganha novos campos.
Imagine:
QUEUE........ CUSTOMER.EVENTS
DEPTH........ 18,442
OLDEST MSG... 00:07:31
PUT RATE..... 920/s
GET RATE..... 710/s
CONSUMERS.... 24
BACKOUTS..... 17
RETRIES...... 2,881
Agora conseguimos enxergar:
QUEUE DEPTH
Mas profundidade sozinha não basta.
18 mil mensagens pode ser terrível.
Ou normal.
Precisamos de contexto.
Talvez mais importante seja:
OLDEST MESSAGE AGE.
Se a fila cresce e a mensagem mais velha envelhece:
temos backlog real.
📈 CAPÍTULO 33 — PRODUCER MAIS RÁPIDO QUE CONSUMER
Imagine:
PRODUCER = 1000 msg/s
CONSUMER = 700 msg/s
Diferença:
+300 msg/s.
Depois de um minuto:
18,000 messages
Depois de uma hora:
1,080,000.
A fila absorve pico.
Mas não derrota matemática.
😂
Se a entrada continuamente supera a saída:
BACKLOG ↑
LATENCY ↑
MESSAGE AGE ↑
Precisamos:
mais consumers
mais capacidade
reduzir trabalho
priorizar
aplicar backpressure
WLM aparece novamente.
🚦 CAPÍTULO 34 — BACKPRESSURE
Se consumidores não conseguem acompanhar produtores, precisamos evitar crescimento ilimitado.
Isso pode envolver:
rate limiting
quotas
producer throttling
queue limits
load shedding
priority
Mensageria desacopla componentes.
Mas desacoplamento não significa capacidade infinita.
Fila é amortecedor.
Não buraco negro.
🧰 CAPÍTULO 35 — CHECKLIST DO MQ DOS AGENTES
Antes de colocar um agente consumindo mensagens, responda:
1. Quem produz?
PRODUCER?
2. Quem consome?
CONSUMER?
3. A mensagem é persistente quando necessário?
Defina de acordo com criticidade.
4. Ela pode ser entregue novamente?
Projete assumindo que sim quando sua semântica permitir.
5. O processamento é idempotente?
Se não, por quê?
6. Existe idempotency key?
Defina.
7. Existe MESSAGE-ID?
Sempre saiba qual mensagem está tratando.
8. Existe CORRELATION-ID?
Rastreie o fluxo.
9. A ordem importa?
Defina a fronteira.
10. Existe sequence number?
Quando necessário.
11. A mensagem expira?
Defina validade.
12. Existe retry?
Classifique erros.
13. Existe backoff?
Evite tempestades.
14. Existe poison message handling?
Tenha backout ou quarentena adequada.
15. Existe DLQ?
Monitore.
16. A mensagem representa evento ou estado?
Não confunda.
17. Precisa revalidar no Db2?
Para ações críticas, provavelmente.
18. O commit inclui quais recursos?
Defina UOW.
19. Existem efeitos externos?
Planeje idempotência ou compensação.
20. Conseguimos provar o que aconteceu?
Logs, traces, IDs e timestamps.
🥚 EASTER EGG — A MENSAGEM IMORTAL
04:12.
O operador percebe:
BACKOUT COUNT = 847.
— Professor...
Turing aproxima-se.
MESSAGE-ID = M666
— Há quanto tempo?
FIRST DELIVERY:
1999-12-31 23:59:58
Silêncio.
O programador COBOL arregala os olhos.
— Isso está tentando processar desde o bug do milênio?
O administrador MQ responde:
— Ninguém teve coragem de apagar.
O pequeno robô pergunta:
— O que ela contém?
Abrem.
CUSTOMER-ID = 000000001
ACTION = MIGRATE
FORMAT = VERSION-0
Turing olha.
— Algum consumidor suporta versão zero?
Todos:
— Não.
— Então por que continuam tentando?
Silêncio.
O operador responde:
— Retry policy.
Turing pega o café.
— Qual?
RETRY = FOREVER.
Turing fecha os olhos.
No fundo do CPD alguém sussurra:
Ela não é uma mensagem... agora ela é patrimônio histórico.
😂
🧬 CAPÍTULO 36 — NOSSO MAINFRAME DOS AGENTES GANHOU SISTEMA NERVOSO
Nossa arquitetura agora possui:
RACF
↓
QUEM PODE?
PF3
↓
POSSO PARAR?
SDSF
↓
O QUE ESTÁ FAZENDO?
MAXCC
↓
FUNCIONOU?
WLM
↓
QUEM RECEBE RECURSOS?
JES2
↓
QUEM COMEÇA E QUANDO?
CICS
↓
QUANTO TEMPO O HUMANO PODE ESPERAR?
Db2
↓
QUAL REALIDADE O AGENTE ESTÁ VENDO?
E agora:
MQ
↓
COMO A NOTÍCIA VIAJA ENTRE OS AGENTES?
Começamos a montar algo muito interessante.
🏰 CAPÍTULO 37 — O SISTEMA OPERACIONAL CONCEITUAL DOS AGENTES
Turing desenha:
HUMANO
│
▼
CICS
│
┌───────────┴───────────┐
│ │
▼ ▼
AGENT MQ
│ │
│ ┌────┴────┐
│ ▼ ▼
│ AGENT-B AGENT-C
│ │ │
└──────────┬───────┴─────────┘
▼
Db2
Acima:
JES2
SCHEDULING
Ao redor:
WLM
CAPACITY
Na porta:
RACF
AUTHORITY
Na sala de operações:
SDSF
OBSERVABILITY
E sobre as mensagens:
MESSAGE-ID
CORRELATION-ID
SEQUENCE
TIMESTAMP
VERSION
Agora agentes não são apenas pequenos cérebros isolados.
Eles formam:
UM SISTEMA DISTRIBUÍDO.
E sistemas distribuídos possuem regras próprias.
🧠 CAPÍTULO 38 — O PROGRAMADOR COBOL TEM OUTRA VANTAGEM INESPERADA
Para muita gente:
event-driven architecture
message queues
async processing
retries
parecem invenções moderníssimas.
O mainframeiro olha para MQ e diz:
Sente-se, jovem.
😂
Porque aplicações corporativas trabalham com:
mensageria
filas
commit
rollback
correlation
persistence
backout
recovery
há décadas.
Claro que tecnologias modernas acrescentaram:
cloud
Kafka
microservices
containers
serverless
agents
LLMs.
Mas a pergunta fundamental continua:
Como dois componentes independentes trocam trabalho sem transformar cada falha em catástrofe?
Essa pergunta é antiga.
E continua excelente.
☕ CAPÍTULO 39 — O AGENTE PRECISA SER DESCONFIADO
Um consumidor robusto não deveria pensar:
Recebi uma mensagem. Vou obedecer.
Deveria perguntar:
Quem enviou?
Posso confiar?
Qual schema?
Qual versão?
Já processei?
Está na ordem?
Ainda é válida?
Tenho autorização?
Preciso consultar o estado atual?
Posso executar duas vezes?
Como confirmo?
Como desfaço?
Isso parece paranoia.
Em produção chamamos de:
ENGENHARIA.
🎭 CAPÍTULO 40 — EVENTO NÃO É ORDEM
Outra regra importante para agentes.
Mensagem:
CUSTOMER_CHANGED_ADDRESS
significa:
O endereço mudou.
Não significa automaticamente:
Faça alguma coisa perigosa.
O agente precisa interpretar eventos dentro de política.
Uma arquitetura segura separa:
FACT
de:
DECISION
e:
ACTION.
Assim:
EVENT
↓
OBSERVE
↓
VALIDATE
↓
DECIDE
↓
AUTHORIZE
↓
ACT
Essa separação reduz o risco de transformar qualquer mensagem numa ordem administrativa.
RACF agradece novamente.
🔐 CAPÍTULO 41 — MENSAGEM TAMBÉM PRECISA DE ZERO TRUST
Nosso agente não deveria confiar numa mensagem apenas porque ela apareceu numa fila.
Considere:
identity
authorization
queue permissions
message integrity
origin
schema validation
content validation
Uma mensagem malformada ou maliciosa pode tentar fazer o agente:
CALL TOOL
TRANSFER MONEY
DELETE DATA
Logo:
MQ
não elimina segurança.
Ele apenas transporta dados.
Autorização continua sendo outra camada.
🧾 CAPÍTULO 42 — SCHEMA É O COPYBOOK DA MENSAGEM
O programador COBOL conhece isso imediatamente.
Mensagem:
01 PAYMENT-MESSAGE.
05 MSG-VERSION PIC 9(04).
05 PAYMENT-ID PIC X(20).
05 CUSTOMER-ID PIC 9(09).
05 AMOUNT PIC S9(11)V99 COMP-3.
05 EVENT-TIMESTAMP PIC X(26).
Isso define estrutura.
Mas lembramos do Db2 dos Agentes:
ESTRUTURA NÃO É SEMÂNTICA.
Precisamos saber:
AMOUNT é em BRL?
PAYMENT-ID é único?
EVENT-TIMESTAMP usa qual timezone?
CUSTOMER-ID pode ser zero?
MSG-VERSION 0002 é compatível?
O copybook voltou.
Ele nunca foi embora.
🔄 CAPÍTULO 43 — VERSIONAMENTO DE MENSAGEM
Hoje:
VERSION 1
Amanhã precisamos adicionar:
CURRENCY.
Criamos:
VERSION 2.
Mas ainda existem consumidores antigos.
Agora precisamos pensar:
backward compatibility
forward compatibility
schema evolution
Nosso agente deve saber:
Consigo interpretar esta versão?
Se não:
QUARANTINE
é melhor do que:
VOU CHUTAR.
Especialmente quando “chutar” movimenta dinheiro.
🌊 CAPÍTULO 44 — A FILA É UM AMORTECEDOR, NÃO UM MILAGRE
Turing desenha:
PRODUCER
████████████████████████
↓
QUEUE
~~~~~~~~~~~~~~~~~~~~~~~~
↓
CONSUMER
██████████
A fila absorve diferença temporária.
Mas se producer permanece mais rápido:
QUEUE DEPTH ↑
até algum limite operacional.
Portanto monitoramos:
depth
oldest age
put rate
get rate
consumer count
failure rate
backout count
Uma fila crescente é uma mensagem.
Ironicamente:
A própria fila está tentando falar com você.
🏁 CAPÍTULO 45 — A REGRA DE OURO
Alan Turing escreve no quadro:
MESSAGE RECEIVED
≠
BUSINESS ACTION COMPLETED
Depois:
BUSINESS ACTION COMPLETED
≠
MESSAGE ACKNOWLEDGED
Depois:
MESSAGE ACKNOWLEDGED
≠
WORLD STILL UNCHANGED
O pequeno robô fica olhando.
— Então eu não posso confiar em nada?
Turing sorri.
— Pode.
— Em quê?
— Em protocolos, transações, IDs, versões, políticas, observabilidade e verificações.
— Parece trabalhoso.
— É.
— E se eu simplesmente confiar que tudo dará certo?
Do fundo do CPD, cinco administradores começam a rir.
☕ EPÍLOGO — RECEBIDO NÃO SIGNIFICA TERMINADO
07:02.
A equipe da manhã chega.
Nosso agente recebe:
MESSAGE-ID..... M004821
CORREL-ID...... C009912
EVENT.......... PAYMENT_REQUESTED
PAYMENT-ID..... P88271
AMOUNT......... 850.00
SEQUENCE....... 42
EVENT-TIME..... 07:02:01
Ele verifica:
SCHEMA......... OK
VERSION........ SUPPORTED
AUTHORITY...... OK
DUPLICATE...... NO
ORDER.......... OK
FRESHNESS...... OK
Consulta Db2.
CURRENT STATE.. VALID
Inicia unidade de trabalho.
PROCESS PAYMENT
INSERT AUDIT
REGISTER MESSAGE
Confirma.
COMMIT.
Resultado:
SUCCESS.
Alguns milissegundos depois:
💥
O worker cai.
Reinicia.
A mensagem aparece novamente.
O velho agente teria gritado:
Já fiz isso!
O novo apenas consulta:
IDEMPOTENCY-KEY = P88271.
Resposta:
ALREADY PROCESSED.
Nenhum pagamento duplicado.
Nenhuma madrugada destruída.
Nenhum DBA perseguindo robôs pelo corredor.
O agente registra:
DUPLICATE DELIVERY
BUSINESS EFFECT SKIPPED
STATUS = SAFE
Turing toma o primeiro café da manhã.
O programador pergunta:
— Então a duplicata deixou de ser um problema?
— Não.
— Mas não aconteceu nada.
— Exatamente.
O programador pensa.
Turing continua:
— Engenharia confiável não é construir um universo onde falhas nunca acontecem.
Aponta para a tela:
REDELIVERY DETECTED
— É construir um sistema no qual falhas esperadas deixam de virar desastres inesperados.
O pequeno robô levanta a mão.
— Professor?
— Sim?
— Posso apagar a mensagem de 1999?
Do outro lado do CPD o administrador MQ grita:
— NÃO! PRIMEIRO FAZ BACKUP!
😂
Turing fecha o terminal.
Na tela permanece:
MQ
────────────────────────
MESSAGE-ID...... M004821
DELIVERY........ 2
PROCESSING...... IDEMPOTENT
BUSINESS EFFECT. ONCE
STATUS.......... SAFE
COMMIT.......... OK
E embaixo:
A MENSAGEM PODE CHEGAR NOVAMENTE.
O EFEITO NÃO PRECISA ACONTECER NOVAMENTE.
Essa é talvez a grande lição do MQ dos Agentes.
Porque quando centenas de inteligências artificiais começarem a conversar entre si, delegando tarefas, publicando eventos, recebendo respostas, reiniciando workers, atravessando APIs, bancos, filas e nuvens, não bastará perguntar:
A mensagem chegou?
Teremos de perguntar:
Qual mensagem?
De quem?
Quando?
Em qual ordem?
Quantas vezes?
Ainda é válida?
Já foi processada?
O efeito foi confirmado?
Posso executar novamente?
E se eu cair exatamente agora?
O mainframe conhece essa paranoia há décadas.
E talvez por isso tenha tanta coisa para ensinar aos agentes.
MQGET
↓
VALIDATE
↓
DEDUPLICATE
↓
REVALIDATE
↓
PROCESS
↓
COMMIT
↓
ACKNOWLEDGE
Alan Turing olha para o fluxo.
Toma mais um gole.
E acrescenta apenas uma linha:
IF IN DOUBT
DO NOT CHARGE THE CUSTOMER TWICE.
READY