Skip to content

[contracts] CA-I2 do worker de outbox é intermitente — exige pendência zero de um consumidor num cenário de dois #992

Description

@lekadecastro

🎯 Problema (uma frase, sem ambiguidade)

O caso CA-I2 do worker de outbox falha de forma intermitente porque exige pendência ZERO para um único consumidor num cenário com dois consumidores — asserção que deixou de fazer sentido quando a pendência virou por consumidor (#800/#824, ADR-0064).

📍 Localização

🔍 Atual vs. Esperado

Descrição
Hoje (atual) O caso passa ou falha conforme o SKIP LOCKED reparta os 6 eventos: só passa quando o worker-1 fica com todos.
Esperado O caso é determinístico e afirma o invariante real — repartição sem duplicação —, independente de quem pegou o quê.

🧠 Por quê (causa-raiz + impacto)

🔁 Reprodução / evidência

Medido em 06/09/2026, MySQL 8.4 via runner de integração (-p core-api-test, MYSQL_PORT=3307):

pnpm run test:integration        # suíte contracts, 102 casos

topo da pilha do PR #983:  FALHA · FALHA · passa
origin/dev (baseline):     passa · FALHA · passa

Mensagem da falha:

✖ CA-I2: 2 workers paralelos não duplicam delivery (FOR UPDATE SKIP LOCKED)
  AssertionError: não deve restar eventos pendentes
  6 !== 0        <- o worker-2 levou os 6; para o worker-1 os 6 seguem pendentes, corretamente

⚠️ Não é regressão do PR #983 — o diff daquele PR está confinado a src/modules/financial/** e não toca contracts. A falha reproduz na dev limpa.

✅ Critérios de aceite (testáveis)

  • CA1 — Dado o cenário do CA-I2 (6 eventos, dois workers com consumer ids distintos), Quando a suíte rodar 10 vezes seguidas, Então passa nas 10 — sem depender de qual worker venceu a disputa.
  • CA2 — Dado o mesmo cenário, Quando a asserção final for reescrita, Então ela afirma o invariante real: a UNIÃO dos entregues é 6, sem interseção entre os dois workers (o que as linhas 170-181 já fazem), e a pendência residual é avaliada para os dois consumidores juntos, não para um só.
  • CA3 — Dado um cenário em que o worker-2 processa TODOS os 6, Quando o caso rodar, Então ele passa — hoje é exatamente esse o caminho que falha.
  • CA4 — Dado o CA-I1 (um consumidor só), Quando a mudança for aplicada, Então ele continua exigindo pendência zero para consumer-i1 — a semântica por consumidor não pode ser afrouxada onde ela é legítima.

🧪 Definition of Done

  • Teste(s) cobrindo os CAs, com a repetição do CA1 demonstrada no PR.
  • Gate verde: pnpm run typecheck + pnpm run format:check + pnpm run lint + pnpm test, mais pnpm run test:integration rodado em repetição.
  • Sem regressão — contagem de testes ≥ baseline.
  • Não afrouxar a asserção para "passar": a repartição sem duplicação continua sendo o que o caso existe para provar (ver .claude/rules/testing.md, "Não afrouxe o teste para 'passar'").

🏷️ Classificação

  • Tipo: bug
  • Severidade: média
  • Tamanho estimado: S
  • dedup-key: contracts:worker:outbox-ca-i2-pendencia-por-consumidor

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions