odoo-brazil / odoo-brazil-eletronic-documents

Odoo SPED: NF-e, NFS-E, NFC-E, CT-E, ...
Other
59 stars 66 forks source link

memory leak? #82

Open rvalyi opened 8 years ago

rvalyi commented 8 years ago

Ola pessoal

Temos um cliente que esta lancando centenas de notas hoje ele usa so a parte de criar NFe's e transmitir elas nesse tempo. A cada duas horas ou algo assim, o servidor cai, parece que ele esta aumentando o consumo da memoria RAM sempre. Provavelemte o processo ultrapassa o que ele pode alcancar.

Tou perguntando aqui se alguem ja viu algo assim porque e o unico projeto que temos onde acontece isso e acontece que o ERP so esta usando o pysped massivamente hoje.

A gente vai resolver o problema la com supervisao, mas seria interessante saber: alguem ja observou algum memory leak nessa parte da transmissao das notas com o pysped?

Como esta usando algumas bibliotecas com extencoes em C por tras esse tipo de problema nao seria impossivel...

mileo commented 8 years ago

Qual a configuração da maquina?

rvalyi commented 8 years ago

Ubuntu Trusty 64 bits. A memoria ta mais do que suficent (8Go mas tem outras coisas rodando), o lance e que a gente ve que transmissao apos transmissao ele nao libera a memoria. Geralmente o Gunicorn ja supervisa os workers do Odoo e um worker nao consegue matar o processo pai. Nesse caso especifico porem, e o processo global que morre.

Alem de observar a memoria engordar, eu nao sei o que mata o processo no final. Nesse caso nao temos supervisao entao fica misterioso mesmo. Ele morre com uns 80% da memoria ocupada apenas com a messagem:

Killed

no log. Talvez o Gunicorn nao consegue mais reservar a memoria que ele precisa e morre. Eu nao tenho a minima ideia. So tenho a impressao que algo da transmissao nao libera a memoria como deveria. Tambem como e um processo repititivo de cadastrar e transmitir notas, eu realmente acho que o processo para por causa do memory leak e nao algum outro problema pontual. Esse problema provavelemte seria dentro do pysped ou uma lib que ele usa.

Mas enfim relato o problema aqui. Isso vai ficar chato de debugar mesmo e aqui nao e prioridade. Quis apenas saber se alguem ja encontrou esse problema. Senao fica registrado para que o proximo se toca que pode ter um problema nisso.

mileo commented 8 years ago

O limite de memoria do worker ta em quanto?

rvalyi commented 8 years ago

@mileo nesse caso:

limit_memory_hard = 2684354560
limit_memory_soft = 2147483648

Porem nao e o Gunircorn que mata um worker, porque quando isso acontece, ele escreve no log e alem disso so o um worker morre e nao o processo pai. Eu nao acho que o problema tenha a ver com a supervisao do Gunicorn nesse caso.

mileo commented 8 years ago

@rvalyi Tivemos alguns problemas no começo, mas eu não consegui diagnosticar se é um problema do PySPED ou algo relacionado.

Mas depois de vários ajustes na nossa infraestrutura tenho rodado instancias com 512 ram emitindo nf-e tranquilamente.

rvalyi commented 8 years ago

@mileo no caso nao estamos usando mas de 512Mo nesse servico. O unico problema que eu vejo e que a cada nota transmitida a memoria aumenta. Com uma supervisao por cima isso funciona com certeza. Agora nao deixa de ser um probleminha. Imagine que vc use o Odoo como CMS/ecommerce, vc vai ter seus workers consumindo rapidamente mais RAM do que vc deveria. Ai vc pode ter a supervisao que mata os workers ou o processo global (talvez). Mas ai restartando workers novos vc perde performance porque perde o cache por examplo.

Nao acho que seja nehnum problema prioritario e nao acho que seja simples de resolver. So queria deixar arquivado algum lugar para ver se temos relatorios convergentes disso ate alguem achar algo para resolver.

mileo commented 8 years ago

Filename: /home/mileo/Projects/odoo/odoo8-oca/parts/l10n_br/odoo-brazil-eletronic-documents/nfe/sped/nfe/processing/processor.py

Line # Mem usage Increment Line Contents

