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
Quatro formas de recuperar suas anotações: desfazer, versões, lixeira e o salvamento recusado — o que o desfazer, os 30 dias de histórico de versões, a lixeira e a proteção contra conflito ao salvar recuperam, e onde cada um para.
O responsável que não trabalha mais aqui — por que remover alguém de um espaço de trabalho limpa as atribuições dessa pessoa em vez de deixar um nome em cinza em quarenta notas.
Notez: um aplicativo de anotações feito para desenvolvedores — o que é o Notez, as formas de uma nota chegar até ele e o que cada plano inclui.