Quando uma biblioteca local atinge centenas de milhares de faixas, a rolagem das primeiras telas costuma permanecer fluida, mas a paginação profunda, a navegação alfabética e as buscas globais passam a apresentar latências expressivas. Utilizando uma base de referência com 500.500 faixas, analisamos os tempos de execução dos principais caminhos de leitura e implementamos um espelho local de leitura em SQLite planejado para a versão 7.0.
Nota de versão: a versão 6.0 já trouxe melhorias estruturais de desempenho. O espelho de leitura aqui apresentado é voltado para a versão 7.0 e encontra-se em testes internos. Todos os números foram coletados em compilações internas de desenvolvimento.
A solução mantém uma cópia derivada em SQLite da biblioteca localmente. Na fase de indexação, chaves de ordenação, atributos de filtragem e âncoras de índice A-Z são pré-calculados e gravados em disco. O armazenamento nativo mantém o controle dos dados de referência, enquanto o espelho atende às consultas frequentes, permitindo operações independentes de reconstrução ou limpeza.
Resultados dos testes de desempenho
Os testes foram executados em um Mac mini M4 com a base de 500.500 faixas, passando por validação de consistência campo a campo:
| Caminho de leitura | Escala | Armazenamento nativo | Espelho | Ganho |
|---|---|---|---|---|
| Criação de snapshot da lista | 10k | 3030 ms | 21 ms | ~143× |
| Paginação de lista (com filtros) | 10k | 1984 ms | 8,2 ms | ~242× |
| Salto de âncora A-Z | 10k | ~2050 ms | 0,4–7,6 ms | 274–5100× |
| Página profunda (offset 450k) | 500k | ~28 min/página | ~0,9 s | ~1800× |
| Varredura sem resultados «linhas visíveis» | 100k | 98,1 s | 46 ms | ~2100× |
| Candidatos multicritério em listas inteligentes | 100k | 6,9 s | 1,27 s | ~5,4× |
| Tela de playlist / detalhe de artista / primeira exibição | 50k | – | – | 4,5–7,3× |
Na escala de 500.000 faixas, o caminho nativo realiza varreduras completas da tabela, elevando a duração conforme a profundidade da busca: consultar uma página na altura da faixa 400.000 consumia cerca de 1668 segundos na referência. O espelho reduz esse tempo para entre 0,8 e 1,4 segundo, proporcionando tempos de resposta consistentes em qualquer nível de deslocamento.
Etiquetas de ordenação e compatibilidade de telas
Para estilos como música clássica e jazz, ouvintes costumam habilitar etiquetas de ordenação (Sort Tags) para classificar «The Beatles» sob a letra B («Beatles, The»). O espelho mantém dois conjuntos de chaves de ordenação para os cinco campos compatíveis. Alternar as etiquetas apenas seleciona a coluna equivalente na consulta SQL, mantendo a régua A-Z atualizada sem exigir nova geração de índices.
Telas consolidadas de álbuns, artistas e compositores também usam as consultas do espelho. No entanto, com etiquetas ativadas nessas telas, o sistema recorre ao armazenamento nativo para reproduzir os valores individuais de cada entidade com exatidão. O desenvolvimento de tabelas consolidadas no fluxo de gravação está programado para as próximas versões.
Uso de recursos e compromissos
O uso do espelho local requer tempo de indexação e espaço em disco:
- Tempo de geração: a indexação completa roda em segundo plano. Para 500.000 faixas, a geração inicial dura cerca de 32 minutos, mantendo a navegação ativa no modo padrão; bibliotecas com 50.000 faixas completam em aproximadamente 35 segundos.
- Espaço em disco: o arquivo de espelho para 500.000 faixas ocupa por volta de 2,1 GB de armazenamento local. A tela de Ajustes detalha a ocupação real; ao desativar o recurso, os arquivos são mantidos para reativação imediata, existindo também a opção de exclusão manual total.
- Sobrecarga de gravação: atualizações de metadados sincronizam o espelho em tempo real, acrescentando cerca de 20 ms por confirmação de lote em nossas medições.
Em virtude desse perfil de consumo, o recurso vem desativado de fábrica, permitindo ativação sob demanda conforme o tamanho da biblioteca do usuário.
Mecanismos centrais de otimização
- Projeção de valores leves: enquanto o fluxo padrão cria objetos gerenciados completos para cada registro, o espelho lê linhas brutas do SQLite e as projeta em tipos leves, contendo os ciclos de vida de memória e CPU.
- Empurramento de predicados: regras de filtragem, critérios de ordenação e eliminação de duplicatas rodam diretamente no motor de banco de dados, filtrando os registros antes de carregá-los na memória.
- Pré-cálculo de chaves de ordenação e índices: chaves normalizadas e caracteres do índice A-Z são gravados no momento da indexação. As consultas utilizam paginação por chaves (Keyset Pagination) e leituras diretas de índice, estabilizando as respostas em páginas profundas.
Consistência de dados e detalhes de implementação
Para garantir paridade total entre o espelho e o armazenamento principal, o conjunto de testes executou combinações de 19 colunas de ordenação, direções ascendente e descendente, estados de etiquetas e filtros compostos. Diversos detalhes de fronteira foram ajustados:
- Coalescência de nulos e cadeias vazias: as regras de etiquetas priorizam o valor da tag e usam o nome de exibição caso ela não exista. Considerando que operadores do Swift e funções SQL tratam sequências vazias de formas distintas, o espelho separa as chaves em colunas próprias e as define explicitamente nas consultas.
- Ordenação em campos legados: a duração de faixas era classificada historicamente por comparação alfanumérica de texto (como «120.0» antes de «90.0»). Para manter a ordenação original, os campos de texto contam com chaves próprias de ordenação textual, deixando índices numéricos para colunas estritamente numéricas.
- Alinhamento de caracteres de espaço: as definições de espaços em branco no Swift divergem sutilmente das funções usuais de remoção de espaços em SQL. Marcadores de estado gravados no banco foram inseridos para uniformizar a detecção de conteúdo vazio.
- Divisão em lotes em grandes volumes: ao processar 500.000 registros, gatilhos de busca textual gravam múltiplos índices, demandando cerca de 32 minutos no total. O fluxo de geração foi estruturado em lotes com suporte a retomada após pausas.
- Arbitragem de gravações paralelas: para alterações de metadados ocorridas durante a indexação, cada gravação recebe um marcador sequencial estritamente crescente, permitindo que regras de concorrência apliquem as versões mais recentes sem inconsistências.
Todos os números foram obtidos em compilações de depuração; relatórios de medição e rotinas de teste estão preservados no repositório.
