Controle o desempenho do BD no Django com django-test-query-counter

Apresentamos uma biblioteca para manter o desempenho do banco de dados de aplicações Django por testes. Mede consultas durante testes unitários e oferece um comando para comparar contagens de duas execuções, com detalhes como rastreamentos de pilha. Busca detectar problemas o quanto antes.
Introdução
Quando um problema torna a aplicação lenta, não é raro deixar o desempenho de lado ao resolvê-lo. O mesmo código pode gerar o dobro de consultas. Algumas causas comuns:
- Requisitos novos que exigem informações de registros relacionados
- Instâncias diferentes de QuerySet para a mesma consulta.
- Falta de select_related, prefetch_related ou subconsultas no queryset original.
- Falta de cache como
@lrucache,@cached_propertye o framework de cache do Django.
Se uma página demora a carregar, o usuário pode reclamar. Depois, gastamos muito tempo diagnosticando a lentidão. No meu projeto, criávamos entidades que geravam 500 ou mais consultas em uma única solicitação. Detectar antes seria muito mais econômico. A figura mostra o custo da mudança em função do tempo, também aplicável ao desempenho.

A documentação do Django descreve problemas e soluções comuns. Ferramentas como django-debug-toolbar mostram o que acontece em uma solicitação, incluindo a contagem detalhada de consultas. São excelentes para depurar casos pontuais, mas não para fluxos automatizados. Assim como cobertura de código e testes funcionais, o desempenho do BD deve ser medido e controlado. Convém integrá-lo ao fluxo habitual, por exemplo antes de enviar código para master.
A solução

Django-test-query-counter, ou query-counter, registra cada consulta dos testes unitários sob a premissa “menos consultas, mais velocidade”. Controla consultas por solicitação em cada teste. Tem duas partes: middleware que gera um JSON com a contagem de uma execução e um comando que compara dois arquivos e mostra violações na segunda. Pode ser usado localmente, mas se destaca com CI como Jenkins, em que testes e verificações são periódicos. Não se integra a uma CI específica, mas é simples adicionar check_query_count às tarefas.
Por exemplo, um teste obtém uma lista de livros e depois informações do primeiro:
class BookTester(TestCase):
def test_getbook(self):
# This makes 30 queries
book_list = self.client.get('/api/books/all')
self.assertEqual(
book_list.status_code,
status.HTTP_200_OK
)
first_book = json.decode(book_list.data)
# This makes 20 queries
book_info = self.client.get(
'/api/books/{}'.format(first_book['id'])
)
self.assertEqual(
book_info.status_code,
status.HTTP_200_OK
)
Query-counter registrou:
- BookTester.test_getbook (50 consultas)
* /api/books/all (20 consultas) * /api/books/5 (30 consultas)
O limite de tolerância k é percentual. A verificação falha se uma solicitação ultrapassa em mais de k por cento as consultas da última execução bem-sucedida. Se for 10%, /api/books/all tolera no máximo 22 consultas.
Usar a aplicação localmente
Query-counter está no GitHub e no PyPI.
-
Instale pelo PIP:
$ pip install django-request-query-counter -
Adicione a
INSTALLED_APPSnas configurações:INSTALLED_APPS = ( ... 'test_query_count', ... ) -
Execute os testes como sempre:
$ python manage.py test -
Se foi instalado corretamente, aparecerá reports/query_count.json no diretório da aplicação com a contagem por teste. Copie o arquivo localmente:
$ cp reports/query_count.json last_query_count.json -
Adicione consultas ao código, execute os testes novamente e execute check_query_count:
$ python manage.py check_query_count --last-count-file last_query_count.json
Ele comparará last_query_count.json com reports/query_count.json e informará cada teste que excedeu o limite de 10%, o padrão. Você pode configurá-lo.
Interação com CI
Pode ser integrado facilmente com Jenkins ou Travis. A figura da seção anterior descreve o fluxo.
1) Executar testes unitários
2) Executar check_query_count contra query_count.json da última compilação bem-sucedida. A compilação deve ser marcada como instável se houver uma violação.
3) Arquivar query_count.json como artefato de compilação.
Este script bash ilustra o passo 2 para Jenkins. É semelhante ao caso local, mas baixa query_count.json da CI.
curl http://yourci.com/yourjob/lastSuccessfulBuild/artifact/reports/query_count.json -o last_query_count.json
python manage.py check_query_count --last-count-file last_query_count.json
Próximos passos
Todo feedback é bem-vindo. É uma biblioteca nova e adicionaremos funcionalidades; você pode contribuir com relatos de erros ou pull requests. O roteiro inclui:
- Rastreamentos de pilha detalhados nas consultas.
- Analisar consultas sem índices por SQL explain e gerar avisos.
- Relatório gráfico HTML de consultas executadas por cada linha de código.