fix(ci): tetos de tempo nos serviços efêmeros e por passo no job backend [INFRA-68352] #39

Merged
tarcisio merged 1 commit from fix/INFRA-68352-timeouts-servicos-efemeros into main 2026-10-04 11:26:31 -03:00
Owner

⚠️ Leia antes de aprovar

  1. Muda o ci.yml COMPARTILHADO (job backend), então afeta todos os produtos que usam o CI único, não só o DANI.
  2. O timeout-minutes por passo foi conferido só por LEITURA do código do runner, NÃO executado. Nenhum workflow da frota usava a chave em passo. No forgejo/runner v12.12.0 o campo existe (Step.TimeoutMinutes) e é aplicado (context.WithTimeout), mas não rodei isso num Forgejo real. Um erro aqui afetaria todos os produtos (precedente: INFRA-64944).
  3. Os orçamentos por passo são ESTIMATIVAS generosas, não medições. Não há medição por passo (o log do job não é legível pela API). O job normal leva 9 a 17 min no total. Devem ser ajustados com o log real.
  4. A causa NÃO está confirmada. O que está provado é um ponto sem limite no código (curl sem --max-time) e um padrão de travamento intermitente que ele explicaria. Qual passo travou de fato só se sabe lendo o log de uma execução que estourou (tarcisio/DANI/actions/runs/3122), que ainda não foi lido.

Recomendação ao operador: validar primeiro num produto. Depois do merge, repetir (re-run) um PR do DANI e olhar o passo a passo do novo log (cada passo agora aparece com o seu tempo e teto) antes de seguir para os outros produtos.

Problema

O job CI / backend (pull_request) do DANI estoura os 30 min de forma intermitente (≥ 8 em ~130 execuções, ~6%), e o mesmo commit passa em 9 a 11 min quando repetido. Não é fila de runner (três dos seis estouros visíveis tiveram 1 a 3 runs ativos; a duração não correlaciona com a carga, r = −0,01) nem teste que trava (o pytest.ini tem timeout = 120 e o pytest-timeout está instalado). Detalhes e números: INFRA-68352.

O ponto sem limite verificado: api() { curl -s ... } do servicos-efemeros.sh, sem --max-time, usado no pull das imagens de Postgres e Redis e no create/start dos contêineres pelo daemon do host. Um daemon que emperra pendurava o passo até o teto do job.

O que muda

scripts/servicos-efemeros.sh

  • api(): -sS, --connect-timeout 10, --max-time 60. api_pull(): --max-time 600 e aborta se ficar 90 s sem receber um byte.
  • Sonda de vida do daemon (/_ping, 15 s) no início do up: daemon mudo ou socket ausente falha em segundos, com ::error:: dizendo o curl rc (28 = sem resposta, 7 = socket ausente) e o socket, sem criar nada.
  • Pull que falha vira ::warning:: com o código; falha de curl no create/start não aborta mais calada.
  • Tetos e socket ajustáveis por variável de ambiente (0 desliga), usados nos testes.

.forgejo/workflows/ci.yml (só o job backend) — timeout-minutes por passo: checkout 5, Infisical 5, PyPI 3, ci-shared 5, serviços 10, dependências 15, pip-audit 10, pré-teste 5, testes 25, smoke 10, derrubar 3. O teto do job continua 30. Os jobs frontend, gates e changes não foram alterados (há teste).

Prova

Daemon falso em socket Unix que aceita a conexão e nunca responde:

  • Daemon mudo: o up falha em menos de 10 s, com a mensagem.
  • Controle: o mesmo daemon com os limites em 0 faz o script pendurar.
  • Controle com o script antigo da main (só trocando o socket): pendura nos dois cenários; no de pull travado para exatamente depois de imprimir o env do Postgres, ou seja, no pull.
  • Daemon saudável passa; sem socket falha rápido (rc 7); pull travado aborta por falta de progresso e diz o código.
  • 12 testes novos. tests/ci-config 88 OK, ci-unico 39, infisical-secrets 9.