46    164.3 MiB      0.0 MiB       @profile
47                                 def __init__(self, company):
48    164.3 MiB      0.0 MiB           super(ProcessadorNFe, self).__init__()
49    164.3 MiB      0.0 MiB           self.ambiente = int(company.nfe_environment) or 2
50    164.3 MiB      0.0 MiB           self.estado = company.partner_id.l10n_br_city_id.state_id.code
51    164.3 MiB      0.0 MiB           self.versao = company.nfe_version
52    164.3 MiB      0.0 MiB           self.certificado = Certificado(company)
53    164.3 MiB      0.0 MiB           self.caminho = company.nfe_root_folder
54    164.3 MiB      0.0 MiB           self.salvar_arquivos = False
55    164.3 MiB      0.0 MiB           self.contingencia_SCAN = False
56    164.3 MiB      0.0 MiB           self.contingencia = False
57    164.3 MiB      0.0 MiB           self.danfe = DANFE()
58    164.3 MiB      0.0 MiB           self.daede = DAEDE()
59    164.3 MiB      0.0 MiB           self.caminho_temporario = ''
60    164.3 MiB      0.0 MiB           self.maximo_tentativas_consulta_recibo = 5
61    164.3 MiB      0.0 MiB           self.consulta_servico_ao_enviar = False
62                             
63    164.3 MiB      0.0 MiB           self._servidor     = ''
64    164.3 MiB      0.0 MiB           self._url          = ''
65    164.3 MiB      0.0 MiB           self._soap_envio   = None
66    164.3 MiB      0.0 MiB           self._soap_retorno = None

Filename: /home/mileo/Projects/odoo/odoo8-oca/parts/l10n_br/odoo-brazil-eletronic-documents/nfe/sped/nfe/processing/xml.py

Line # Mem usage Increment Line Contents

35    164.3 MiB      0.0 MiB   @profile
36                             def __processo(company):
37                             
38    164.3 MiB      0.0 MiB       p = ProcessadorNFe(company)
39    164.3 MiB      0.0 MiB       p.ambiente = int(company.nfe_environment)
40    164.3 MiB      0.0 MiB       p.estado = company.partner_id.l10n_br_city_id.state_id.code
41    164.3 MiB      0.0 MiB       p.certificado = Certificado(company)
42    164.3 MiB      0.0 MiB       p.salvar_arquivos = True
43    164.3 MiB      0.0 MiB       p.contingencia_SCAN = False
44    164.3 MiB      0.0 MiB       p.caminho = company.nfe_root_folder
45    164.3 MiB      0.0 MiB       return p

Filename: /home/mileo/Projects/odoo/odoo8-oca/parts/l10n_br/odoo-brazil-eletronic-documents/nfe/sped/nfe/processing/xml.py

Line # Mem usage Increment Line Contents

47    164.3 MiB      0.0 MiB   @profile
48                             def monta_caminho_nfe(company, chave_nfe):
49    164.3 MiB      0.0 MiB       p = __processo(company)
50    164.3 MiB      0.0 MiB       return p.monta_caminho_nfe(p.ambiente, chave_nfe)

Filename: /home/mileo/Projects/odoo/odoo8-oca/parts/l10n_br/odoo-brazil-eletronic-documents/nfe/models/account_invoice.py

Line # Mem usage Increment Line Contents

