| Bellacosa Mainframe apresenta cobol e db2 |
☕ Um Café no Bellacosa Mainframe
Kojak Entra no CPD — O Dia em que um SQLCODE -911 Virou Suspeito e o Db2 Foi Chamado para Depor
Ou: como COBOL, SQL, DBRM, BIND, PACKAGE, access path, COMMIT, ROLLBACK, locking, CICS e um programador iniciante descobriram que, no mainframe, até um SELECT tem antecedentes
Nova York, 1973.
Um policial careca, impecavelmente vestido, andando pelas ruas com um pirulito na boca começa a resolver crimes enquanto solta ironias e uma pergunta que se tornaria inseparável do personagem:
“Who loves ya, baby?”
Era o tenente Theo Kojak.
A série Kojak, criada por Abby Mann e protagonizada por Telly Savalas, estreou na CBS em 24 de outubro de 1973 e permaneceu originalmente no ar até 1978. (Paramount Press Express)
Curiosamente, Kojak surgiu antes da série: Telly Savalas interpretou o personagem no telefilme The Marcus-Nelson Murders, de 1973, que funcionou como origem do programa. (Los Angeles Times)
E existe algo profundamente mainframe em Kojak.
Ele não olha apenas para o cadáver.
Olha para quem entrou, quem saiu, quem tinha autorização, quem estava segurando o recurso, quem alterou o registro e por que aquela história aparentemente perfeita não combina com as evidências.
Troque a delegacia por um CPD.
Troque o assassinato por uma transação travada.
Troque as testemunhas por logs.
E dê ao tenente um terminal 3270.
Temos nosso novo investigador Db2.
Prólogo — 03:17, alguma coisa morreu em produção
O telefone toca.
O programador COBOL iniciante atende.
— Produção está parada.
Cinco palavras capazes de transformar café em combustível nuclear.
Ele abre o fonte.
EXEC SQL
SELECT SALDO
INTO :WS-SALDO
FROM CONTA
WHERE NUMERO = :WS-CONTA
END-EXEC.Olha novamente.
Nada parece errado.
Compilou.
Ontem funcionava.
O programador conclui:
— Deve ser o banco.
A porta do CPD abre.
Um homem careca entra lentamente, coloca um pirulito na boca e observa o terminal.
— O banco, baby?
Silêncio.
— Vamos começar novamente. Quem executou o SQL?
E aí descobrimos nosso primeiro problema.
1. COBOL não executa SQL sozinho
Quando vemos:
EXEC SQL
SELECT ...
END-EXECé fácil imaginar que SELECT seja uma instrução COBOL.
Não é.
COBOL e SQL são linguagens diferentes trabalhando juntas.
Podemos imaginar três responsabilidades:
COBOL
│
├── lógica da aplicação
├── cálculos
├── decisões
└── fluxo
│
▼
SQL
│
└── declara quais dados queremos
│
▼
Db2
│
└── determina como obtê-losO COBOL pode dizer:
“Preciso consultar o saldo desta conta.”
O SQL expressa:
SELECT SALDO
FROM CONTA
WHERE NUMERO = :WS-CONTAE o Db2 precisa resolver como encontrar aquele registro eficientemente.
Kojak tira o pirulito da boca.
— Então temos três testemunhas. COBOL diz uma coisa, SQL pede outra e Db2 faz o trabalho pesado.
Exatamente.
2. O primeiro interrogatório: as Host Variables
Observe:
WHERE NUMERO = :WS-CONTAPor que existe aquele :?
Porque WS-CONTA pertence ao programa hospedeiro.
É uma host variable.
Temos uma fronteira:
COBOL SQL
WS-CONTA ───────────────► :WS-CONTA
WS-SALDO ◄─────────────── :WS-SALDONo comando:
EXEC SQL
SELECT SALDO
INTO :WS-SALDO
FROM CONTA
WHERE NUMERO = :WS-CONTA
END-EXECWS-CONTA fornece informação.
WS-SALDO recebe informação.
Parece simples.
Até aparecerem:
tipos incompatíveis;
valores NULL;
indicator variables;
truncamentos;
conversões;
nenhuma linha;
múltiplas linhas.
Kojak aponta para WS-CONTA.
— Interrogue esse sujeito primeiro. Todo mundo culpa o Db2 antes de perguntar o que mandou para ele.
Primeira regra da investigação:
entrada errada produz investigação errada.
3. O crime aconteceu antes da execução
Nosso iniciante acredita que o fonte vai diretamente para o compilador COBOL.
Kojak balança a cabeça.
— Baby... tem alguém faltando nessa história.
Quando existe SQL embutido, o SQL precisa ser processado.
No fluxo tradicional, podemos representar:
COBOL + SQL
│
▼
PRECOMPILE
│
├────────────► DBRM
│
▼
COBOL modificado
│
▼
COBOL COMPILER
│
▼
OBJECT
│
▼
LINK-EDIT
│
▼
LOAD MODULEO processamento SQL e o processamento COBOL são coisas distintas.
E acabamos de encontrar nosso primeiro suspeito misterioso.
DBRM.
4. DBRM — o dossiê policial do SQL
DBRM significa Database Request Module.
Não é o executável COBOL.
Não é a tabela.
Não é o banco.
Não é o programa completo.
Uma maneira didática de imaginá-lo é como o dossiê contendo informações sobre as requisições SQL extraídas do programa para uso posterior pelo Db2.
A documentação IBM descreve o DBRM dentro justamente desse processo de preparação das instruções SQL para o bind. (Wikipedia)
Imagine:
PROGRAMA ABC123
SQL 001
SELECT SALDO...
SQL 002
UPDATE CONTA...
SQL 003
INSERT MOVIMENTO...O Db2 precisará conhecer essas requisições.
O DBRM entra nessa história.
Kojak pega a pasta.
— Agora temos antecedentes.
5. BIND — agora leve o suspeito para a delegacia
Aqui aparece uma palavra que todo programador COBOL/Db2 precisa conhecer:
BIND.
Simplificando bastante, durante o bind o Db2 trabalha com as informações SQL preparadas anteriormente e produz aquilo que será necessário para executar o SQL estático.
Temos:
DBRM
│
▼
BIND PACKAGE
│
▼
PACKAGEAgora nossa investigação começa a ficar interessante.
Porque significa que existem dois universos relacionados:
APLICAÇÃO
┌───────────────┐
│ │
▼ ▼
COBOL SQL
│ │
▼ ▼
LOAD MODULE DBRM
│
▼
BIND
│
▼
PACKAGEÉ perfeitamente possível que alguém diga:
“Mas o COBOL compilou!”
Ótimo.
Isso prova apenas uma parte da história.
Kojak sorri.
— Compilou? Muito bonito. Agora me mostre o package.
6. PACKAGE — o personagem que o iniciante quase nunca conhece
O package contém informações necessárias para a execução das instruções SQL estáticas associadas à aplicação.
E aqui ocorre uma mudança mental importante.
O programa executável e o SQL preparado possuem processos relacionados, mas distintos.
Portanto:
SOURCE
│
├──────── COBOL ────────► LOAD MODULE
│
└──────── SQL ─► DBRM ─► BIND ─► PACKAGENa execução, esses mundos precisam conversar corretamente.
É por isso que um problema de Db2 pode existir mesmo quando o compilador COBOL não reclamou.
O compilador não é Sherlock Holmes.
Muito menos Kojak.
7. “Mas quem decidiu usar aquele índice?”
Chegamos ao grande interrogatório.
Considere:
SELECT NOME,
LIMITE,
SALDO
FROM CLIENTE
WHERE CPF = :WS-CPFO programador declarou o que deseja.
Não escreveu:
vá para cilindro X
abra página Y
ande 37 posições
leia registro ZEssa é uma das grandes diferenças entre programação procedural e acesso relacional.
O Db2 possui um optimizer.
Conceitualmente:
SQL
│
▼
OPTIMIZER
│
┌──────┼──────┐
│ │ │
▼ ▼ ▼
índices estatísticas cardinalidade
│ │ │
└──────┼──────┘
▼
ACCESS PATH
│
▼
EXECUÇÃOO optimizer avalia alternativas e custos.
Pode considerar índices.
Pode considerar outras estratégias.
O resultado dessa decisão é fundamental para performance.
Kojak olha para o programador:
— Você escreveu o SELECT. Mas não decidiu necessariamente como ele encontraria o sujeito.
Perfeito.
8. RUNSTATS — quando a testemunha está dizendo a verdade de 2019
Agora temos uma situação deliciosa.
O programa não mudou.
O SQL não mudou.
A tabela não mudou estruturalmente.
Mas o desempenho ficou horrível.
Como?
Imagine que o Db2 esteja tomando decisões baseado em informações estatísticas inadequadas ou desatualizadas.
É como Kojak perguntar:
— Quantas pessoas vivem neste prédio?
E alguém responder:
— Doze.
— Quando você contou?
— Em 1997.
Temos um problema.
As estatísticas ajudam o optimizer a compreender características dos dados.
Daí a importância de operações como RUNSTATS.
Isso nos ensina uma lição extraordinária:
Performance SQL não está inteiramente dentro do fonte SQL.
Ela depende do ecossistema.
9. Índice não é pó mágico
O iniciante aprende:
Índice deixa SELECT rápido.
Depois cria índice para tudo.
Parabéns.
Acabamos de transformar uma boa ideia em um novo incidente.
Índices possuem custo.
Precisam ser mantidos.
INSERT, UPDATE e DELETE podem ter trabalho adicional quando índices associados precisam ser atualizados.
Portanto:
MAIS ÍNDICES
não significa automaticamente
MAIS PERFORMANCEA pergunta é:
qual índice atende quais padrões reais de acesso?
Kojak provavelmente perguntaria:
— Esse índice estava onde na noite do crime?
10. O SQLCODE chega para depor
Executamos:
EXEC SQL
SELECT SALDO
INTO :WS-SALDO
FROM CONTA
WHERE NUMERO = :WS-CONTA
END-EXECE agora?
Precisamos saber o resultado.
Entra o SQLCODE.
Didaticamente:
SQLCODE = 0
│
└── sucesso
SQLCODE positivo
│
└── condição/warning
SQLCODE negativo
│
└── erroUm programador iniciante pode fazer:
IF SQLCODE NOT = ZERO
DISPLAY 'ERRO DB2'
END-IF.Kojak quase engole o pirulito.
Porque os códigos precisam ser interpretados, não simplesmente classificados como “deu certo/deu errado”.
O famoso +100, por exemplo, normalmente indica ausência de linha correspondente ou fim do conjunto de resultados conforme a operação.
Isso pode ser perfeitamente normal.
Pesquisar cliente inexistente não significa necessariamente que o banco “quebrou”.
11. Até que aparece o cadáver: SQLCODE -911
Agora temos drama.
O programa retorna:
SQLCODE = -911E alguém grita:
— Db2 caiu!
Não necessariamente.
Temos uma pista ligada a situações envolvendo rollback provocado por deadlock ou timeout, conforme o contexto e reason code.
Kojak sorri.
Finalmente um crime digno dele.
Imagine:
TRANSAÇÃO A
LOCK RECURSO 1
│
▼
PRECISA RECURSO 2
TRANSAÇÃO B
LOCK RECURSO 2
│
▼
PRECISA RECURSO 1Temos:
A espera B
B espera AOs dois poderiam esperar até a aposentadoria do programador.
O sistema precisa resolver a situação.
Bem-vindo ao mundo de locking, timeout e deadlock.
12. ACID — a divisão de crimes financeiros
Imagine uma transferência:
CONTA A = 1000
CONTA B = 500
TRANSFERÊNCIA = 100Queremos terminar:
A = 900
B = 600Não queremos:
A = 900
B = 500porque o sistema caiu no meio.
É aqui que as propriedades ACID entram em cena:
Atomicity — a unidade lógica precisa ser tratada de maneira indivisível quanto ao resultado transacional.
Consistency — regras de consistência devem permanecer válidas.
Isolation — transações concorrentes precisam coexistir sob regras controladas.
Durability — depois da confirmação, os resultados precisam possuir as garantias de persistência esperadas.
Esse não é detalhe acadêmico.
É o motivo pelo qual você consegue dormir depois de transferir dinheiro pelo banco.
13. COMMIT — pode liberar os suspeitos
Considere:
UPDATE CONTA A
│
▼
UPDATE CONTA B
│
▼
INSERT MOVIMENTO
│
▼
COMMITO COMMIT delimita a confirmação apropriada daquela unidade de trabalho.
Mas imagine:
UPDATE A OK
UPDATE B OK
INSERT ERRODependendo da lógica e do contexto transacional, podemos precisar desfazer alterações.
Entra:
ROLLBACKPortanto:
UNIT OF WORK
UPDATE
│
UPDATE
│
INSERT
│
├──── tudo correto ───► COMMIT
│
└──── problema ───────► ROLLBACKEssa dupla é parte central da engenharia transacional.
14. COMMIT também é performance
Agora vem uma sutileza importante.
Alguns iniciantes imaginam COMMIT apenas como:
salvar alterações.
Mas unidades de trabalho também estão relacionadas à utilização e liberação de determinados recursos e locks.
Imagine um batch processando:
10 registros
100 registros
10.000 registros
10.000.000 registrossem pensar adequadamente na estratégia de commit.
Ele pode manter recursos por períodos indesejáveis, afetar concorrência e complicar recuperação.
Logo, frequência de commit não deveria ser escolhida com:
COMMIT a cada 1000simplesmente porque alguém encontrou esse número num programa de 1987.
A estratégia depende da aplicação.
15. Então chega o CICS à delegacia
Até agora poderíamos estar pensando em batch.
Mas coloque a aplicação em ambiente transacional:
CLIENTE
│
▼
CICS
│
▼
PROGRAMA COBOL
│
▼
SQL
│
▼
DB2Agora milhares de usuários podem executar transações.
Temos simultaneamente:
CICS transaction 001 ──┐
CICS transaction 002 ──┤
CICS transaction 003 ──┼──► Db2
CICS transaction 004 ──┤
CICS transaction 005 ──┘Nesse momento, locking deixa de ser um capítulo de apostila.
Vira sobrevivência.
16. COBOL + Db2 Connect? Cuidado com essa simplificação
O infográfico original apresenta conceitualmente uma camada intermediária chamada Db2 Connect.
Aqui Kojak levantaria uma sobrancelha.
Db2 Connect não deve ser ensinado como passagem obrigatória de todo programa COBOL local no z/OS para Db2.
Dependendo do ambiente, temos diferentes mecanismos de attachment e execução.
Batch, CICS, IMS, TSO e aplicações distribuídas podem envolver arquiteturas diferentes.
Portanto:
COBOL → DB2 Connect → Db2não é uma representação universal.
Esse é exatamente o tipo de simplificação que funciona num infográfico 101, mas precisa ser desmontada quando o aluno chega ao nível 201.
17. “COBOL cuida da regra; Db2 cuida dos dados”
Boa explicação.
Mas incompleta.
No mundo real, Db2 também possui recursos relacionados a:
constraints;
integridade referencial;
views;
triggers;
stored procedures;
functions.
Então podemos pensar:
COBOL
│
├── fluxo
├── cálculo
├── regras da aplicação
└── orquestração
DB2
│
├── persistência
├── integridade
├── concorrência
├── recuperação
├── otimização
└── acesso relacionalAs responsabilidades precisam ser projetadas.
Não simplesmente presumidas.
18. Static SQL e Dynamic SQL entram na sala
Nosso exemplo:
EXEC SQL
SELECT SALDO
...
END-EXECé um exemplo clássico de SQL embutido estático.
Conceitualmente:
STATIC SQL
conhecido durante
preparação/deployment
│
▼
processamento
│
▼
DBRM
│
▼
BIND
│
▼
PACKAGEMas existe SQL dinâmico.
Podemos ter situações nas quais a instrução é preparada durante a execução usando mecanismos como PREPARE e EXECUTE.
Isso oferece flexibilidade, mas muda aspectos da preparação e execução.
Novamente:
não existe “um é sempre melhor”.
Existe:
qual mecanismo atende melhor ao problema?
19. O crime perfeito: SELECT *
Kojak encontra:
SELECT *
FROM CLIENTEOlha para o programador.
Olha novamente para a tela.
— Você precisa de todas essas colunas?
— Não.
— Então por que pediu?
Silêncio.
Essa pergunta vale ouro.
Evite transportar dados desnecessários.
Se precisa de:
NOME
SALDOnão peça quarenta colunas simplesmente porque:
SELECT *é mais rápido de digitar.
Cinco segundos economizados na programação podem gerar trabalho desnecessário repetido milhões de vezes em produção.
20. Outro suspeito: SELECT dentro de LOOP
Agora Kojak encontra:
PERFORM 100000 TIMES
SELECT ...
END-PERFORMAcende a luz da sala de interrogatório.
Dependendo do problema, isso pode representar enorme quantidade de chamadas e processamento que talvez pudesse ser resolvida por uma estratégia SQL muito melhor.
O erro conceitual é tratar banco relacional como arquivo sequencial:
LEIA
LEIA
LEIA
LEIA
LEIASQL trabalha naturalmente com conjuntos.
Essa mudança de pensamento é enorme para quem vem de COBOL procedural.
21. Não coloque toda a investigação num único SQL monstruoso
Mas existe o extremo oposto.
Alguém aprende que SQL trabalha com conjuntos e produz:
SELECT
JOIN
JOIN
JOIN
CASE
CASE
SUBQUERY
UNION
CTE
FUNCTION
JOIN
...O SQL parece o mapa genealógico de Game of Thrones.
SQL complexo não é automaticamente ruim.
Às vezes uma operação sofisticada no banco é exatamente a solução correta.
Mas complexidade precisa possuir justificativa.
Legibilidade importa.
Manutenção importa.
Performance importa.
Testabilidade importa.
22. EXPLAIN — reconstruindo a cena do crime
Se queremos investigar desempenho, precisamos saber mais sobre o access path.
É aí que conceitos relacionados ao EXPLAIN tornam-se fundamentais.
O objetivo é investigar como o Db2 pretende ou decidiu acessar os dados, conforme o cenário analisado.
Você começa a procurar pistas:
INDEX?
TABLESPACE SCAN?
JOIN METHOD?
JOIN ORDER?
ESTIMATIVAS?
SORT?Kojak coloca fotografias na parede.
Agora temos uma investigação de verdade.
O programador deixa de dizer:
“Esse SELECT está lento.”
E começa a perguntar:
“Qual access path está sendo utilizado e por quê?”
Essa diferença separa palpite de diagnóstico.
23. O verdadeiro CPD aparece
Finalmente afastamos a câmera.
O programador pensava que estava trabalhando com:
COBOL + DB2Mas agora enxerga:
z/OS
│
┌──────────────┼──────────────┐
│ │ │
JES2 CICS Db2
│ │ │
JCL COBOL SQL
│ │ │
datasets ├──────────────┤
│ │
MQ PACKAGE
│ │
API OPTIMIZER
│
ACCESS PATH
│
┌───────┴───────┐
│ │
INDEXES TABLESE ainda não terminamos.
Ao redor estão:
RACF
SMF
WLM
TCP/IP
LOGS
RECOVERY
BACKUP
MONITORAMENTO
PARALLEL SYSPLEXAgora entendemos por que profissionais podem trabalhar décadas com mainframe e continuar aprendendo.
24. Kojak finalmente resolve o caso
Voltamos às 03:17.
O programa estava correto.
O SQL também.
O Db2 estava disponível.
O problema estava relacionado à concorrência.
Outra unidade de trabalho estava mantendo recursos necessários.
A transação esperou.
O limite foi atingido.
O SQL recebeu erro.
O programa não possuía tratamento suficientemente inteligente.
A mensagem apresentada ao operador foi:
ERRO NO BANCOOu seja:
o único inocente da investigação talvez fosse justamente “o banco”.
Kojak olha para o programador.
— Então você acusou Db2 sem verificar SQLCODE, reason code, locks, unidade de trabalho e logs?
— Sim.
Kojak coloca o pirulito na boca.
— Who loves ya, baby?
🍭 Easter egg — por que Kojak usa pirulito?
Esse detalhe é perfeito para nossa história.
O pirulito não nasceu simplesmente como um adereço aleatório inventado pelo departamento de marketing. Telly Savalas contou posteriormente que a ideia surgiu numa situação em que uma personagem queria que Kojak parasse de fumar e lhe entregava um pirulito; o detalhe pegou e tornou-se uma das marcas visuais do personagem. (Los Angeles Times)
Na própria série, o pirulito apareceu durante a primeira temporada e acabou associado à tentativa de reduzir o cigarro. (Wikipedia)
Ou seja:
pequena decisão
│
▼
efeito inesperado
│
▼
torna-se característica permanenteQualquer semelhança com sistemas legados é absolutamente maravilhosa.
Um programador em 1984 cria:
05 WS-FLAG PIC X.para resolver temporariamente um problema.
Quarenta anos depois:
“Não mexa no
WS-FLAG. Ninguém sabe exatamente por quê, mas o fechamento mensal depende dele.”
😂
25. A lição que Kojak deixaria ao programador COBOL
O grande erro de quem começa no mainframe é imaginar que aprender COBOL significa aprender sintaxe:
MOVE
COMPUTE
IF
EVALUATE
PERFORMIsso é apenas a porta de entrada.
Depois vem:
COBOL
+
JCL
+
Db2
+
CICS
+
VSAM
+
MQ
+
RACF
+
JES2
+
WLM
+
SMF
+
z/OSE finalmente você percebe:
o sistema corporativo não é um programa. É um ecossistema de componentes cooperando.
O SELECT SALDO que parece possuir cinco linhas pode envolver host variables, processamento SQL, DBRM, package, optimizer, estatísticas, índices, buffer management, locking, logging, segurança, recuperação e infraestrutura antes de devolver alguns bytes para WS-SALDO.
Essa é uma das coisas mais bonitas do mainframe.
Na superfície:
EXEC SQL
SELECT SALDO
INTO :WS-SALDO
...
END-EXEC.Debaixo:
50 ANOS
DE
ENGENHARIA
│
▼
WS-SALDOO programador olha para a variável.
Depois olha para Kojak.
Finalmente compreendeu.
Não basta perguntar:
“O programa compilou?”
É preciso investigar:
Qual SQL executou? Qual SQLCODE retornou? Qual package? Qual access path? As estatísticas estão adequadas? Existe índice apropriado? Houve locking? Timeout? Deadlock? Onde começa e termina a unidade de trabalho? Houve COMMIT? O que os logs mostram?
Porque produção não aceita álibi.
Produção aceita evidência.
Kojak se levanta, pega o sobretudo e caminha para a porta do CPD.
O programador pergunta:
— Tenente, e se amanhã aparecer outro -911?
Kojak sorri.
— Agora você sabe interrogar os suspeitos, baby.
E sai.
Na mesa fica apenas um pirulito, um dump, três relatórios de performance e um post-it:
****************************************
* NÃO CULPE O DB2 ANTES DO EXPLAIN. *
****************************************Fim do JOB.
IEF404I KOJAK - ENDED
IEF142I KOJAK STEP01 - COND CODE 0000E, pela primeira vez naquela madrugada, ninguém precisou dar ROLLBACK no café. ☕🍭
Sem comentários:
Enviar um comentário