Limites

  • O status do commit continua com o texto do job ("Failing after Xm"). Quem mostra qual passo travou é o log e a marcação do passo na UI, não a descrição do status.
  • O teto do passo "Serviços efêmeros" (10 min) é o limite efetivo de todas as tentativas de pull juntas (3 de até 600 s não cabem nele).
  • Se o log mostrar que o passo que trava é outro que não o pull/create/start, esta mudança continua útil (o passo falha cedo e fica identificado), mas o conserto da causa será outro.

Refs: INFRA-68352 (investigação e correção), INFRA-68349 (build da imagem de QA do DANI, outro job).

🤖 Generated with Claude Code

https://claude.ai/code/session_01UdMSC8CGmShbesE5rfVNfe

## ⚠️ Leia antes de aprovar 1. **Muda o `ci.yml` COMPARTILHADO** (job `backend`), então **afeta todos os produtos** que usam o CI único, não só o DANI. 2. **O `timeout-minutes` por passo foi conferido só por LEITURA do código do runner, NÃO executado.** Nenhum workflow da frota usava a chave em passo. No `forgejo/runner` v12.12.0 o campo existe (`Step.TimeoutMinutes`) e é aplicado (`context.WithTimeout`), mas não rodei isso num Forgejo real. Um erro aqui afetaria todos os produtos (precedente: INFRA-64944). 3. **Os orçamentos por passo são ESTIMATIVAS generosas, não medições.** Não há medição por passo (o log do job não é legível pela API). O job normal leva 9 a 17 min no total. Devem ser ajustados com o log real. 4. **A causa NÃO está confirmada.** O que está provado é um ponto **sem limite** no código (`curl` sem `--max-time`) e um padrão de travamento intermitente que ele explicaria. Qual passo travou de fato só se sabe lendo o log de uma execução que estourou (`tarcisio/DANI/actions/runs/3122`), que ainda não foi lido. **Recomendação ao operador: validar primeiro num produto.** Depois do merge, **repetir (re-run) um PR do DANI** e olhar o passo a passo do novo log (cada passo agora aparece com o seu tempo e teto) **antes** de seguir para os outros produtos. ## Problema O job `CI / backend (pull_request)` do DANI estoura os 30 min de forma **intermitente** (≥ 8 em ~130 execuções, ~6%), e o **mesmo commit passa em 9 a 11 min quando repetido**. Não é fila de runner (três dos seis estouros visíveis tiveram 1 a 3 runs ativos; a duração não correlaciona com a carga, r = −0,01) nem teste que trava (o `pytest.ini` tem `timeout = 120` e o `pytest-timeout` está instalado). Detalhes e números: INFRA-68352. O ponto sem limite verificado: `api() { curl -s ... }` do `servicos-efemeros.sh`, **sem `--max-time`**, usado no `pull` das imagens de Postgres e Redis e no `create`/`start` dos contêineres pelo daemon do host. Um daemon que emperra pendurava o passo até o teto do job. ## O que muda **`scripts/servicos-efemeros.sh`** - `api()`: `-sS`, `--connect-timeout 10`, `--max-time 60`. `api_pull()`: `--max-time 600` e aborta se ficar 90 s sem receber um byte. - Sonda de vida do daemon (`/_ping`, 15 s) no início do `up`: daemon mudo ou socket ausente **falha em segundos**, com `::error::` dizendo o `curl rc` (28 = sem resposta, 7 = socket ausente) e o socket, sem criar nada. - Pull que falha vira `::warning::` com o código; falha de `curl` no `create`/`start` não aborta mais calada. - Tetos e socket ajustáveis por variável de ambiente (`0` desliga), usados nos testes. **`.forgejo/workflows/ci.yml` (só o job `backend`)** — `timeout-minutes` por passo: checkout 5, Infisical 5, PyPI 3, ci-shared 5, serviços 10, dependências 15, pip-audit 10, pré-teste 5, testes 25, smoke 10, derrubar 3. O teto do job continua **30**. Os jobs `frontend`, `gates` e `changes` **não** foram alterados (há teste). ## Prova Daemon **falso** em socket Unix que aceita a conexão e nunca responde: - **Daemon mudo:** o `up` falha em **menos de 10 s**, com a mensagem. - **Controle:** o **mesmo** daemon com os limites em `0` faz o script **pendurar**. - **Controle com o script antigo da `main`** (só trocando o socket): **pendura nos dois cenários**; no de pull travado para exatamente depois de imprimir o `env` do Postgres, ou seja, no `pull`. - Daemon saudável passa; sem socket falha rápido (`rc 7`); pull travado aborta por falta de progresso e diz o código. - 12 testes novos. `tests/ci-config` 88 OK, `ci-unico` 39, `infisical-secrets` 9. ## Limites - O **status do commit** continua com o texto do job ("Failing after Xm"). Quem mostra **qual passo** travou é o **log e a marcação do passo na UI**, não a descrição do status. - O teto do passo "Serviços efêmeros" (10 min) é o **limite efetivo** de todas as tentativas de pull juntas (3 de até 600 s não cabem nele). - Se o log mostrar que o passo que trava é **outro** que não o pull/create/start, esta mudança continua útil (o passo falha cedo e fica identificado), mas **o conserto da causa será outro**. Refs: INFRA-68352 (investigação e correção), INFRA-68349 (build da imagem de QA do DANI, outro job). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01UdMSC8CGmShbesE5rfVNfe
fix(ci): tetos de tempo nos servicos efemeros e por passo no job backend [INFRA-68352]
All checks were successful
PR checks (ci-shared) / testes (pull_request) Successful in 2m32s
07b52b53d0
O job backend do DANI estourava os 30 min de forma INTERMITENTE em ~6% das execucoes, e o MESMO
commit passava em 9-11 min quando repetido; nao era fila (3 dos 6 estouros com 1-3 runs ativos) nem
teste (pytest-timeout=120 instalado). Ponto sem limite verificado: api() { curl -s ... } do
servicos-efemeros.sh, sem --max-time, usado no pull e no create/start dos conteineres pelo daemon.

