fix(articlemeta): corrige memory leak por lru_cache em instância e robustez dos formatters journal/issue#1424
Merged
robertatakenaka merged 2 commits intoscieloorg:mainfrom Mar 26, 2026
Conversation
…stez e legibilidade - Substitui @lru_cache por cache manual em _titles_in_database_medline_secs para evitar memory leak (lru_cache em método de instância retém self indefinidamente no cache do decorador) - Renomeia _medline_titles para _titles_in_database_medline_secs para consistência com o nome da property - Renomeia _former_dict_journal_history para _format_journal_history_entry (corrige typo e segue convenção _format_* da classe) - Renomeia variável key_to_issn para key_to_value (nem todos os valores são ISSNs) - Adiciona guard clause 'journal_acron.upper() if journal_acron else None' em v930 para evitar AttributeError quando journal_acron é None - Refatora _format_publisher_info: substitui loop com break por .first() para obter apenas o primeiro owner, eliminando iteração desnecessária - Refatora _format_contact_address_info: substitui try/except genérico por checagem explícita 'if address', removendo tratamento silencioso de exceções - Refatora _format_journal_history: inverte condicional para early return, unifica blocos ADMITTED/INTERRUPTED duplicados em um único 'if in', e garante que subfield_b é determinado por evento (não acumulado) - Refatora _format_indexing_info: substitui duas list comprehensions separadas por um único loop para classificar medline vs secs, evitando dupla iteração sobre titles_in_db - Refatora _format_collection_info: remove checagem redundante 'if collection' (já coberta pelo 'if self.scielo_journal.collection') - Remove guard desnecessário 'if self.official' em _format_issn_list (método só é chamado dentro de bloco que já verifica self.official) - Adiciona TODO/docstring em _format_issn_type sinalizando possível inversão na lógica de negócio (issn_print == issn_scielo retorna 'ONLIN', o que parece invertido) - Corrige docstring typo 'Title Journalal' -> 'Title Journal' - Remove comentários óbvios/redundantes (e.g. 'tem que ser objeto datetime', 'Deixa preparado para tornar obsoleto') - Normaliza trailing whitespace e vírgulas finais em dicts/listas
…et e robustez - Substitui @lru_cache por cache manual em medline_titles para evitar memory leak (mesmo padrão aplicado no journal formatter) - Substitui atributo self.article (queryset avaliado no __init__) por property lazy self.article_qs que só executa a query quando acessado, evitando query desnecessária quando o dado não é utilizado - Corrige _format_article_info: usa self.article_qs (filtrado por issue + journal) em vez de self.obj.article_set (sem filtro por journal), garantindo contagem consistente com o queryset da classe - Adiciona guard clause em _format_field_use_system para checar journal_acron antes de chamar .upper(), e usa 'or empty string' para volume/number None, evitando concatenação com None - Refatora _format_issn_info com early return (guard clause) para reduzir nível de indentação; adiciona placeholder v435 no add_multiple_to_result para documentar que será preenchido por _format_issn_with_type - Adiciona guard 'if issns' antes de atribuir v435 em _format_issn_with_type para evitar lista vazia no resultado - Melhora select_related em _format_code_sections: inclui 'journal_toc' além de 'journal_toc__language'; adiciona guard 'if not journal_toc: continue' para pular registros sem relação - Remove comentários redundantes (e.g. 'Path to base issue', 'Ordem de publicação', 'Só adiciona v/n se houver') - Remove import não utilizado 'from functools import lru_cache' - Corrige missing newline at end of file
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
O que esse PR faz?
Refatora os formatters ArticleMeta de journal e issue, eliminando problemas de memory leak causados pelo uso de
@lru_cacheem métodos de instância, melhorando a robustez contra valoresNonee simplificando a legibilidade geral do código.Principais mudanças:
Memory leak —
lru_cacheem instância:Substitui todos os usos de
@lru_cachepor cache manual via atributo privado (_medline_titles,_titles_in_database_medline_secs, etc.). Olru_cacheaplicado a métodos de instância retémselfcomo chave do cache no nível da classe, impedindo o garbage collector de liberar as instâncias — o que em contexto de request Django/Gunicorn causa crescimento contínuo de memória.Queryset lazy no Issue formatter:
O queryset
Article.objects.filter(issue=self.obj, journal=self.journal)era executado no__init__, mesmo quando o dado não era necessário. Agora é uma@propertylazy (article_qs) que só dispara a query quando acessada. Além disso,_format_article_infousavaself.obj.article_set.count()(sem filtro por journal) para a contagem, divergindo do queryset filtrado — corrigido para usarself.article_qs.count().Robustez contra
None:_format_field_use_system: guard clause parajournal_acronantes de.upper(), eor ''paravolume/number_format_scielo_journal_info:journal_acron.upper() if journal_acron else Noneemv930_format_code_sections: guardif not journal_toc: continue+select_relatedincluindojournal_toc_format_issn_with_type:if issnsantes de atribuirv435para evitar lista vaziaSimplificações e limpeza:
_format_publisher_info: substitui loop combreakpor.first()_format_contact_address_info: substituitry/exceptgenérico porif address_format_journal_history: early return, unifica blocos duplicados ADMITTED/INTERRUPTED_format_indexing_info: loop único em vez de duas list comprehensions_format_issn_info(issue): refatorado com early return para reduzir indentação_former_dict_journal_history→_format_journal_history_entrykey_to_issn→key_to_valueSinalização de possível bug de negócio:
Adicionado TODO em
_format_issn_type: quandoissn_print == issn_scielo, o código original atribui'ONLIN'aov35, o que parece invertido. Mantido o comportamento original, mas marcado para revisão.Onde a revisão poderia começar?
journal/formats/articlemeta_format.py— é a base (o issue formatter importa o journal formatter). As mudanças mais relevantes estão em:titles_in_database_medline_secs(padrão de cache manual)_format_journal_history(lógica de subfield_b simplificada)_format_issn_type(TODO sobre possível inversão de lógica)Depois seguir para
issue/formats/articlemeta_format.py, começando porarticle_qse_format_issn_info.Como este poderia ser testado manualmente?
main— os valores devem ser idênticos (exceto se o bug de contagem em_format_article_infoestava ativo, onde a contagem pode mudar para journals com múltiplos issues compartilhando artigos)Noneemvolume,number,journal_acron,journal_toc— a branchmainpode lançarAttributeErrorouTypeError, esta branch deve retornar resultado sem errolru_cachenão deve mais ocorrerAlgum cenário de contexto que queira dar?
Este refactoring foi motivado pela investigação de consumo de memória em produção. O
@lru_cacheem métodos de instância é um antipattern conhecido em Python — o decorator armazena obound method(que incluiself) como chave, efetivamente criando uma referência circular que impede o GC de coletar a instância. Em um servidor WSGI com muitos requests, isso causa leak progressivo.As mudanças são conservadoras: nenhuma lógica de negócio foi alterada intencionalmente (com exceção da correção do queryset em
_format_article_info). O TODO em_format_issn_typeé uma sinalização para revisão futura, não uma mudança de comportamento.Screenshots
N/A (refactoring interno sem impacto visual)
Quais são tickets relevantes?
Referências