TS vs TD — quando usar cada um
| Característica | Temporary Storage (TS) | Transient Data (TD) |
|---|---|---|
| Acesso | Aleatório — por número de item | Sequencial — FIFO, um sentido |
| Leitura destrutiva? | Não — o item permanece após READQ | Sim — READQ remove o registro da fila |
| Escrita | WRITEQ TS (novo item ou reescreve) | WRITEQ TD (sempre adiciona ao final) |
| Exclusão | DELETEQ TS apaga a fila inteira | Não há DELETEQ TD; esvazia lendo |
| Definição prévia? | Não — criada dinamicamente pelo programa | Sim — TDQUEUE deve ser definida no CSD |
| Trigger automático | Não | Sim — dispara transação ao atingir nível |
| Uso típico | Paginação, cache temporário entre telas | Impressão, log, fila de processamento |
Temporary Storage — MAIN vs AUXILIARY
Ao gravar em uma fila TS, você pode especificar onde o CICS deve armazená-la:
| Opção | Armazenamento | Velocidade | Persistência | Quando usar |
|---|---|---|---|---|
MAIN | Memória principal (RAM) | Muito rápida | Perdida se CICS cair | Dados temporários pequenos, alta frequência de acesso |
AUXILIARY | Dataset em disco (DFHTEMP) | Mais lenta | Sobrevive a restart do CICS (se recovery ativo) | Volumes maiores, listas longas, dados que precisam de proteção |
Nomeando a fila TS
O nome da fila TS tem até 8 caracteres e é criado dinamicamente pelo programa. Para garantir unicidade por usuário, a convenção mais usada é concatenar o TERMID (identificador do terminal, 4 chars) com um sufixo fixo:
WORKING-STORAGE SECTION.
01 WS-QUEUE-NAME.
05 WS-TERMID PIC X(4).
05 WS-QUEUE-SUFIXO PIC X(4) VALUE 'CTLG'.
PROCEDURE DIVISION.
MOVE EIBTRMID TO WS-TERMID
* WS-QUEUE-NAME = 'T001CTLG' para o terminal T001
WRITEQ TS — gravando na fila
WRITEQ TS adiciona um novo item à fila ou reescreve um item existente. Cada item recebe um número sequencial (1, 2, 3...) retornado no parâmetro ITEM.
* gravar novo item no final da fila
EXEC CICS WRITEQ TS
QUEUE(WS-QUEUE-NAME)
FROM(WS-REGISTRO-CONTA)
LENGTH(LENGTH OF WS-REGISTRO-CONTA)
ITEM(WS-NUMERO-ITEM) * CICS retorna o número do item gravado
MAIN * ou AUXILIARY
RESP(WS-RESP)
END-EXEC
* reescrever um item já existente (REWRITE)
EXEC CICS WRITEQ TS
QUEUE(WS-QUEUE-NAME)
FROM(WS-REGISTRO-CONTA)
LENGTH(LENGTH OF WS-REGISTRO-CONTA)
ITEM(WS-NUMERO-ITEM)
REWRITE * sobrescreve o item de número WS-NUMERO-ITEM
RESP(WS-RESP)
END-EXEC
| Parâmetro | Obrigatório | Significado |
|---|---|---|
| QUEUE(nome) | Sim | Nome da fila TS (até 8 chars) |
| FROM(área) | Sim | Área de Working-Storage com os dados a gravar |
| LENGTH(n) | Sim | Comprimento do item em bytes |
| ITEM(var) | Não | CICS retorna o número do item gravado; obrigatório para REWRITE |
| MAIN / AUXILIARY | Não | Onde armazenar. Padrão depende da instalação (geralmente MAIN) |
| REWRITE | Não | Sobrescreve o item identificado por ITEM |
| NOSUSPEND | Não | Não suspende a tarefa se a fila estiver cheia — retorna NOSPACE |
READQ TS — lendo da fila
READQ TS lê um item da fila sem removê-lo. Você pode ler pelo número do item ou sequencialmente com NEXT.
* ler item específico pelo número
EXEC CICS READQ TS
QUEUE(WS-QUEUE-NAME)
INTO(WS-REGISTRO-CONTA)
LENGTH(WS-LENGTH)
ITEM(WS-NUMERO-ITEM) * item a ler
RESP(WS-RESP)
END-EXEC
* ler próximo item sequencialmente
EXEC CICS READQ TS
QUEUE(WS-QUEUE-NAME)
INTO(WS-REGISTRO-CONTA)
LENGTH(WS-LENGTH)
NEXT * avança ponteiro interno da fila
RESP(WS-RESP)
END-EXEC
* verificar conteúdo sem consumir (NUMITEMS = total de itens)
EXEC CICS READQ TS
QUEUE(WS-QUEUE-NAME)
INTO(WS-REGISTRO-CONTA)
LENGTH(WS-LENGTH)
ITEM(WS-NUMERO-ITEM)
NUMITEMS(WS-TOTAL-ITENS) * CICS retorna total de itens na fila
RESP(WS-RESP)
END-EXEC
| RESP retornado | Significado |
|---|---|
| DFHRESP(NORMAL) | Item lido com sucesso |
| DFHRESP(QIDERR) | Fila não existe (ainda não foi criada ou já foi deletada) |
| DFHRESP(ITEMERR) | Número de item inválido ou fora do intervalo |
| DFHRESP(ENDDATA) | NEXT: não há mais itens após o ponteiro atual |
| DFHRESP(LENGERR) | LENGTH menor que o item armazenado — dados truncados |
DELETEQ TS — limpando a fila
DELETEQ TS apaga a fila inteira de uma vez — todos os itens, sem possibilidade de desfazer. Sempre execute ao final do uso para liberar memória ou espaço em disco.
EXEC CICS DELETEQ TS
QUEUE(WS-QUEUE-NAME)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(QIDERR)
CONTINUE * fila não existia — não é erro crítico
END-IF
Padrão de paginação multi-tela com TS
O uso mais comum de TS é implementar listagens paginadas: o programa carrega todos os registros na fila na primeira execução, depois navega por elas com PF7 (página anterior) e PF8 (próxima página).
Estrutura da COMMAREA para paginação
01 WS-COMMAREA.
05 WS-FASE PIC X.
88 PRIMEIRA-VEZ VALUE 'I'.
88 PAGINANDO VALUE 'P'.
05 WS-PAG-ATUAL PIC S9(4) COMP VALUE 1.
05 WS-TOT-PAGINAS PIC S9(4) COMP.
05 WS-REGS-POR-PAG PIC S9(4) COMP VALUE 15.
05 WS-QUEUE-NAME PIC X(8).
Fluxo completo de paginação
PROCEDURE DIVISION.
INICIO.
IF EIBCALEN = 0
MOVE 'I' TO WS-FASE
ELSE
MOVE DFHCOMMAREA TO WS-COMMAREA
END-IF
EVALUATE TRUE
WHEN PRIMEIRA-VEZ
PERFORM CARREGAR-FILA-TS
PERFORM EXIBIR-PAGINA
WHEN PAGINANDO AND EIBAID = DFHPF8 * próxima página
IF WS-PAG-ATUAL < WS-TOT-PAGINAS
ADD 1 TO WS-PAG-ATUAL
END-IF
PERFORM EXIBIR-PAGINA
WHEN PAGINANDO AND EIBAID = DFHPF7 * página anterior
IF WS-PAG-ATUAL > 1
SUBTRACT 1 FROM WS-PAG-ATUAL
END-IF
PERFORM EXIBIR-PAGINA
WHEN EIBAID = DFHPF3 * sair — limpar fila
EXEC CICS DELETEQ TS
QUEUE(WS-QUEUE-NAME)
RESP(WS-RESP)
END-EXEC
EXEC CICS XCTL PROGRAM('PGMMENU') END-EXEC
END-EVALUATE
EXEC CICS RETURN
TRANSID(EIBTRNID)
COMMAREA(WS-COMMAREA)
LENGTH(LENGTH OF WS-COMMAREA)
END-EXEC.
CARREGAR-FILA-TS.
* montar nome único de fila
MOVE EIBTRMID TO WS-TERMID
MOVE 'LIST' TO WS-QUEUE-SUFIXO
* garantir que fila anterior foi limpa
EXEC CICS DELETEQ TS
QUEUE(WS-QUEUE-NAME)
RESP(WS-RESP)
END-EXEC
* ler todos os registros do VSAM/DB2 e gravar na fila
EXEC CICS STARTBR FILE('CNTFILE')
RIDFLD(WS-CHAVE-INICIO)
RESP(WS-RESP)
END-EXEC
MOVE 0 TO WS-TOTAL-REGISTROS
PERFORM UNTIL WS-RESP NOT = DFHRESP(NORMAL)
EXEC CICS READNEXT FILE('CNTFILE')
INTO(WS-REG-CONTA)
RIDFLD(WS-CHAVE)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(NORMAL)
EXEC CICS WRITEQ TS
QUEUE(WS-QUEUE-NAME)
FROM(WS-REG-CONTA)
LENGTH(LENGTH OF WS-REG-CONTA)
AUXILIARY
RESP(WS-RESP2)
END-EXEC
ADD 1 TO WS-TOTAL-REGISTROS
END-IF
END-PERFORM
EXEC CICS ENDBR FILE('CNTFILE') END-EXEC
* calcular total de páginas
COMPUTE WS-TOT-PAGINAS =
(WS-TOTAL-REGISTROS + WS-REGS-POR-PAG - 1)
/ WS-REGS-POR-PAG
MOVE 1 TO WS-PAG-ATUAL
MOVE 'P' TO WS-FASE.
EXIBIR-PAGINA.
* calcular primeiro e último item desta página
COMPUTE WS-ITEM-INICIO =
(WS-PAG-ATUAL - 1) * WS-REGS-POR-PAG + 1
COMPUTE WS-ITEM-FIM =
WS-PAG-ATUAL * WS-REGS-POR-PAG
MOVE LOW-VALUES TO MAPLISTO
PERFORM VARYING WS-ITEM FROM WS-ITEM-INICIO BY 1
UNTIL WS-ITEM > WS-ITEM-FIM
EXEC CICS READQ TS
QUEUE(WS-QUEUE-NAME)
INTO(WS-REG-CONTA)
LENGTH(WS-LENGTH)
ITEM(WS-ITEM)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(NORMAL)
PERFORM MOVER-PARA-LINHA-TELA
END-IF
END-PERFORM
EXEC CICS SEND MAP('MAPLISTA')
MAPSET('BANCMAP2')
FROM(MAPLISTO)
ERASE
END-EXEC.
Transient Data — intrapartição e extrapartição
As filas TD são definidas previamente no CSD (Control Section Dataset) e têm dois tipos:
| Tipo | Onde grava | Quem lê | Uso típico |
|---|---|---|---|
| Intrapartição | Dataset interno do CICS (DFHINTRA) | Outra transação CICS (via READQ TD ou trigger) | Comunicação entre transações CICS, filas de impressão interna, log de eventos |
| Extrapartição | Dataset externo (qualquer QSAM ou JES SPOOL) | Jobs batch fora do CICS, impressoras, arquivos de saída | Gerar relatórios, enviar dados para processamento batch, saída em SYSOUT |
Definição de fila TD no CSD
Antes de usar uma fila TD, ela precisa ser definida com CEDA:
* definição de fila intrapartição (via CEDA ou DFHCSDUP)
DEFINE TDQUEUE(PRNT)
GROUP(BANCGRP)
TYPE(INTRA)
TRIGGERLEVEL(1) * dispara transação após 1 item na fila
TRANSID(PRNTRANS) * transação disparada automaticamente
* definição de fila extrapartição
DEFINE TDQUEUE(RELAT)
GROUP(BANCGRP)
TYPE(EXTRA)
DDNAME(RELDDIN) * DD do JCL do CICS que aponta para o dataset
OPENTIME(INITIAL) * abre o arquivo quando o CICS inicia
DISPOSITION(MOD) * modo de abertura do dataset
WRITEQ TD — gravando na fila TD
WRITEQ TD sempre adiciona um registro ao final da fila. Não há número de item — é puramente sequencial.
WORKING-STORAGE SECTION.
01 WS-LOG-RECORD.
05 WS-LOG-DATA PIC X(10).
05 WS-LOG-HORA PIC X(8).
05 WS-LOG-USERID PIC X(8).
05 WS-LOG-TRANSID PIC X(4).
05 WS-LOG-MENSAGEM PIC X(50).
PROCEDURE DIVISION.
GRAVAR-LOG.
MOVE CURRENT-DATE(1:10) TO WS-LOG-DATA
MOVE CURRENT-DATE(12:8) TO WS-LOG-HORA
EXEC CICS ASSIGN USERID(WS-LOG-USERID) END-EXEC
MOVE EIBTRNID TO WS-LOG-TRANSID
MOVE 'TRANSFERENCIA EFETUADA' TO WS-LOG-MENSAGEM
EXEC CICS WRITEQ TD
QUEUE('ALOG') * nome de 4 chars para TD
FROM(WS-LOG-RECORD)
LENGTH(LENGTH OF WS-LOG-RECORD)
RESP(WS-RESP)
END-EXEC
IF WS-RESP NOT = DFHRESP(NORMAL)
DISPLAY 'ERRO AO GRAVAR LOG TD: ' WS-RESP
END-IF.
CSML (log de mensagens do sistema), CSSL (log de estatísticas), CSDL (log de deadlocks). Evite esses prefixos em filas de aplicação.
READQ TD — lendo da fila TD
READQ TD lê e remove o primeiro registro da fila (FIFO). Não é possível reler um registro já lido — ele desaparece após a leitura.
PROCEDURE DIVISION.
LER-FILA-IMPRESSAO.
PERFORM UNTIL WS-RESP NOT = DFHRESP(NORMAL)
EXEC CICS READQ TD
QUEUE('PRNT')
INTO(WS-LINHA-IMPRESSAO)
LENGTH(WS-LENGTH)
RESP(WS-RESP)
END-EXEC
EVALUATE WS-RESP
WHEN DFHRESP(NORMAL)
PERFORM IMPRIMIR-LINHA
WHEN DFHRESP(QZERO)
CONTINUE * fila vazia — fim do PERFORM
WHEN OTHER
DISPLAY 'ERRO READQ TD: ' WS-RESP
END-EVALUATE
END-PERFORM.
| RESP retornado | Significado |
|---|---|
| DFHRESP(NORMAL) | Registro lido e removido da fila |
| DFHRESP(QZERO) | Fila vazia — não há mais registros |
| DFHRESP(QIDERR) | Fila não existe (nome inválido ou não definida no CSD) |
| DFHRESP(LENGERR) | Registro maior que a área INTO — dados truncados |
TDQUEUE com trigger — disparando transações
Filas TD intrapartição podem ter um TRIGGERLEVEL — quando o número de registros na fila atinge esse valor, o CICS automaticamente dispara a transação definida em TRANSID. Isso permite construir pipelines de processamento assíncrono dentro do CICS.
* definição da fila com trigger
DEFINE TDQUEUE(PEND)
GROUP(BANCGRP)
TYPE(INTRA)
TRIGGERLEVEL(10) * dispara quando acumular 10 registros
TRANSID(BTCH) * transação BTCH é iniciada automaticamente
USERID(BTCHUSER) * usuário sob o qual a transação roda
A transação disparada pelo trigger lê a fila com READQ TD e processa os registros. Ela termina quando a fila fica vazia (QZERO). Se mais registros chegarem enquanto ela está rodando, o trigger não é acionado novamente até que a transação termine e o nível seja atingido de novo.
* transação BTCH — disparada pelo trigger da fila PEND
PROCEDURE DIVISION.
INICIO.
PERFORM UNTIL WS-FILA-VAZIA
EXEC CICS READQ TD
QUEUE('PEND')
INTO(WS-PEDIDO)
LENGTH(WS-LENGTH)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(QZERO)
SET WS-FILA-VAZIA TO TRUE
ELSE IF WS-RESP = DFHRESP(NORMAL)
PERFORM PROCESSAR-PEDIDO
END-IF
END-PERFORM
EXEC CICS RETURN END-EXEC.
Boas práticas e armadilhas comuns
Temporary Storage
| Prática | Por quê |
|---|---|
| Sempre inclua EIBTRMID no nome da fila | Evita colisão de nomes entre usuários diferentes |
| Faça DELETEQ TS ao sair da transação (PF3, CLEAR, timeout) | Evita acúmulo de filas órfãs consumindo memória |
| Use AUXILIARY para listas longas (> 50 registros) | MAIN consome memória do region CICS; AUXILIARY vai para disco |
| Verifique QIDERR antes de READQ TS na primeira tela | Fila pode não existir se a transação foi iniciada sem dados |
| Grave o tamanho do item na COMMAREA | READQ TS pode truncar se LENGTH for menor que o gravado |
Transient Data
| Prática | Por quê |
|---|---|
| Nomes de fila TD têm exatamente 4 chars | Nomes menores causam QIDERR; maiores são truncados |
| Trate QZERO como condição normal, não como erro | Fila vazia é o sinal de fim de leitura sequencial |
| Não use TD para dados críticos sem recovery configurado | Registros em DFHINTRA podem ser perdidos se CICS cair sem journal |
| Para extrapartição, defina OPENTIME=INITIAL no CSD | Abre o dataset quando o CICS sobe — evita falha no primeiro WRITEQ |
Armadilha clássica: fila TS não deletada
* ERRADO: programa termina sem limpar a fila
SAIR.
EXEC CICS XCTL PROGRAM('PGMMENU') END-EXEC.
* a fila WS-QUEUE-NAME persiste no CICS consumindo recursos!
* CORRETO: limpar antes de sair
SAIR.
EXEC CICS DELETEQ TS
QUEUE(WS-QUEUE-NAME)
RESP(WS-RESP)
END-EXEC
EXEC CICS XCTL PROGRAM('PGMMENU') END-EXEC.
Armadilha clássica: LENGTH errado no READQ TS
* item gravado com 200 bytes
EXEC CICS WRITEQ TS
QUEUE(WS-QUEUE-NAME)
FROM(WS-REGISTRO)
LENGTH(200)
RESP(WS-RESP)
END-EXEC
* ERRADO: lendo com 100 bytes — dados truncados, RESP = LENGERR
EXEC CICS READQ TS
QUEUE(WS-QUEUE-NAME)
INTO(WS-REGISTRO)
LENGTH(100) * menor que o gravado!
ITEM(1)
RESP(WS-RESP)
END-EXEC
* CORRETO: usar o mesmo tamanho do item gravado
EXEC CICS READQ TS
QUEUE(WS-QUEUE-NAME)
INTO(WS-REGISTRO)
LENGTH(200)
ITEM(1)
RESP(WS-RESP)
END-EXEC