Skip to content

Encurtar a janela de recuperação: recuperação reativa no proxy + listener de eventos Docker (follow-up #1015/#1016) #1018

Description

@milkway

Contexto

O #1017 (fecha #1015 e #1016) tornou a reconciliação de liveness do scaler
autoritativa e bidirecional: classifica réplicas conhecidas com autoridade por
host (Missing vs Unknown), poda Missing/Stopped-pós-graça por uma rotina de
limpeza única, e readota um container reiniciado externamente após readiness (com
janela de graça que evita spawn duplicado). O caminho de recuperação é o reconcile
periódico do scaler (tick de ~10 s)
.

As duas otimizações marcadas como opcionais nos issues #1015 e #1016 ficaram
deliberadamente fora do #1017 para manter a fatia mergeável. Este issue as
rastreia. Ambas apenas encurtam a janela de recuperação; nenhuma substitui o
reconcile periódico, que continua sendo o fallback.

Fatia A — recuperação reativa no caminho do proxy

Hoje, entre o docker rm -f/docker restart e o próximo tick, uma requisição
roteada para a réplica agora inalcançável recebe 502 upstream error. O reconcile
periódico limpa em ≤10 s, mas o usuário vê o 502 nesse intervalo.

Proposta: em falha de transporte (erro de conexão HTTP, ou falha ao abrir o
WebSocket upstream) contra a réplica selecionada:

  1. Confirmar o estado real no backend via ContainerBackend::replica_liveness
    (apenas para a réplica em questão).
  2. Se o backend confirmar Stopped/Missing (autoritativo), executar a mesma
    cleanup_replica_state do scaler e invalidar o vínculo sticky (expirar o cookie
    __ruscker_session_{spec}).
  3. Disparar o spawn coalescido para a spec (a infra state.spawn_locks já existe).
  4. Replay apenas de requisições seguramente reproduzíveis (GET/HEAD, sem
    corpo já enviado). Para métodos não idempotentes: não repetir cegamente —
    limpar a réplica para que a próxima chamada se recupere e devolver um erro
    explícito.

Não podar por um 5xx da aplicação nem por um erro transitório do daemon
Docker — a poda reativa exige confirmação autoritativa Stopped/Missing
(reusar exatamente a distinção Missing vs Unknown do #1017; um host Unknown
nunca poda).

Cuidados: é o hot path — a confirmação no backend deve ser barata (uma réplica),
fora de qualquer lock de registro, e o replay deve ter limite (uma tentativa) para
não amplificar carga. WebSocket já conecta o upstream antes do 101
(ws::connect), então a falha de conexão WS é o ponto natural de recuperação.

Critérios de aceite (A)

  • Um GET para uma spec cuja réplica sofreu docker rm -f recupera na mesma
    requisição (sem esperar o tick), sem 502 persistente.
  • O sticky antigo é invalidado; um novo binding é criado para a réplica nova.
  • Métodos não idempotentes não sofrem replay cego; retornam erro explícito
    após limpar a réplica.
  • Um 5xx da aplicação ou um erro transitório do daemon não poda a réplica.
  • Multi-host: um host Unknown (timeout) na confirmação reativa não poda.

Fatia B — listener de eventos Docker (recuperação quase imediata)

Proposta: assinar o stream de eventos do daemon (die/destroy/stop/start),
filtrado pelos labels do Ruscker, e disparar a mesma reconciliação/limpeza sem
esperar o tick:

  • die/stop/restart → tirar a réplica do roteamento (equivalente a
    mark_restarting).
  • start → reinspecionar binding + readiness + readotar ao pool.
  • destroy → poda definitiva + limpeza de sessões.
  • O listener deve reconectar ao stream em caso de queda; o reconcile periódico
    permanece como fallback para eventos perdidos durante desconexões/restarts do
    Ruscker.

Escopo: implementar primeiro no LocalDockerBackend (um daemon). Multi-host = um
stream por host, com um host indisponível apenas degradando para o fallback
periódico daquele host (nunca podando por ausência de eventos). Pode ficar atrás de
um flag de config inicialmente.

Critérios de aceite (B)

  • Remoção/parada externa de um container Ruscker é refletida no registro em
    ~1 s (não ~10 s), sem interface.
  • O listener reconecta após uma queda do stream; nenhuma réplica é podada por
    "silêncio" de eventos.
  • Multi-host: a indisponibilidade do stream de um host degrada para o reconcile
    periódico, sem poda nem duplicação.

Notas

  • Fatias independentes e mergeáveis separadamente (A entrega o ganho visível ao
    usuário; B reduce a latência de fundo). Sugиро A primeiro.
  • Testes: unit para a decisão de recuperação reativa (transport-fail + Missing
    confirmado → limpa sticky/registro + inicia recuperação; 5xx da app → nada);
    docker-it para o listener de eventos (spawn, docker rm -f, asserir limpeza em
    ~1 s).

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:adminruscker-admin cratearea:dockerruscker-docker cratearea:proxyruscker-proxy cratephase:3Phase 3 — Proxy + Docker backend

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions