Pular para conteúdo

0002 — Multi-fluxo: N Processador em série num par de arquivos único

Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-07-30 · Decidido em: 2026-07-30

Contexto

O integrador-client é o cliente coringa da plataforma: o mesmo binário envia dados de várias origens ao X-Adm, cada origem um fluxo (zim/Fluxo.java) com suas tabelas-espelho e seu literal de handshake. Até esta decisão o Main fixava um único Fluxo.PIED em código — não havia como uma instalação rodar mais de um fluxo (ex. onpetro e vantroba passarão a rodar abastecimento + MDF-e juntos). Invariante de deploy: 1 integrador-server ↔ 1 integrador-client por cliente; um cliente multi-fluxo roda um fluxo por vez no par de arquivos único (dXpEnvio/dXpRetorno) — o handshake por arquivos e a TravaInstancia (uma trava por pasta de dados) proíbem concorrência e instâncias separadas na mesma pasta.

Decisão

N Processador (um por fluxo ativo) + um scheduler serial de thread única (processo/Agendador), no par de arquivos único. Peças:

  • Config zim.fluxos — lista de chaves de fluxo ativas, obrigatória e sem default (ausente/ vazio = exit 2 com a lista de válidos; a instalação declara o que roda). Resolvida na largada via registry Fluxo.porChave (constantes = fonte da verdade; a config só as nomeia); chave inválida = exit 2.
  • Agendador dono do laço de vida (o laço saiu do Processador, que virou só o miolo de UM fluxo): a cada tick — ou quando o gatilho do PowerSync acorda — roda executarCiclo() de cada fluxo em série, na ordem da config. A falha de um fluxo é isolada (não afoga os demais).
  • Guarda fluxo×schema na largada — fluxo ativo cuja tabela não está no SchemaTabelas derruba com exit 2 (impede subir "pela metade").
  • Fluxos seguem o MODELO COMUM. Adicionar um fluxo é, no geral, uma constante + um layout no ArquivoEnvio + espelhos no schema. O ENCERRA_MDFE (tabela única mdf) foi implementado assim: case "mdf" no ArquivoEnvio (contador + tipo + chave_nota), retorno com o mesmo layout do pied. Exceções (só no retorno): o MDF-e casa por contador (Fluxo.getRetornoCasaPorContador()) — tolera vir fora de ordem (o X-Adm reordena por filial) e parcial (o ZIM aborta num conflito; o Processador restaura as posições não cobertas para retentar); e um 1XX dispara alerta GlitchTip (getAlertaNoErroTerminal()). Ver contrato §4.

Consequências

  • Um lote (um dXpEnvio) carrega somente as tabelas de UM fluxo; a serialização garante que um lote é escrito, processado, lido e apagado por completo antes do próximo — sem colisão.
  • Preserva o invariante "uma thread só, sem concorrência" (legibilidade é a prioridade do repo).
  • O modo debug ganhou um seletor de fluxo (só com >1 ativo); com um fluxo só, console intacto.
  • Co-execução real de dois fluxos é provada em integração (SQLite temp + pAbast fake); o e2e cobre no-regression do pied (o espelho abastecimento/mdf ainda não existe no servidor).

Alternativas consideradas

  • Diretório/par de arquivos por fluxo — exigiria mudança no lado ZIM ("programa intacto" a proíbe); descartada.
  • Multi-thread (um fluxo por thread) — quebra a legibilidade sem concorrência que é a prioridade do repo; descartada.
  • Instâncias separadas por fluxo na mesma pasta — a TravaInstancia (uma por pasta) e o par de arquivos único as impediriam de conviver; multi-fluxo numa instância é a única forma coerente.
  • Formato de arquivo próprio para o MDF-e (linha só com a ChaveNota, retorno textual OK/ERRO, casamento por chave) — chegou a ser implementado a partir da amostra crua do ZIM, mas foi colapsado no modelo do pied: o ZIM alinha o lado dele e o cliente reusa a máquina existente (menos código, menos superfície de erro). Sobraram divergências mínimas só no retorno (casa por contador — fora de ordem/parcial — e alerta no 1XX), flags no Fluxo — não uma máquina paralela. A amostra crua fica como referência.