Utilizando Pool de conexões com MySQL2 em uma aplicação Next.js
Tags: mysql2, pool, nextjs
Published: 2023-11-12 20:26:42

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

### Considerações

- Next.js com a [App Router](https://nextjs.org/docs/app)
- Banco de dados MySQL utilizando [Amazon RDS for MySQL](https://aws.amazon.com/pt/rds/mysql/)
- Pacote [MySQL2](https://www.npmjs.com/package/mysql2)

## 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:

```sql
show status where `variable_name` = 'Threads_connected';
```

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](https://aws.amazon.com/pt/rds/mysql/).

## Analisando a implementação atual

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

```ts
// 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
}
```

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.](https://cloud.google.com/sql/docs/mysql/manage-connections?hl=pt-br#pools)

## Aplicando a solução

Ótimo, conceitualmente é fantástico. A biblioteca que utilizo: [MySQL 2](https://www.npmjs.com/package/mysql2#using-connection-pools) possuí este recurso, então bastava implementar e realizar os testes:

```ts
// /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
}
```

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:

```ts
// /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 )
   }
}
```

## 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:

```sql
SHOW STATUS WHERE `variable_name` = 'Threads_connected';
```

Para analisar a proveniência das conexões:

```sql
SHOW processlist
```

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

```sql
SHOW VARIABLES LIKE "max_connections";
```

### Referências

Também quero destacar uma postagem no [Medium](https://blog.devgenius.io/nodejs-how-to-close-your-mysql-connections-and-why-a7cc7287132b) 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](https://medium.com/@jorgen_90767).

Também obtive ajuda em uma pergunta no [Stack Overflow](https://stackoverflow.com/questions/7432241/mysql-show-status-active-or-total-connections) onde nas respostas os usuários citaram diversos comandos úteis para o MySQL.