64    114.2 MiB      0.0 MiB       @api.multi
65                                 @profile
66                                 def nfe_export(self):
67                             
68    164.3 MiB     50.1 MiB           for inv in self:
69                             
70    114.2 MiB    -50.1 MiB               validate_nfe_configuration(inv.company_id)
71                             
72    114.2 MiB      0.0 MiB               nfe_obj = self._get_nfe_factory(inv.nfe_version)
73                             
74                                         # nfe_obj = NFe310()
75    114.2 MiB      0.0 MiB               nfes = nfe_obj.get_xml(self.env.cr, self.env.uid, self.ids,
76    114.2 MiB      0.0 MiB                                      int(inv.company_id.nfe_environment),
77    116.9 MiB      2.6 MiB                                      self.env.context)
78                             
79    164.3 MiB     47.5 MiB               for nfe in nfes:
80                                             # erro = nfe_obj.validation(nfe['nfe'])
81    164.3 MiB      0.0 MiB                   erro = XMLValidator.validation(nfe['nfe'], nfe_obj)
82    164.3 MiB      0.0 MiB                   nfe_key = nfe['key'][3:]
83    164.3 MiB      0.0 MiB                   if erro:
84                                                 raise RedirectWarning(
85                                                     erro, _(u'Erro na validaço da NFe!'))
86                             
87    164.3 MiB      0.0 MiB                   inv.write({'nfe_access_key': nfe_key})
88    164.3 MiB      0.0 MiB                   save_dir = os.path.join(
89    164.3 MiB      0.0 MiB                       monta_caminho_nfe(
90    164.3 MiB      0.0 MiB                           inv.company_id,
91    164.3 MiB      0.0 MiB                           chave_nfe=nfe_key) +
92    164.3 MiB      0.0 MiB                       'tmp/')
93    164.3 MiB      0.0 MiB                   nfe_file = nfe['nfe'].encode('utf8')
94                             
95    164.3 MiB      0.0 MiB                   file_path = save_dir + nfe_key + '-nfe.xml'
96    164.3 MiB      0.0 MiB                   try:
97    164.3 MiB      0.0 MiB                       if not os.path.exists(save_dir):
98    164.3 MiB      0.0 MiB                           os.makedirs(save_dir)
99    164.3 MiB      0.0 MiB                       f = open(file_path, 'w')
   100                                             except IOError:
   101                                                 raise RedirectWarning(
   102                                                     _(u'Erro!'), _(u"""Não foi possível salvar o arquivo
   103                                                         em disco, verifique as permissões de escrita
   104                                                         e o caminho da pasta"""))
   105                                             else:
   106    164.3 MiB      0.0 MiB                       f.write(nfe_file)
   107    164.3 MiB     -0.0 MiB                       f.close()
   108                             
   109    164.3 MiB      0.0 MiB                       event_obj = self.env['l10n_br_account.document_event']
   110    164.3 MiB      0.0 MiB                       event_obj.create({
   111    164.3 MiB      0.0 MiB                           'type': '0',
   112    164.3 MiB      0.0 MiB                           'company_id': inv.company_id.id,
   113    164.3 MiB      0.0 MiB                           'origin': '[NF-E]' + inv.internal_number,
   114    164.3 MiB      0.0 MiB                           'file_sent': file_path,
   115    164.3 MiB      0.0 MiB                           'create_date': datetime.datetime.now(),
   116    164.3 MiB      0.0 MiB                           'state': 'draft',
   117    164.3 MiB      0.0 MiB                           'document_event_ids': inv.id
   118                                                 })
   119    164.3 MiB      0.0 MiB                       inv.write({'state': 'sefaz_export'})

Filename: /home/mileo/Projects/odoo/odoo8-oca/parts/l10n_br/odoo-brazil-eletronic-documents/nfe/sped/nfe/processing/processor.py

Line # Mem usage Increment Line Contents

46    165.0 MiB      0.0 MiB       @profile
47                                 def __init__(self, company):
48    165.0 MiB      0.0 MiB           super(ProcessadorNFe, self).__init__()
49    165.0 MiB      0.0 MiB           self.ambiente = int(company.nfe_environment) or 2
50    165.0 MiB      0.0 MiB           self.estado = company.partner_id.l10n_br_city_id.state_id.code
51    165.0 MiB      0.0 MiB           self.versao = company.nfe_version
52    165.0 MiB      0.0 MiB           self.certificado = Certificado(company)
53    165.0 MiB      0.0 MiB           self.caminho = company.nfe_root_folder
54    165.0 MiB      0.0 MiB           self.salvar_arquivos = False
55    165.0 MiB      0.0 MiB           self.contingencia_SCAN = False
56    165.0 MiB      0.0 MiB           self.contingencia = False
57    165.0 MiB      0.0 MiB           self.danfe = DANFE()
58    165.0 MiB      0.0 MiB           self.daede = DAEDE()
59    165.0 MiB      0.0 MiB           self.caminho_temporario = ''
60    165.0 MiB      0.0 MiB           self.maximo_tentativas_consulta_recibo = 5
61    165.0 MiB      0.0 MiB           self.consulta_servico_ao_enviar = False
62                             
63    165.0 MiB      0.0 MiB           self._servidor     = ''
64    165.0 MiB      0.0 MiB           self._url          = ''
65    165.0 MiB      0.0 MiB           self._soap_envio   = None
66    165.0 MiB      0.0 MiB           self._soap_retorno = None

Filename: /home/mileo/Projects/odoo/odoo8-oca/parts/l10n_br/odoo-brazil-eletronic-documents/nfe/sped/nfe/processing/xml.py

Line # Mem usage Increment Line Contents

35    165.0 MiB      0.0 MiB   @profile
36                             def __processo(company):
37                             
38    165.0 MiB      0.0 MiB       p = ProcessadorNFe(company)
39    165.0 MiB      0.0 MiB       p.ambiente = int(company.nfe_environment)
40    165.0 MiB      0.0 MiB       p.estado = company.partner_id.l10n_br_city_id.state_id.code
41    165.0 MiB      0.0 MiB       p.certificado = Certificado(company)
42    165.0 MiB      0.0 MiB       p.salvar_arquivos = True
43    165.0 MiB      0.0 MiB       p.contingencia_SCAN = False
44    165.0 MiB      0.0 MiB       p.caminho = company.nfe_root_folder
45    165.0 MiB      0.0 MiB       return p

