Pular para o conteúdo
Development

A aba Discover que ficou permanentemente presa cavando

Por Victor Da Luz
iosswiftmusickitdev-logdeep-cut-atlas

Recebi um relatório de bug com uma captura de tela: a aba Discover estava travada em “Cavando atrás de deep cuts / Checando os artistas da sua biblioteca em busca de lançamentos que você não tem,” com um link “Continuar cavando” que não fazia nada, não importava quantas vezes fosse tocado. Esse virou um bug de duas camadas, e a segunda camada só apareceu porque um teste de regressão a pegou antes de eu lançar qualquer coisa.

Problema

O Discover percorre a biblioteca de forma incremental: checa um lote de artistas, guarda em cache o que é novo, lembra quando cada artista foi checado pela última vez, e só checa de novo quando estiver desatualizado. A aba mostra “Cavando atrás de deep cuts” enquanto algum artista ainda está sem checagem, e muda para um feed de resultados ou um estado vazio de “tudo em dia” assim que tudo já foi checado pelo menos uma vez.

“Preso para sempre, tentar de novo não ajuda” é um formato bem específico de bug. Geralmente significa que algo que deveria mudar entre as tentativas não está mudando de verdade.

Por que essa abordagem

Rastreei o caminho real do código em vez de adivinhar instabilidade do MusicKit. O mecanismo que decide “já checamos tudo?” só atualiza quando a busca do catálogo de um artista tem sucesso. Uma busca que falha só incrementa um contador e segue em frente, o artista fica completamente ausente do cache, o que significa que a checagem de “já checei esse artista?” o trata como “nunca checado, mais antigo, precisa ser checado primeiro.” Toda atualização o reseleciona, ele falha de novo (pelo motivo que o fez falhar da primeira vez), e a checagem de “estamos com cobertura completa?” nunca consegue chegar a 100% se mesmo um único artista nunca conseguir completar uma busca com sucesso. É exatamente “preso não importa quantas vezes você toca.”

O gatilho provável no mundo real: um artista na biblioteca de alguém que o MusicKit não consegue resolver de forma limpa para um artista de catálogo, um artista autopublicado ou associado via iTunes Match, algo fora dos casos organizados que as fixtures de teste simuladas cobrem. Esse modo de falha simplesmente não existe numa biblioteca de teste pequena e feita à mão, então nada pegou isso antes de um dispositivo real com uma biblioteca real (maior e mais bagunçada) pegar.

Implementação

A correção: uma busca que falha ainda deveria contar como “checada,” só que sem nenhum resultado novo, reaproveitando a mesma janela de 7 dias de desatualização que o app já tem como mecanismo natural de “tentar de novo depois,” em vez de inventar um novo conceito de contagem de tentativas ou backoff. Um artista cronicamente problemático sai do “sempre primeiro” e é revisitado automaticamente mais adiante, exatamente como qualquer outro artista que simplesmente ficou desatualizado.

Escrevi a correção, rodei os testes, e um dos meus testes novos falhou. Bom, é exatamente para isso que serve um teste de regressão novo. Descobri que havia uma segunda proteção, pré-existente: “se todo artista de um lote falhar, trate a coisa toda como catastrófica e não coloque nada em cache.” Essa proteção é legítima para um cold start real em que o pipeline inteiro está quebrado (rede fora do ar, autenticação revogada), você não quer colocar em cache silenciosamente “nada” e considerar concluído. Mas assim que a maior parte da biblioteca já teve sucesso, a rotação naturalmente vai se estreitando até sobrar só os artistas problemáticos. Eventualmente um lote passa a consistir inteiramente do único artista problemático, o que então é 100% das falhas daquele lote, o que dispara a mesma proteção “catastrófica”, e lança erro antes mesmo da atualização de cache da minha correção rodar.

A correção de verdade precisou das duas partes: marcar falhas como checadas, E fazer a proteção de falha catastrófica só disparar quando não houver nenhuma cobertura bem-sucedida anterior (um cold start genuíno de pipeline quebrado), não só “esse lote pequeno em particular por acaso falhou por completo.”

Pegadinhas

Esse é o tipo de bug que é basicamente invisível num teste de uma rodada só. “Um artista falha, os outros têm sucesso, a cobertura avança” passa tranquilo na primeira checagem. A quebra só aparece na segunda rodada, quando o lote encolhe até sobrar exatamente o artista problemático remanescente, um cenário que nenhum dos testes existentes construía, porque eles foram escritos para provar que a funcionalidade funciona, não para provar que ela sobrevive a um item persistentemente ruim ao longo de várias rotações. Escrevi um teste novo que exercita especificamente “o mesmo artista falha em toda atualização, ao longo de várias chamadas”, foi isso que de fato pegou a segunda proteção.

Resultados

Compilei, instalei e lancei a correção num dispositivo físico eu mesmo (via devicectl, não no simulador, o MusicKit simplesmente não funciona lá) e confirmei que o Discover agora mostra lançamentos reais em vez de continuar preso. Suíte completa: 165/165, incluindo os dois testes novos que exercitam especificamente um artista cronicamente falho ao longo de várias rodadas.

Achado bônus da mesma passagem pelo dispositivo, sem relação com esse bug: tocar num card de lançamento do Discover não faz absolutamente nada. Verifiquei o código, o próprio comentário de documentação do card diz literalmente que a ação de adicionar à playlist deveria ter sido lançada junto com a ação de deslizar “Not Interested,” e só metade disso chegou a ser lançada. Registrei separadamente em vez de incorporar isso a essa correção, já que é uma lacuna de funcionalidade real com questões de design próprias, não um bug de uma linha.

Leitura relacionada

Development

A playlist que já tinha o nome certo

Um artefato de renomeação, uma API sem campo de autor pra atualizar, e um teste em dispositivo real provando que o bug já tinha se corrigido sozinho, fechado como aceitar como está.

Ler

Você também pode achar útil

Proton

Proton Pass

Gerenciador de senhas focado em privacidade, da equipe por trás do Proton Mail.

Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).

Saiba mais
Proton

Proton Drive

Armazenamento em nuvem criptografado, da equipe por trás do Proton Mail.

Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).

Saiba mais
Proton

Proton VPN

VPN comercial com filtragem NetShield e interruptor de desligamento automático.

Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).

Saiba mais