Executando verificação de segurança...
2

Engatinhando do Monolítico ao Microsserviços: Pooling de DbContext's

Introdução

Meu nome é Otávio Villas Boas e estou desenvolvendo um projeto de estudos sobre arquitetura de software, políticas de resiliência, padrões de projeto, padrões de design, entre outros conceitos, em um contexto de negócio relacionado a lojas virtuais (também chamado de ecommerce's). A ideia desse projeto, apesar de ser pessoal, é levantar requisitos, cair em problemas técnicos, de modo na qual eu produza conteúdos sobre tais conceitos técnicos assim como levantar discussões a respeito desses problemas que eu encontrar. Assim, além de estudar eu consigo compartilhar/discutir a experiência e conhecimento que adquiri ao longo do processo.

Tecnologias Utilizadas no Projeto

  • .NET 7
  • ASP NET CORE
  • PostgreeSQL
  • Entity Framework Core

Contextualizando

Imaginem um cenário na qual o Ecommerce que estou desenvolvendo tenham grande de quantidade requisições, assim, o acesso ao banco de dados é feito muitas vezes, não apenas uma única vez. Por outro lado, optei por realizar a escolha de um ORM para acesso a dados no banco de dados transacional, no entanto, a cada vez que uma requisição é feita, é feita a instância de um objeto para fazer o traqueamento e mapeamento dos dados em um contexto a nível de aplicação dos dados que estão no banco de dados.

Problema

Acesso simultâneos ao banco de dados em grandes quantidades provocam uma instanciação e configuração de serviços do objeto de mapeamento e assim gerando um gargalo no momento de se realizar transações/consultas/comandos no banco de dados, visto que, muitos objetos com mesmas configurações são criados simultaneamente.

Solução

Adotei uma solução providenciada pelo Entity Framework Core (ORM amplamente utilizado no .NET), na qual posso reutilizar instâncias do objeto responsável por configurar/traquear/mapear em objetos os dados presente no banco de dados já configuradas em outras requisições por meio do Pooling de DbContext's. Assim, ao invés de criar um novo objeto DbContext, o Entity Framework Core por baixo dos panos armazena esse objeto em memória e caso seja necessário reutilizar ele pega de volta a configuração de serviços presente nesse DbContext (presente já na memória).

Veja a seguir a configuração do Entity Framework Core:

public static void ApplyInfrascructureDependenciesConfiguration(this IServiceCollection serviceCollection,
        string? npgsqlConnectionString)
    {
        #region Entity Framework Core Context Configuration

        serviceCollection.AddDbContextPool<DataContext>(
            optionsAction: p => p.UseNpgsql(
                connectionString: npgsqlConnectionString,
                npgsqlOptionsAction: p => p.MigrationsAssembly("OVB.Monolithic.Ecommerce.Infrascructure")));

        #endregion
    }

Alguns Pontos

  1. A primeira requisição nunca irá ter a mesma performance em relação as próximas.
  2. Uso de Pooling provoca um aumento do uso da memória ram no servidor.
  3. O não uso do Pooling provoca um maior números de alocações e desalocações na memória, assim como, um maior nível de utilização do processador.

Discussão

Considerando o que foi apresentado nesse post, o que vocês acham da utilização de pooling para contextos técnicos não funcionais como esse?

Observações: Caso você encontrem algum outro problema ou solução a ser adotada, por favor, deixe nos comentários. A ideia é que sejam levantadas problemas reais (mesmo que verdadeiramente não exista para o contexto atual do projeto).

Carregando publicação patrocinada...
2

Cara muito interessante seu post, é legal ver coisas de .NET por aqui.

Eu recentemente fiz uma API Aspnet core 3.1 que recebe de 3 a 4 milhões de requisições por dia e nesse projeto optei por não usar ORM exatamente por não saber como funcionaria por debaixo dos panos.

Dentro das condições de implementação o banco é oracle Exadata, e o servidor é um monstro com balanceamento de carga.

Está na minha lista de estudos NoSQL, escalabilidade, etc. porque agora o problema ta em contabilizar os registos ingeridos.

Ja ouviu falar do Dapper ?

1

@juliofpsm, fico feliz em encontrar pessoas de .NET aqui pela plataforma. Apesar do post parecer algo mais "específico" do .NET ele pode ser mais genérico, e pessoas que utilizam outros ORM's de outras linguagems pode ter um gatilho a conhecer mais afundo ORM's que elas utilizam de modo a trazer melhora de performance a suas aplicações.

Quanto ao projeto de desenvolvimento que você realizou, realmente, a carga é absurdamente alta e o trabalho apenas do desenvolvedor back-end não é o suficiente. Ainda não cheguei aprender nada a respeito de Balanceamento de Carga em Infra, mas é um dos tópicos que irei aprender futuramente.

Quanto ao Dapper, já cheguei a ouvir falar, mas nunca cheguei a implementá-lo verdadeiramente, gosto de utilizar os framework's quando entendo como eles funcionam por baixo dos panos para evitar frustações quanto sua utilização.