Odoo 13 — In-Memory ORM — A maior refatoração do core desde o OpenERP 8

Fomos acompanhar o lançamento da nova versão do Odoo (ERP de código aberto escrito em Python e Javascript) na Bélgica, durante o Odoo Experience.

Quem acompanha o Odoo como a gente faz bastante tempo, desde 2011, nas versões anteriores a 8. Sabe explicar o quanto a vida do desenvolvedor e do usuário melhorou.

Com o lançamento da nova api em 2014, na versão 8, o ORM passou a ser muito similar ao do DJANGO. Mas a nova versão veio com novidades técnicas impressionantes:

Performance: Tempo das transações

As transações cotidianas estão abaixo dos 100 milissegundos (ms), por exemplo:

  • Obter os dados de 2000 clientes e fornecedores ~40ms;
  • Criar 5 clientes e/ou fornecedores ~30ms;
  • Criar 4 orçamentos com 3 itens em cada um ~60ms;
  • Criar e confirmar os lançamentos contábeis de uma fatura com 2 linhas ~110ms;
  • Modificar 10 itens de uma venda, por exemplo, quando uma operação de estoque informa que eles foram entregues ~20ms;
  • Duplicar e validar 3 movimentações de estoque ~60ms;
  • Duplicar uma ordem de compra e confirmá-la ~95ms.

O criador do Gmail, Paul Buchheit, tinha uma regra: toda interação deveria ser inferior a 100ms. Por quê? Porque 100ms é o limite em que as interações parecem instantâneas. Essa é uma das razões pelas quais quando usamos o Gmail sentimos que ele é rápido e eficiente.[2]

Gráfico comparativo de tempo das operações quando realizadas nas versões 12 e 13

Performance: Redução da quantidade de consultas SQL

O ORM foi refatorado para reduzir a quantidade de consultas SQL efetuadas em cada operação, isto faz com que seja possível obter:

  • Grande impacto em grandes bancos de dados;
  • Menos problemas de lock em tabelas;
  • Menos problemas de concorrência.

A principal característica de qualquer sistema de banco de dados é a implementação do acesso simultâneo aos dados respeitando as restrições e propriedades do sistema quando várias transações estão modificando seu estado simultaneamente.

A associação do PostgreSQL(desde 1996) e ORM do Odoo(desde 2005), cria um conjunto de ferramentas extremamente maduras (de código aberto e com muitos contribuidores) que proporciona incontáveis benefícios para os desenvolvedores e para os usuários.

O PostgreSQL é único entre os bancos de dados de código aberto no suporte a transações ACID complexas e simultâneas.

Para garantir que transações complexas possam ser executadas com segurança ao mesmo tempo, o PostgreSQL usa várias camadas de bloqueios para serializar alterações em seções críticas do banco de dados. As transações são executadas simultaneamente até tentarem obter um bloqueio conflitante, por exemplo, quando atualizam a mesma linha. Nesse caso, a primeira transação a adquirir o bloqueio pode prosseguir e a segunda espera até que a primeira transação seja confirmada ou interrompida.[3]

Gráfico relativo entre as consultas SQL quando realizadas nas versões 12 e 13

Como estas melhorias de performance foram feitas?

Cache único

Ao invés de um cache para cada “enviroment”/contexto, é criado apenas um cache.

Isto reduz drasticamente o número de leituras no banco de dados, principalmente em operações complexas que mantêm vários contextos e algumas vezes podem demandar o sudo durante uma transação.

Nos casos em que os cálculos de um método dependam de dados disponíveis no cache, deve-se decorar o método com o novo “decorator” criado, o @dependent_context. Por exemplo:

Ou seja, sempre que em um método você utilize dados do contexto decore a mesma com os campo utilizados.

Preferência por updates In-Memory

Pra quem ainda é novo no Odoo, é bom lembrar que o mesmo utiliza o padrão “Active Record Pattern”.

Em Engenharia de softwareactive record é um padrão de projeto encontrado em softwares que armazenam seus dados em Banco de dados relacionais. Assim foi nomeado por Martin Fowler em seu livro Patterns of Enterprise Application Architecture. A interface de um certo objeto deve incluir funções como por exemplo Inserir(Insert), Atualizar(Update), Apagar(Delete) e propriedades que correspondam de certa forma diretamente às colunas do banco de dados associado.[4]

Ou seja, anteriormente, cada vez que atribuíamos um valor a uma variável era executado um update no banco de dados. Um bom programador sabia muito bem como contornar este problema, salvando os dados em um dicionário, por exemplo, e chamando o método write.

Agora, o flush torna o desenvolvimento um ato fluido e também beneficia a leitura de código. Existem vários exemplos do uso do flush no core do Odoo, recomendo a leitura dos mesmos antes de iniciar seus projetos na v13.

Dependências em memória

Esta nova funcionalidade é uma extensão da citada acima, aplicada quando os dados para os cálculos dos campos calculados que dependem de modelos relacionais são obtidos do cache.

Por exemplo, em um pedido de vendas:

Atrasar os cálculos de campos calculados

Nas versões anteriores à versão 13, quando alterávamos um valor em um campo que tinha outros campos que dependiam dele, seus dependentes eram recalculados.

Por exemplo: sempre que alterávamos uma linha do pedido em um laço de repetição, os totais do pedido eram recalculados.

Isso fazia com que o tempo de computação crescesse exponencialmente à medida que a operação tinha muitos itens. Uma nota fiscal com 400 itens tornava o sistema extremamente lento.

Nosso amigo Leonardo Rochael Almeida fez até modificações no core para deixar o cálculo para depois em versões anteriores apenas com a instalação de um módulo.

https://github.com/kmee/kmee_odoo_addons/tree/8.0/base_delay_computed

Atrasar as escritas SQL

Atrasa as escritas SQL o maior tempo possível:

  • Salvando todas as mudanças em: self.env.all.towrite;
  • Otimizando os updates para minimizar o número de queries, atualizando vários registros ou vários campos de uma só vez.

Otimizar as árvores de dependências

Anteriormente a árvore de dependências ficava por conta da experiência do programador. Agora como todos os campos são calculados posteriormente, o Odoo monta uma árvore otimizada de dependências e as calculas.

Otimiza o browse()

Quem já precisou debugar o core do ORM sabe a complexidade e a quantidade de conversões que o mesmo faz para tornar as coisas fáceis para os programadores.

  1. O cache agora está sendo em formato similar aos dados obtidos do PostgreSQL:
  • None ao invés de False,
  • 4 ao invés de (4,) para os many2one.

2. Não realiza a conversão para o formato read(), para realizar a o browse()

Conclusão

O Odoo 13 está cheio de novidades interessantes que vão facilitar a vida dos desenvolvedores menos experientes. Essa versão vai permitir o uso do sistema em empresas maiores sem muitos malabarismos. E, não menos importante, vai deixar a vida do usuário mais ágil.

[1] https://docs.google.com/presentation/d/1cHuXcAz3dxqNdNt8nE4nDXmy_osC5_yPe70-SyBidkE/edit#slide=id.g60b92c9125_0_8

[2] https://medium.com/@TWiStartups/email-productivity-with-superhuman-f4d934fe8668

[3] https://www.postgresql.org/docs/current/mvcc-intro.html

[4] https://pt.wikipedia.org/wiki/Active_record