1) servicos-efemeros.sh
   - api(): -sS, --connect-timeout 10 e --max-time 60; api_pull(): --max-time 600 e aborta sem
     progresso por 90 s (--speed-limit 1 --speed-time 90).
   - sonda de vida do daemon (/_ping, 15 s) no inicio do up: daemon mudo ou socket ausente falha
     em segundos com ::error:: dizendo o curl rc (28/7) e o socket, sem criar nada.
   - pull que falha vira :⚠️: com o rc; create/start com falha de curl nao abortam calados.
   - tetos e o socket ajustaveis por env (TMF_DOCKER_SOCK, TMF_API_*, TMF_PING_MAX_TIME,
     TMF_PULL_*, TMF_RETRY_SLEEP; 0 desliga). /etc/hostname com fallback p/ hostname.
2) ci.yml, SO o job backend: timeout-minutes por passo (checkout 5, Infisical 5, PyPI 3, ci-shared 5,
   servicos 10, dependencias 15, pip-audit 10, pre-test 5, testes 25, smoke 10, derrubar 3); o do job
   continua 30. Orcamentos generosos e ESTIMADOS: nao ha medicao por passo (log ilegivel pela API).
   O campo e lido e aplicado pelo runner (forgejo/runner v12.12.0: Step.TimeoutMinutes ->
   context.WithTimeout), conferido no codigo-fonte, nao executado.

Prova: daemon FALSO em socket Unix. Mudo: o up falha em <10 s com mensagem; CONTROLE com os limites
em 0 o mesmo script pendura; daemon saudavel passa; sem socket falha rapido (rc 7); pull mudo aborta
por falta de progresso. O script ANTIGO da main contra o mesmo daemon mudo PENDURA (morto aos 10 s)
nos dois cenarios. 12 testes novos; ci-config 88 OK, ci-unico 39, infisical-secrets 9.

Limite: o status do commit continua com o texto do JOB ('Failing after Xm'); quem mostra QUAL passo
travou e o log/marcacao do passo na UI. Causa nao confirmada: o log do run que estourou nao foi lido.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UdMSC8CGmShbesE5rfVNfe
tarcisio deleted branch fix/INFRA-68352-timeouts-servicos-efemeros 2026-10-04 11:26:31 -03:00
Sign in to join this conversation.
No reviewers
No labels
seguranca
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
TMF/ci-shared!39
No description provided.