Filename: /home/mileo/Projects/odoo/odoo8-oca/parts/l10n_br/odoo-brazil-eletronic-documents/nfe/sped/nfe/processing/xml.py

Line # Mem usage Increment Line Contents

85    165.0 MiB      0.0 MiB   @profile
86                             def send(company, nfe):
87                             
88    165.0 MiB      0.0 MiB       p = __processo(company)
89                                 # Busca a versão da NF a ser emitida, não a do cadastro da empresa
90    165.0 MiB      0.0 MiB       p.versao = str(nfe[0].infNFe.versao.valor)
91    165.1 MiB      0.1 MiB       p.danfe.logo = add_backgound_to_logo_image(company)
92    165.1 MiB      0.0 MiB       p.danfe.leiaute_logo_vertical = True
93    165.1 MiB      0.0 MiB       p.danfe.nome_sistema = company.nfe_email or \
94                                     u"""Odoo/OpenERP - Sistema de Gestao Empresarial de Codigo Aberto
95    165.1 MiB      0.0 MiB           - 100%% WEB - www.openerpbrasil.org"""
96                             
97    165.1 MiB      0.0 MiB       return p.processar_notas(nfe)

Filename: /home/mileo/Projects/odoo/odoo8-oca/parts/l10n_br/odoo-brazil-eletronic-documents/nfe/models/account_invoice.py

Line # Mem usage Increment Line Contents

   122    164.3 MiB      0.0 MiB       @api.multi
   123                                 @profile
   124                                 def action_invoice_send_nfe(self):
   125                             
   126    169.7 MiB      5.4 MiB           for inv in self:
   127                             
   128    164.3 MiB     -5.4 MiB               event_obj = self.env['l10n_br_account.document_event']
   129    164.3 MiB      0.0 MiB               event = max(
   130    164.3 MiB      0.0 MiB                   event_obj.search([('document_event_ids', '=', inv.id),
   131    164.3 MiB      0.0 MiB                                     ('type', '=', '0')]))
   132    164.3 MiB      0.0 MiB               arquivo = event.file_sent
   133    164.3 MiB      0.0 MiB               nfe_obj = self._get_nfe_factory(inv.nfe_version)
   134                             
   135    164.3 MiB      0.0 MiB               nfe = []
   136    164.3 MiB      0.0 MiB               results = []
   137    164.3 MiB      0.0 MiB               protNFe = {}
   138    164.3 MiB      0.0 MiB               protNFe["state"] = 'sefaz_exception'
   139    164.3 MiB      0.0 MiB               protNFe["status_code"] = ''
   140    164.3 MiB      0.0 MiB               protNFe["message"] = ''
   141    164.3 MiB      0.0 MiB               protNFe["nfe_protocol_number"] = ''
   142    164.3 MiB      0.0 MiB               try:
   143    165.0 MiB      0.7 MiB                   nfe.append(nfe_obj.set_xml(arquivo))
   144    169.7 MiB      4.7 MiB                   for processo in send(inv.company_id, nfe):
   145    169.7 MiB      0.0 MiB                       vals = {
   146    169.7 MiB      0.0 MiB                           'type': str(processo.webservice),
   147    169.7 MiB      0.0 MiB                           'status': processo.resposta.cStat.valor,
   148    169.7 MiB      0.0 MiB                           'response': '',
   149    169.7 MiB      0.0 MiB                           'company_id': inv.company_id.id,
   150    169.7 MiB      0.0 MiB                           'origin': '[NF-E]' + inv.internal_number,
   151                                                     # TODO: Manipular os arquivos manualmente
   152                                                     # 'file_sent': processo.arquivos[0]['arquivo'],
   153                                                     # 'file_returned': processo.arquivos[1]['arquivo'],
   154    169.7 MiB      0.0 MiB                           'message': processo.resposta.xMotivo.valor,
   155    169.7 MiB      0.0 MiB                           'state': 'done',
   156    169.7 MiB      0.0 MiB                           'document_event_ids': inv.id}
   157    169.7 MiB      0.0 MiB                       results.append(vals)
   158    169.7 MiB      0.0 MiB                       if processo.webservice == 1:
   159    169.7 MiB      0.0 MiB                           for prot in processo.resposta.protNFe:
   160    169.7 MiB      0.0 MiB                               protNFe["status_code"] = prot.infProt.cStat.valor
   161                                                         protNFe["nfe_protocol_number"] = \
   162    169.7 MiB      0.0 MiB                                   prot.infProt.nProt.valor
   163    169.7 MiB      0.0 MiB                               protNFe["message"] = prot.infProt.xMotivo.valor
   164    169.7 MiB      0.0 MiB                               vals["status"] = prot.infProt.cStat.valor
   165    169.7 MiB      0.0 MiB                               vals["message"] = prot.infProt.xMotivo.valor
   166    169.7 MiB      0.0 MiB                               if prot.infProt.cStat.valor in ('100', '150'):
   167    169.7 MiB      0.0 MiB                                   protNFe["state"] = 'open'
   168                                                         elif prot.infProt.cStat.valor in ('110', '301',
   169                                                                                           '302'):
   170                                                             protNFe["state"] = 'sefaz_denied'
   171    169.7 MiB      0.0 MiB                           self.attach_file_event(None, 'nfe', 'xml')
   172    169.7 MiB      0.0 MiB                           self.attach_file_event(None, None, 'pdf')
   173                                         except Exception as e:
   174                                             _logger.error(e.message, exc_info=True)
   175                                             vals = {
   176                                                 'type': '-1',
   177                                                 'status': '000',
   178                                                 'response': 'response',
   179                                                 'company_id': self.company_id.id,
   180                                                 'origin': '[NF-E]' + inv.internal_number,
   181                                                 'file_sent': 'False',
   182                                                 'file_returned': 'False',
   183                                                 'message': 'Erro desconhecido ' + str(e),
   184                                                 'state': 'done',
   185                                                 'document_event_ids': inv.id
   186                                             }
   187                                             results.append(vals)
   188                                         finally:
   189    169.7 MiB     -0.0 MiB                   for result in results:
   190    169.7 MiB      0.0 MiB                       if result['type'] == '0':
   191    169.7 MiB      0.0 MiB                           event_obj.write(result)
   192                                                 else:
   193    169.7 MiB      0.0 MiB                           event_obj.create(result)
   194                             
   195    169.7 MiB      0.0 MiB                   self.write({
   196    169.7 MiB      0.0 MiB                       'nfe_status': protNFe["status_code"] + ' - ' +
   197    169.7 MiB      0.0 MiB                       protNFe["message"],
   198    169.7 MiB      0.0 MiB                       'nfe_date': datetime.datetime.now(),
   199    169.7 MiB      0.0 MiB                       'state': protNFe["state"],
   200    169.7 MiB      0.0 MiB                       'nfe_protocol_number': protNFe["nfe_protocol_number"],
   201                                             })
   202    169.7 MiB      0.0 MiB           return True
