Blog

Clicar em Publicar duas vezes agora publica uma vez só

Três pedidos de publicação do mesmo app chegaram à nossa API com 84 milissegundos de diferença, e cada um iniciou um deploy completo desse app. Eles disputaram a mesma hospedagem, e várias das versões resultantes falharam porque outra já tinha chegado primeiro. O único sinal visível era um e-mail de “falha na versão”, enquanto o app em si acabou funcionando. Agora, uma publicação que chega enquanto outra já está rodando para o mesmo app não inicia um segundo build. O banco de dados decide qual pedido vence, e os demais são informados com a verdade: já existe uma publicação em andamento.

O que aconteceu

Quando você publica um app no Mythex, a API registra uma implantação para ele e pede ao nosso orquestrador, o serviço que roda os builds, para construir e liberar o app. A publicação pode começar pelo botão Publicar, por outra aba do navegador ou pelo agente, quando você pede a ele para publicar. Um clique duplo, duas abas ou um pedido do agente que chega duas vezes podem gerar mais de uma publicação para o mesmo app quase ao mesmo tempo.

Nesse caso, três chegaram em 84 ms. Cada uma rodou um deploy completo. Na plataforma de hospedagem, versões para o mesmo app foram criadas no mesmo segundo, e três delas falharam porque as outras já estavam em andamento. Uma versão posterior travou, e o app funcionou depois.

Foi isso que tornou fácil passar despercebido. O usuário viu um único app funcionando. As falhas só apareceram como e-mails de falha de versão.

Por que nada impedia isso

O código de publicação tinha, sim, uma etapa que olhava as implantações em andamento do projeto. Mas o que ela fazia era marcar todas as implantações em andamento como falhas antes de começar, com a mensagem “substituída por uma nova publicação”. A intenção era limpar builds deixados para trás por um processo que travou.

Na prática, uma segunda publicação que chegava durante um build declarava a primeira morta e iniciava a sua própria. As duas rodavam até o fim contra o mesmo app. Não havia trava; havia uma etapa que ativamente abria caminho para uma corrida.

Havia também um sintoma mais estranho na primeira publicação de um app. Sem um registro de implantação para reaproveitar, duas primeiras publicações simultâneas tentavam inserir um registro. O banco tem um índice único no nome do link do app (seu slug, como seu-app em seu-app.mythex.ai), então a segunda inserção falhava. A API traduzia isso como “esse nome de link já está em uso”, orientando o usuário a renomear um app que, naquele momento, estava sendo publicado com sucesso com esse nome.

A correção: a reivindicação é a trava

Reivindicar o registro da implantação agora é uma única atualização condicional no banco: pegue esta linha e marque-a como em build somente se ninguém estiver publicando. Como é uma única instrução, o banco resolve pedidos que chegam no mesmo milissegundo. Exatamente um deles altera a linha. Os outros veem que zero linhas mudaram, param e nunca chegam ao orquestrador.

Uma trava também precisa de uma saída quando quem a segura morre. Se a API reiniciar no meio de uma publicação, a linha ficaria marcada como “em build” para sempre e o app não poderia mais ser publicado. Então, uma linha cujo dono parou de enviar batimentos (a publicação em andamento atualiza a linha a cada 30 segundos, e cinco minutos de silêncio contam como morte) pode ser reivindicada de novo. Esse limite vem do batimento, não de um palpite sobre quanto tempo um build leva. Construir o Dockerfile próprio do usuário pode levar muito tempo de forma legítima, e um build lento que continua enviando batimentos mantém a sua reivindicação.

A etapa que marcava como falha todas as implantações em andamento não roda mais antes de uma publicação. Só as linhas deixadas para trás por um processo morto são limpas.

Dizendo a verdade ao perdedor

O pedido que perde agora recebe uma resposta clara: publish_in_progress, junto com o ID da implantação que está rodando. Cada lugar que pode iniciar uma publicação trata isso:

  • O workspace acompanha o build que já está rodando, em vez de mostrar um erro por cima dele.
  • O agente é informado de que uma publicação deste app já está em andamento e deve esperar por ela, em vez de ser informado de que algo falhou, o que antes podia levá-lo a “consertar” um Dockerfile e tentar de novo.
  • Uma colisão de primeira publicação no nome do link é verificada: se a linha que segura o nome for a publicação em andamento do próprio projeto, a resposta é “já está publicando”, e não “esse nome já está em uso”. Uma colisão real com outro projeto continua recebendo a mensagem de renomear.

Como verificamos

Escrevemos um script de reprodução que roda três publicações do mesmo app ao mesmo tempo, com um banco de dados real e um orquestrador simulado, e conta quantos deploys o orquestrador recebe. Ele cobre a primeira publicação e a republicação de um app ativo. Antes da correção, contava dois deploys. Depois, um.

O script também cobre a saída de emergência: ele marca a linha do app como em build, sem batimento por dez minutos, publica de novo e verifica se a nova publicação reivindica a linha e roda.

Testes unitários mantêm a trava nas duas coisas que uma trava precisa fazer: de três republicações simultâneas, exatamente uma passa e as outras duas são informadas de que existe uma publicação em andamento; e um build cujo dono morreu pode ser reivindicado. Outro teste verifica que uma nova tentativa não herda a mensagem de erro da tentativa anterior.

O que tiramos disso

  • Código de limpeza pode ser uma corrida disfarçada. “Falhe tudo o que está em andamento e depois comece” parece higiene. Com dois chamadores, é cada um cancelando o outro.
  • Deixe o banco de dados arbitrar. Uma leitura seguida de uma escrita deixa uma brecha para outro pedido. Uma única atualização condicional não deixa.
  • Toda trava precisa de uma expiração ligada à vitalidade. Sem isso, uma queda vira um app travado.
  • Mensagens de erro são instruções. “Esse nome já está em uso” levava as pessoas a fazer a coisa errada. A mensagem certa era mais simples e verdadeira.

Mais sobre publicação na documentação: como publicar. E uma correção relacionada: seu app publicado continua no ar enquanto você o republica.

Mythex · Pricing · Crie qualquer coisa com IA · Templates de sites e apps · Mythex comparado · Glossário de criação de apps com IA · Ferramentas grátis para criar apps com IA · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)