cuducos / minha-receita

🏢 Sua API web para consulta de informações do CNPJ da Receita Federal
https://minhareceita.org
MIT License
1.32k stars 132 forks source link

Automatização da atualização do banco de dados #132

Closed cuducos closed 2 years ago

cuducos commented 2 years ago

ATUALIZAÇÃO Nova forma de atualização (manual) descrita num comentário um ano mais recente.


O processo de atualização do servidor minhareceita.org é basicamente composto por três etapas com diferentes gargalos. Essa issue tem como objetivo discutir a automatização do terceiro desses passos:

Descrição Gargalo Solução no momento
1 Baixar os dados Mais de 5Gb em um servidor altamente instável Fico alternando entre ./minha-receita download e ./minha-receita -x check até tudo estar OK
2 Importar os dados Processo é demorado (ver #120) Utilizo uma VM parruda por ums horas (o que não sai caro 1x no mês), importo os dados e gero um arquivo de dump
3 Subir os dados para o Postgres em produção A conexão do pg_restore não é resiliente e o processo, por ser demorado, sempre acaba interrompido por erro de conexão (levando a #131) Crio uma VM (simplezinha) na nuvem, faço o login via SSH, instalo ferramentas como PostgreSQL, Screen etc., subo o dump, inicio uma sessão no Screen, rodo o pg_restore na sessão, monitoro e, ao final, excluo a VM

Claro que os passos 1 e 2 podem ser automatizados, mas vamos começar focando no calo que dói mais agora.

Contexto

Atualmente o projeto roda com o plano Happy Hippo do ElephantSQL que oferece 50% de desconto pela natureza do projeto (tecnologia cívica, código aberto, dados abertos). Em suma, isso quer dizer que:

O banco atual tem 4Gb de RAM, mas isso não parece ser um gargalo (dados dos últimos 14 dias mostram que raramente chega a 1Gb de uso — mas isso não leva em conta o restante do sistema operacional pelo o que entendi).

Um custo não facilmente mensurável é a facilidade de criar uma nova instância em poucos cliques — isso facilita e viabiliza consideravelemente a manutenção do projeto (pois meu tempo para mantê-lo é limitado)

O processo de atualização

Dado que a cada novo lote de dados disponibilizados pela Receita Federal:

É mais fácil subir os dados em um novo banco de dados — atualmente crio uma nova instância pois o DBaaS em uso não permite mais de uma database na mesma instância do PostgreSQL, mas nada impede de usarmos um CREATE DATABASE minhareceita_2022_06 na mesma instância, por exemplo.

Possível automatização

Explorei a GCP, mas creio que esse processo pode ser replicdo em outras plataformas. O gargalo do ElephantSQL é que eles não tem uma solução para importar um arquivo da nuvem.

Configuração inicial

Isso precisa ser feito apenas uma vez, sem necessidade de automatização:

Automatizar

Esse processo seria executado a cada novo dump gerado:

  1. subir o dump para o armazenamento de arquivos
  2. criar um novo banco de dados
  3. importar o dump
  4. configurar a aplicação pra o novo banco de dados
    (provavelmente via CLI do Heroku, mas pode ser manual num primeiro momento)
  5. excluir o banco de dados antigo
  6. excluir o arquivo de dump
Dúvidas & comentários
tonnydourado commented 2 years ago

Como vc gera o arquivo de dump? Pelo que eu entendi do código do comando de download, ele só baixa os arquivos.

Update: é com o comando transform?

cuducos commented 2 years ago

Exato, você está correto!

Comando Descrição Origem dos dados Destino dos dados
download Baixa os arquivos CSV com os dados Página da Receita Federal Diretório local
transform Transforma os CSV em JSON Diretório local PostgreSQL

O dump tenho gerado com pg_dump na mão mesmo.

edinhodiluviano commented 2 years ago

Não sei exatamente como ficaria a performance (a latencia da query, especificamente, provavelmente vai sofrer), mas dado que o grosso do seu uso é leitura somente, consideraria utilizar um banco de dados otimizado para leitura?

A aws oferece o redshift spectrum we vc paga somente pelo armazenamento (pra 100gb daria uns $2 por mes) e pelo uso ($5 por TB lido; se as tabelas estiverem indexadas ele le somente as particões relevantes). Se for o caso, a transformação do arquivo geraria um arquivo fixo, por exemplo um parquet, que seria adicionado ao S3 e depois seria executado um novo mapeamento do redshift pra considerar aquele arquivo. O redshift é um postgres modificado, é provavel que sua aplicação consiga ler diretamente dele sem modificação nenhuma.

cuducos commented 2 years ago

@edinhodiluviano, eu nunca havia pensando nisso. Desconhecia DBaaS apenas para leitura, e não sabia que eles eram tão mais baratos! Vou estudar… muito obrigado 💜

cuducos commented 2 years ago

Tentei importar um dump com o formato binário/customizado do Postgres pela interface web do GCP, e falhou. Pelo o que entendi, essa funcionalidade só funciona com texto puro. Então, alguns números para comparação:

Formato Tamanho
custom 11 Gb
plain 91 Gb
plain com GZip (maior nível de compactação) 11 Gb
tonnydourado commented 2 years ago

Dei uma olhada em algumas alternativas de storage que pudessem simplificar o processo, mas infelizmente não achei nada que batesse o seu setup atual:

O que sobra é tentar automatizar o processo de restore do dump, e pra isso tenho algumas idéias:

  1. Criar um container com um script pra fazer o restore a partir de um arquivo de dump. Você pode rodar esse container a partir da mesma VM que faz o download dos dados e gera o dump (ou trocar de VM, mantendo só o volume). O gargalo do pg_restore continua, mas diminui um pouco o trabalho manual
  2. Mesma coisa que 1, mas usando barman ao invés de pg_dump/pg_restore, pra fazer backup do banco local, populado pelo comando transform, e restaurar pra um banco novo no elephantsql. Barman talvez seja mais robusto que pg_dump/restore, e não necessariamente precisa de acesso ssh
  3. Fazer restore por registro, não por dump. A idéia seria fazer um comando em Go que leia os registros do banco local e escreva pro banco remoto. Vai ser mais lento, mas talvez seja interessante aqui trocar velocidade por confiabilidade. Se demorar o dobro, mas puder rodar 100% unsupervised, fica mais fácil de automatizar tudo a partir do download.
cuducos commented 2 years ago

Sensacional, @tonnydourado! Muito obrigado por me ajudar com essa análise, compartilhando detalhes e opções.

Acho que vou começar criando um Dockerfile que tenha o que preciso. Vou ver se no final de semana consigo algo.

cuducos commented 2 years ago

Ok, algum progresso, talvez… #133

wandersonsousa commented 2 years ago

Pelo que entendi, o banco de dados está sendo recriado a cada nova atualização da receita, não seria mais viável adicionar um processo que busca apenas pelas novas empresas abertas ( baseando-se no campo data de abertura) e atualizar o banco de dados com apenas esses novos registros ?.

Como a frequência em que uma empresa atualiza seus dados na receita é muito baixo, eu imagino que seria mais viável deixar essa tarefa de atualizar os dados de uma empresa como um processo separado.

cuducos commented 2 years ago

não seria mais viável adicionar um processo que busca apenas pelas novas empresas abertas

Não, isso deixaria de atualizar as empresas existentes. Por exemplo, se a Empresa A mudou de MEI para limitada entre um lote de dados e outros, essa estratégia deixaria o banco de dados inconsistente.

cuducos commented 2 years ago

Fechando por inatividade. Mas podem reabrir se tiverem mais ideias 💜

cuducos commented 1 year ago

Resolvi atualizar essa issue, mas talvez seja melhor abrir outra já que o contexto mudou muito em um ano.

TLDR Saíram Heroku e ElephantSQL, entrou um VPS rodando Dokku para o projeto.

Com isso, a rotina agora é, em teoria:

  1. Acessar o servidor via SSH
  2. Download dos dados usando minha-receita download
  3. Criação de um novo banco de dados com dokku postgres:create minhareceitaYYYYMM
  4. Exposição do novo banco de dados com dokku postgres:expose minhareceitaYYYYMM
  5. Copiar credenciais de acesso ao novo banco de dados manualmente e configurar a variável de ambiente DATABASE_URL
  6. Rodar o ETL com minha-receita transform
  7. Deixar de expor o novo banco de dados com dokku postgres:unexpose minhareceitaYYYYMM
  8. Disponibilizar o novo banco de dados para a API web com dokku postgres:link minhareceitaYYYYMM minhareceita e dokku config:set minhareceita DATABASE_URL=…
  9. Excluir o banco de dados antido com dokku postgres:destroy minhareceitaYYYYMM

Na prática quase que sempre a etapa do ETL falha ou por falta de memória ou por falta de espaço em disco, aí tenho que intervir manualmente fazendo alguma manutenção básica no servidor e, muitas vezes, o jeito mais rápido acaba sendo não rodar o ETL online — acabo rodando localmente, fazendo um scp para copiar o dump do novo banco de dados em SQL e:

  1. Acessar o servidor via SSH
  2. Criação de um novo banco de dados com dokku postgres:create minhareceitaYYYYMM
  3. Exposição do novo banco de dados com dokku postgres:expose minhareceitaYYYYMM
  4. Importação dos dados com psql -f …
  5. Exclusão do dump em SQL
  6. Deixar de expor o novo banco de dados com dokku postgres:unexpose minhareceitaYYYYMM
  7. Disponibilizar o novo banco de dados para a API web com dokku postgres:link minhareceitaYYYYMM minhareceita e dokku config:set minhareceita DATABASE_URL=…
  8. Excluir o banco de dados antido com dokku postgres:destroy minhareceitaYYYYMM