O que a gente queria era muito simples.
Escrever um artigo novo e publicar o mais rápido possível.
Só isso.
Mas, sem perceber, antes de publicar já tinha começado uma reunião.
De quem é a vez desta rodada? Do Workers ou do Actions? Ainda tem orçamento? A trava anterior ainda está valendo? De quem é a marcação? Quantas vezes já tentamos de novo?
O artigo está esperando.
O sistema também está esperando.
Todo mundo acha que "isso não é comigo".
Numa empresa, dá para chamar todo mundo para a sala de reunião. Um sistema distribuído simplesmente para, em silêncio.
Esse é o inferno do Live Schedule.
1. A gente queria publicar na hora e acabou fazendo eleição para escolher o responsável
Quando você tenta deixar o processo de publicação mais forte, é natural ir colocando mais proteções.
Se o Workers parar, passa para o Actions. Se o orçamento do Actions estiver apertado, passa para o Workers. Coloca uma trava para ninguém fazer o mesmo trabalho duas vezes. Coloca uma marcação para dois processos não mexerem na mesma tarefa. Conta as novas tentativas. Coloca alguém para vigiar.
Olhando uma por uma, todas fazem sentido.
O problema aparece quando você coloca todas em fila.
Antes de "dá para publicar este artigo?", surge uma pergunta bem maior: "quem tem permissão para publicar?".
O mais perigoso é quando só a etiqueta do responsável sobrevive.
O processo do Workers parou. Mas o registro ainda diz "responsável: Workers". Quando o Actions acorda, lê "responsável: Workers" e pula a tarefa.
Nada está quebrado. Ninguém está trabalhando.
É a versão de sistema distribuído de "a pessoa responsável saiu da mesa".
2. A falha era de coordenação, não de capacidade de executar
Numa das execuções, o Actions ainda tinha capacidade e orçamento.
Mesmo assim, o trabalho principal não rodou.
O motivo é simples: a verificação concluiu que "essa tarefa é do Workers".
Aqui está o ponto importante.
Não faltava CPU. Nem todas as APIs tinham caído. O Actions conseguia iniciar normalmente.
O problema é a coordenação, não a execução.
O que trava é a decisão de quem faz, não a força para fazer.
Assim, consertar bugs isolados não resolve de vez.
Você corrige a quantidade de dados buscados. Corrige a falha ao salvar na main. Corrige as novas tentativas. Corrige a troca de responsável. Aí sobra uma marcação antiga. Depois sobra uma trava antiga. O vigia percebe o problema, mas não tem permissão para publicar.
Parece o jogo de acertar toupeiras, mas todas saem do mesmo buraco.
A camada de controle é inteligente demais.
3. Schedule é um relógio, não uma lista de tarefas
O schedule do GitHub Actions é prático, mas a documentação oficial diz que ele pode atrasar quando a carga está alta e que, se a carga for alta o bastante, parte dos trabalhos na fila pode ser descartada.
Então o schedule serve para "me acorde mais ou menos nesta hora".
Mas não serve para "alguém precisa pegar este texto e segurar até dar certo".
O relógio toca.
O relógio não guarda o seu texto.
Já uma fila de mensagens guarda o próprio trabalho. Ela também sabe lidar com falhas: novas tentativas, atrasos e uma "fila de mensagens com falha" (dead-letter queue), um lugar separado para o que continua falhando.
Isso não quer dizer que você precise colocar um serviço novo.
Quer dizer dar um papel claro para cada peça:
- schedule = o que acorda de tempos em tempos
- lista de pendentes = o registro oficial do trabalho não terminado
- executor = quem faz o trabalho de verdade
- trava curta = a chave que impede dois de trabalharem ao mesmo tempo
- nova tentativa e fila de falhas = para onde vai o que deu errado
Se o relógio também for o RH, o almoxarifado e o achados e perdidos, o relógio vai ter um dia ruim.
4. O registro deve dizer "o que ainda não terminou", e não "quem é o dono"
Em uma distribuição de tarefas complicada, o centro do registro costuma virar "o responsável".
Mas, num processo de publicação, o que você realmente quer saber é:
Este artigo foi publicado ou não?
Praticamente só isso.
Então o registro oficial pode ser a lista de pendentes.
Chegou um texto novo: vai para os pendentes. Ao começar a publicar: entra uma trava curta. Deu certo: marca como publicado. Deu errado: volta para os pendentes. Falhou o número de vezes definido: vai para a fila de falhas e dispara um alerta.
"Responsável: Workers" ou "responsável: Actions" é só informação de apoio. Nunca pode virar um veto permanente.
Mesmo escrito "Workers", se ficar um tempo sem andar, outro executor pode assumir.
Assim, uma etiqueta de responsável morta não pode manter o trabalho como refém.
5. O caminho principal começa no evento; o schedule só recolhe o que escapou
Se você quer publicar na hora, esperar o próximo horário programado vai contra o objetivo.
Acorde o executor no instante em que o texto novo chegar.
Esse é o caminho principal.
E o schedule recolhe os pendentes antigos que continuam lá depois de alguns minutos ou algumas dezenas de minutos.
Ou seja:
O evento é o caminho principal. O schedule é o seguro.
Assim, no dia a dia, tudo é rápido.
Se um aviso se perder, o schedule recolhe depois.
Se o schedule for o caminho principal, qualquer atraso atrasa a publicação. E, com a checagem de responsável por cima, acontece o "acordou e não fez nada".
O despertador tocou.
Passou na verificação.
E disse: "hoje não é comigo" e foi para casa.
Então veio fazer o quê?
6. O plano B não é "trocar o responsável", e sim "recolher o que está parado e inacabado"
Uma armadilha comum no plano B é passar o trabalho de A para B de modo formal.
A passagem exige atualizar o registro.
Se essa atualização falhar, a própria passagem vira o problema.
Pode ser bem mais simples.
Normalmente o Workers faz o trabalho. Deu certo, acabou. Deu errado, o item continua nos pendentes.
Um executor reserva, como o Actions, confere de tempos em tempos. Se
- há um item pendente
- ele é mais velho que um tempo definido
- não há trava válida
- esse executor pode rodá-lo
então ele assume o trabalho.
Não precisa de um "certificado de transferência" do Workers.
O que importa é o fato de que o trabalho não terminou.
Assim, existir o resultado vale mais do que a tabela de responsáveis estar arrumada.
Para um sistema que publica artigos, isso é o mais natural.
7. Não precisa jogar fora o Live Schedule, mas não o transforme em aparelho de sobrevivência
O Live Schedule em si não é o problema.
Ele é muito bom quando tem uma pessoa olhando:
Mudar prioridades. Investigar problemas. Preparar ideias de melhoria. Acelerar consertos. Colocar planos novos.
O problema é que, quando ele para, a fábrica de artigos para junto.
O Live deve ser um acelerador, não um ponto único de falha.
Mesmo sem o Live,
texto novo → pendentes → executor → verificação → publicação
tem que continuar rodando sozinho.
Com o Live, fica mais rápido.
Sem ele, nada morre.
É como não ligar o coração de uma pessoa a alguém responder no Slack.
8. Troque a meta de "um sistema esperto pronto" por "entra um, sai um"
Esse tipo de sistema pode ser melhorado para sempre.
Distribuição justa de tarefas. Uso ideal do plano gratuito. Prevenção de duplicatas. Previsão de orçamento. Prioridades. Monitoramento. Recuperação automática. Análises.
Tudo isso é divertido.
E, se você fizer tudo, nenhum artigo sai.
Por isso a primeira meta pode ser bem mais simples.
Entra um texto novo e, sem nenhuma ação especial, sai um artigo publicado.
Se falhar, fica nos pendentes. Outro executor recolhe. Só o que falha várias vezes vai para uma pessoa.
Quando essa linha simples estiver estável, dá para trazer as otimizações de volta.
A gente só queria publicar artigos novos rápido e, sem perceber, estava construindo algo parecido com um sistema caseiro de consenso distribuído.
Tem dia que é assim.
Mas existe um jeito de sair.
Corte a complexidade.
O valor de um sistema de publicação não é a beleza da eleição do responsável.
É o artigo chegar ao público.
Conclusão
A raiz do inferno do Live Schedule não é o schedule em si.
É ter dado mais peso ao estado de "quem é o responsável" do que ao estado de "que trabalho ainda não terminou".
Se você voltar a um desenho mais simples:
- Use a lista de pendentes como registro oficial do trabalho não terminado
- Comece o trabalho assim que o evento chegar
- Mantenha as travas curtas
- Deixe o executor reserva recolher os pendentes antigos
- Use o schedule como seguro
- Separe na fila de falhas o que falha demais
- Trate o Live como um acelerador opcional
Isso já basta.
Melhor uma fábrica que não para do que uma fábrica esperta.
Comece por aí.
- GitHub Docs: Events that trigger workflows — schedule https://docs.github.com/actions/reference/workflows-and-actions/events-that-trigger-workflows
- Cloudflare Workers Docs: Cron Triggers https://developers.cloudflare.com/workers/configuration/cron-triggers/
- Cloudflare Queues Docs: Overview https://developers.cloudflare.com/queues/
- Cloudflare Queues Docs: Dead Letter Queues https://developers.cloudflare.com/queues/configuration/dead-letter-queues/
