Galera, ontem tomei uma surra. Pedi ajuda pra todo mundo. Fui escutando, estudando, errando até que consegui resolver o problema. Não sei se da melhor forma — por isso estou postando aqui. Vai ser um email longo. Se a preguiça estiver por perto é melhor deixar para lá…
Contexto
A operação Serenata de Amor é um projeto de código aberto e dados abertos. Lá temos a geolocalização de todas as empresas onde os deputados gastaram com dinheiro público. O Guilherme, um colaborador do projeto que não conheço pessoalmente mas já admiro, mandou um pull request com um script em Python que tenta descobrir se a nota fiscal apresentada é de um “puteiro” — entre aspas mesmo. Oficialmente chamamos de sex related places: casas de entretenimento adulto de alta rotatividade.
Ele usou a API do Google Places e fez uma lista com 12 palavras-chave para achar esses lugares. O script, então, segue essa lógica:
OR para pesquisar por mais de uma palavra-chave), perguntando se tem algum lugar dessa categoria nas redondezas da coordenada geográfica da empresaProblema
Temos atualmente (esse número deve subir em breve) mais de 60 mil empresas no nosso dataset. 60 mil empresas dentro dessa lógica significa 780 mil requisições à API. Graças à grana do Catarse temos como pagar pela API, esse não é o problema. O problema é o tempo que demora para fazer 780 mil requisições, tratar os resultados e gravar em arquivo. Esse é o calo.
Pelas minhas contas levaríamos mais de 1 semana rodando o script. Isso em si não é problema, o problema é internet estável, necessidades de pausa imposta pela API (limite de 150 mil requests diários), erros etc.
Então resolvi refatorar essa contribuição.
Estratégias
'a' (mais precisamente, no modo 'at' já que estamos usando o módulo lzma), escrever uma linha e fechar o dito cujo. Isso me garante que eu não preciso ter todos os resultados para terminar a escrita do arquivo, e me garante que caso o arquivo fique grande demais eu ocupe memória com ele.gevent por debaixo dos panos).A terceira foi a surra! Tentei usar multiprocessamento para processar o fluxo todo de mais de uma empresa em paralelo já que o fluxo de processamento de uma empresa não depende do fluxo de processamento de outra empresa. Aí esbarrei feio na dificuldade de escrever em arquivo no meio dessa multiprocessamento.
a. Primeiro tentei combinar o asyncio com o ProcessPoolExecutor; fiz uns testes curtos e pareceu funcionar (por funcionar entendam: escrever linhas no arquivo de resultados assim que cada empresa é processada, sem esperar o término da execução do script); mas depois de mais de 6h rodando fui ver e não tinha nada no arquivo — não entendi o porquê, mas essa versão do script está aqui.
b. A versão atual do arquivo é um tanto complexa. Implementei duas filas: uma para todas as empresas, e outra para todos os sex related places encontrados. Aí começo um encadeamento de processos (multiprocess.Process) para processar e gerenciar as filas: um processo extraí todas as empresas do dataset de empresas e adicionar as empresas na fila de empresas; outros processos vão processando a fila de empresas, fazendo as consultas na API; por fim um último processo cuida da fila de lugares encontrados, pegando cada um deles e escrevendo em arquivo. Parece que funcionou, rodei uns testes curtos, mas preciso esperar o respiro da API para fazer um teste de algumas horas. Esse emaranhado de filas e processor está aqui.
Dúvidas
gevent está resolvendo, teria alguma vantagem refatorar para usar oasyncio nativo?asyncio e ProcessPoolExecutor não estar escrevendo para arquivo conforme os resultados estão chegando? (no caso da execução por algumas horas, pelo menos)O email é longo, e o script também (versão atual aqui, caso tenha perdido o link ali em cima). Mas me disponho a apresentar o script a qualquer interessado em uma call, adoraria parear etc. e tal. Alguém me acode?
Muito obrigado,
--
--
------------------------------------
Grupo Python-Brasil
http://www.python.org.br/wiki/AntesDePerguntar
<*> Para visitar o site do grupo na web, acesse:
http://groups.google.com/group/python-brasil
<*> Para sair deste grupo, envie um e-mail para:
python-brasil+unsubscribe@googlegroups.com
---
Você recebeu essa mensagem porque está inscrito no grupo "Python Brasil" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para python-brasil+unsubscribe@googlegroups.com.
Para mais opções, acesse https://groups.google.com/d/optout.
gevent está resolvendo, teria alguma vantagem refatorar para usar oasyncio nativo?asyncio e ProcessPoolExecutor não estar escrevendo para arquivo conforme os resultados estão chegando? (no caso da execução por algumas horas, pelo menos)
>
> ---
> Você recebeu essa mensagem porque está inscrito no grupo "Python Brasil" dos
> Grupos do Google.
> Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie
> um e-mail para python-brasil+unsubscribe@googlegroups.com.
--
--
------------------------------------
Grupo Python-Brasil
http://www.python.org.br/wiki/AntesDePerguntar
<*> Para visitar o site do grupo na web, acesse:
http://groups.google.com/group/python-brasil
<*> Para sair deste grupo, envie um e-mail para:
Você está recebendo esta mensagem porque se inscreveu no grupo "Python Brasil" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para python-brasil+unsubscribe@googlegroups.com.
Para obter mais opções, acesse https://groups.google.com/d/optout.
---
Você recebeu essa mensagem porque está inscrito no grupo "Python Brasil" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para python-brasi...@googlegroups.com.
Para mais opções, acesse https://groups.google.com/d/optout.
Galera, muito obrigado pelas respostas, dicas, sugestões, e tudo mais.
--
--
------------------------------------
Grupo Python-Brasil
http://www.python.org.br/wiki/AntesDePerguntar
<*> Para visitar o site do grupo na web, acesse:
http://groups.google.com/group/python-brasil
<*> Para sair deste grupo, envie um e-mail para:
python-brasi...@googlegroups.com
---
Você recebeu essa mensagem porque está inscrito no grupo "Python Brasil" dos Grupos do Google.
Para cancelar inscrição nesse grupo e parar de receber e-mails dele, envie um e-mail para python-brasi...@googlegroups.com.
Para mais opções, acesse https://groups.google.com/d/optout.