Utilizando Pool de conexões com MySQL2 em uma aplicação Next.js

marcelo-albuquerque

Marcelo Albuquerque

Posted on November 12, 2023

Utilizando Pool de conexões com MySQL2 em uma aplicação Next.js

Uma breve análise da utilização do Pool de conexões do MySQL.

Considerações

Explicando o Problema

Trabalhando em uma aplicação comecei a receber uma mensagem de erro muito incomoda: Too many connections, o código errno é o 1040.

Bem, obviamente havia algo errado. Antes de analisar o código da função para conexão ao bando de dados, observei que minha aplicação realizava muitas chamadas desnecessárias, então passei a guardar algumas informações em states e trabalhei na otimização geral do código da aplicação.

Essa etapa foi muito importante, pois não podemos simplesmente sair disparando chamadas para o banco de dados de forma irresponsável.

Então enfim cheguei no código da função que realiza a conexão com o banco de dados e comecei a verificar - antes de realizar qualquer mudança - a quantidade conexões ao banco de dados toda vez que carregava a aplicação:

show status where `variable_name` = 'Threads_connected';
Enter fullscreen mode Exit fullscreen mode

O que observei foi um número crescente de conexões, que invariavelmente poderia chegar ao limite do meu banco de dados na AWS, utilizo o Amazon RDS for MySQL.

Analisando a implementação atual

Agora observe o meu código de conexão ao banco de dados:

// libs/mysql/index.ts

import mysql from 'mysql2/promise';

/*
  Com essa implementação, o número de conexões precisa ser muito controlado.
*/
export async function MySQL() {
  const connection = await mysql.createConnection({
    host: process.env.MYSQL_HOST,
    user: process.env.MYSQL_USERNAME,
    database: process.env.MYSQL_DATABASE,
    password: process.env.MYSQL_PASSWORD
  });

  return connection
}
Enter fullscreen mode Exit fullscreen mode

O detalhe principal é que estou utilizando o método createConnection. E esta implementação precisa ser cuidadosamente estudada para ser implementada, pois toda chamada a ela irá gerar uma nova conexão ao banco de dados e claramente atingir o limite passa a ser apenas uma questão de tempo ou simplesmente de escalabilidade da aplicação.

Então comecei a ir atrás de soluções, estudar um pouco mais sobre implementações de conexão ao banco de dados MySQL.

Já havia ouvido sobre Pool de Conexões, mas nunca dei a devida atenção.

Um pool de conexões é um cache de conexões de banco de dados que são compartilhadas e reutilizadas para melhorar a latência e o desempenho da conexão. Quando seu aplicativo precisa de uma conexão de banco de dados, ele pega uma emprestada do pool temporariamente. Assim que ele termina de usar a conexão, ela é devolvida ao pool para ser reutilizada da próxima vez em que o aplicativo precisar de uma conexão de banco de dados.

Aplicando a solução

Ótimo, conceitualmente é fantástico. A biblioteca que utilizo: MySQL 2 possuí este recurso, então bastava implementar e realizar os testes:

// /libs/mysql/index.ts

import mysql from 'mysql2'

export async function MySQL() {
  const connection = mysql.createPool({
    host: process.env.MYSQL_HOST,
    user: process.env.MYSQL_USERNAME,
    database: process.env.MYSQL_DATABASE,
    password: process.env.MYSQL_PASSWORD
  });

  const pool = connection.promise()

  return pool
}
Enter fullscreen mode Exit fullscreen mode

Observe que agora estou utilizando o método createPool.

Estou exportando uma função assíncrona, observe que a connection gera uma promise.

Vou deixar aqui apenas um exemplo de como utilizar esse Pool de conexões para gerar uma consulta no banco de dados:

// /api/consulta/route.ts

import { NextRequest, NextResponse } from 'next/server'
import MySQL from '/libs/mysql'

export async function GET( req: NextRequest ) {
  try {
    const mysql = await MySQL()

    const query = `SELECT * FROM produtos`
    const [ rows ] = await mysql.execute( query )

    // Extremamente importante. Encerrar a conexão.
    await mysql.end()

    return NextResponse.json( rows )
   } catch ( error ) {
    return NextResponse.json( error )
   }
}
Enter fullscreen mode Exit fullscreen mode

Conclusão

Ao passar a utilizar o Pool de conexões observei que o número de conexões ao banco de dados diminuiu drasticamente, utilizando basicamente apenas uma única conexão.

Algo extremamente importante nessa implementação é o encerramento da conexão: await mysql.end() principalmente utilizando funções assíncronas, caso contrário, o resultado seria o mesmo de utilizar o método createConnection.

Infelizmente não dediquei o devido tempo ao estudo do MySQL, com certeza teria percebido esse problemas antes, mas deixo aqui para aqueles que de alguma forma possam estar enfrentando problemas semelhantes.

Comandos SQL que podem ser úteis

Exibir a quantidade de conexões ao banco de dados:

SHOW STATUS WHERE `variable_name` = 'Threads_connected';
Enter fullscreen mode Exit fullscreen mode

Para analisar a proveniência das conexões:

SHOW processlist
Enter fullscreen mode Exit fullscreen mode

Exibe o número máximo de conexões que seu banco de dados suporta:

SHOW VARIABLES LIKE "max_connections";
Enter fullscreen mode Exit fullscreen mode

Referências

Também quero destacar uma postagem no Medium que me foi de grande ajudar, até considero que essa postagem deveria fazer parte da documentação oficial do MySQL 2. O autor se chamar Jorgen Lundgren.

Também obtive ajuda em uma pergunta no Stack Overflow onde nas respostas os usuários citaram diversos comandos úteis para o MySQL.

💖 💪 🙅 🚩
marcelo-albuquerque
Marcelo Albuquerque

Posted on November 12, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related