O job de limpeza que teria apagado seu avatar

Product

Os espaços de trabalho ganharam avatares na v0.4.0. O recurso é pequeno: você envia uma imagem e ela aparece no seletor de espaços de trabalho. O upload funcionou de primeira. A imagem apareceu em todos os lugares onde devia aparecer. Todos os testes passaram.

E ela também teria sumido três dias depois.

Duas maneiras de perder um arquivo

Os uploads do Notez vão para o armazenamento de objetos antes de qualquer coisa no banco de dados apontar para eles. É de propósito — o navegador envia os bytes direto para o S3 com um POST pré-assinado, então um upload grande nunca ocupa a API. A linha que descreve o arquivo é gravada primeiro, depois chegam os bytes.

Essa ordem cria uma brecha. Se alguém escolhe um arquivo e fecha a aba, os bytes podem chegar sem que nada nunca aponte para eles. O armazenamento se enche de objetos que nenhuma nota, nenhum avatar e nenhum usuário vai pedir de novo.

Por isso existe um job em segundo plano. Uma vez por dia, ele procura arquivos que foram enviados, mas nunca vinculados a nada, e apaga os que têm mais de três dias. A palavra que faz o trabalho aí é attached (vinculado): um booleano que o código de upload não define. Terminar um upload marca o arquivo como uploaded (enviado). Outra coisa precisa reivindicá-lo.

No caso dos avatares de usuário, essa outra coisa é a atualização do perfil. Quando você define uma imagem nova, o código faz duas coisas além de gravar a URL na sua conta: marca o arquivo novo como vinculado e põe na fila de exclusão o arquivo que ele substituiu. As duas metades importam. Sem a primeira, o job de limpeza apaga uma imagem que está em uso. Sem a segunda, todo avatar que você já definiu fica no armazenamento para sempre, contando na sua cota.

Os avatares de espaço de trabalho não faziam nem uma coisa nem outra. A URL era gravada no espaço de trabalho, a imagem aparecia, e o arquivo ficava lá, marcado como enviado, não vinculado, a três dias da exclusão.

Por que nada pegou o problema

Os testes passaram porque testavam o que eu tinha construído. O upload retorna uma URL, a URL é armazenada, o componente a renderiza — tudo verdade, e tudo continuava verdade com o bug presente. Nenhum teste verificava nada sobre um booleano em outra tabela, porque não me ocorreu que um booleano em outra tabela fazia parte do recurso.

A falha também é invisível na hora. Não há erro, nem aviso no console, nem requisição falhando. Entre o bug e o sintoma há três dias e uma execução do cron, e o sintoma é uma imagem quebrada na tela de outra pessoa.

O que trouxe o problema à tona foi ler o código de avatar que já existia antes de supor que o meu era equivalente. O fluxo dos avatares de usuário tinha dois passos a mais que o meu, e os comentários explicavam exatamente por quê. Esse é o argumento para deixar no código o motivo, e não só o mecanismo: a próxima pessoa que copiar o padrão precisa saber quais partes sustentam o resto.

Excluir também é uma edição

Corrigir o caminho da substituição revelou um terceiro caso. Quando um espaço de trabalho é excluído, o avatar dele está vinculado — então o job de limpeza, que só apaga arquivos não vinculados, nunca vai tocar nele. Nada aponta mais para o arquivo, e nada nunca vai apontar. Ele fica órfão justamente do único jeito que o job de limpeza não consegue resolver.

Agora, excluir um espaço de trabalho libera o ícone dele na saída.

A regra geral: se um job em segundo plano decide o que manter perguntando se algo ainda aponta para um arquivo, então todo caminho do código que deixa de apontar para um arquivo precisa avisar. Substituir. Limpar. Excluir o pai. Esqueça um e você tem um vazamento; faça um ao contrário e você perde dados. Vistos de fora, os dois não se parecem em nada, e vêm da mesma linha que faltou.

O que eu mudei

  • Definir o avatar de um espaço de trabalho marca o arquivo como vinculado, e o job de limpeza o deixa em paz.

  • Substituir o avatar põe o arquivo antigo na fila de exclusão, com a mesma janela de 30 dias para restauração de tudo o que você exclui.

  • Excluir um espaço de trabalho libera o avatar em vez de deixá-lo abandonado.

Agora sete testes cobrem o ciclo de vida, incluindo os casos que não devem disparar nada — renomear um espaço de trabalho não pode pôr o avatar na fila de exclusão, e salvar de novo a mesma URL não é uma substituição. Conferi que eles falham sem a correção antes de confiar neles.

Continue lendo