mileo commented 8 years ago

@rvalyi Não sou especialista nessas analises mas com um teste rápido aqui não vi o PySPED alocando muita memoria não.

Inclusive removendo a geração do Danfe apos a transmissão e a opção de salvar aquivos

danimaribeiro commented 8 years ago

E essa linha aqui:

143 165.0 MiB 0.7 MiB nfe.append(nfe_obj.set_xml(arquivo)) 144 169.7 MiB 4.7 MiB for processo in send(inv.company_id, nfe):

A segunda coluna me parece ser o incremento de memória né?

mileo commented 8 years ago

Sim, incremento de memoria

mileo commented 8 years ago

143 134.5 MiB 8.6 MiB nfe.append(nfe_obj.set_xml(arquivo)) 143 165.0 MiB 0.7 MiB nfe.append(nfe_obj.set_xml(arquivo))

Realizei vários testes, mas não esta me parecendo algo exato

danimaribeiro commented 8 years ago

Que profiler é esse, pycharm?

rvalyi commented 8 years ago

na verdade nao falei que ele usaria muita memoria, so me parece que ele nao estaria liberando tudo o que aloca e a cada nota ele usaria mais. Eu precisaria gastar um tempo com o profiler na instancia onde eu observei isso (ate esgotar a RAM varias vezes no caso), infelizmente tou ocupado com algumas outras coisas no momento... Mas enfim certeza que esse levantamento do uso da memoria servira o dia que mergulhar nisso de novo.

mileo commented 8 years ago

@rvalyi Pior q era isso mesmo! Tava com tanto sono hoje a tarde q nem me liguei. Vou continuar com o profile ativo nos testes da NT003 e verificar se a memoria sobe ao longo do tempo. []s