---
title: "Deadpool vs bb8 em Rust: qual pool usar? | Rust Brasil"
url: "https://rustlang.com.br/blog/deadpool-vs-bb8-pool-conexoes-rust-2026/"
markdown_url: "https://rustlang.com.br/blog/deadpool-vs-bb8-pool-conexoes-rust-2026.MD"
description: "Compare deadpool, bb8 e pools nativos do SQLx em Rust. Veja diferenças, exemplos com PostgreSQL e Redis, timeouts, observabilidade e decisão em produção."
date: "2026-09-23"
author: "Equipe Rust Brasil"
---

# Deadpool vs bb8 em Rust: qual pool usar? | Rust Brasil

Compare deadpool, bb8 e pools nativos do SQLx em Rust. Veja diferenças, exemplos com PostgreSQL e Redis, timeouts, observabilidade e decisão em produção.


**Se o projeto usa SQLx, escolha primeiro o pool nativo do SQLx; se usa `tokio-postgres`, Redis ou recursos heterogêneos, escolha Deadpool pela variedade de adaptadores e configuração prática; use bb8 quando você quer uma abstração enxuta baseada em `ManageConnection` ou quando o driver já oferece integração bb8.** O critério decisivo não é um benchmark isolado: é compatibilidade com o driver, política de reciclagem, timeout de aquisição, observabilidade e comportamento sob saturação.

Um pool de conexões parece detalhe de infraestrutura até a API receber mais requisições do que o banco suporta. Nesse momento, a diferença entre “aguardar com limite” e “criar conexões sem controle” vira latência, erro 5xx ou indisponibilidade. Este guia compara **Deadpool vs bb8**, mostra exemplos com PostgreSQL e Redis e explica quando evitar ambos em favor do [SQLx](/ecossistema/sqlx/).

## Resposta rápida: Deadpool, bb8 ou SQLx Pool?

| Situação | Escolha inicial | Por quê |
|---|---|---|
| Aplicação já usa SQLx | **`sqlx::Pool`** | integração nativa com queries, transactions e drivers |
| `tokio-postgres` com configuração pronta | **`deadpool-postgres`** | adapter consolidado e reciclagem configurável |
| Redis assíncrono | **`deadpool-redis`** | integração direta e ergonomia consistente |
| Driver fornece `bb8::ManageConnection` | **bb8** | abstração pequena e familiar |
| Recurso customizado reutilizável | Deadpool ou bb8 | escolha pela API de manager e pelas métricas necessárias |
| SQLite local simples | frequentemente pool nativo do driver | limite de escrita e modelo do SQLite importam mais que a marca do pool |
| Uma única conexão em CLI curta | **nenhum pool** | pooling adiciona complexidade sem ganho real |

A regra de arquitetura é: **use o pool mais próximo do driver**. Um wrapper extra cria mais estados, tipos de erro e pontos de configuração. Só troque quando houver uma necessidade concreta que o pool nativo não atende.

## O que um pool realmente faz

Abrir uma conexão com banco ou serviço remoto pode envolver DNS, TCP, TLS, autenticação e negociação de sessão. Repetir isso em toda requisição desperdiça tempo e recursos. O pool mantém um conjunto limitado de conexões prontas e executa um ciclo:

1. uma task solicita um recurso;
2. o pool entrega uma conexão ociosa ou cria uma até o limite;
3. a task usa a conexão;
4. ao liberar o guard, a conexão retorna ao pool;
5. antes de reutilizá-la, o pool pode validar ou reciclar o recurso;
6. se o limite está ocupado, a task aguarda — idealmente com timeout.

O pool não aumenta magicamente a capacidade do PostgreSQL. Ele **impõe orçamento**. Se o banco aguenta 40 conexões da aplicação e você inicia quatro réplicas, configurar 40 por réplica pode abrir 160 conexões. O cálculo precisa considerar todas as instâncias, migrações, ferramentas administrativas e margem de segurança.

## Deadpool: adapters prontos e recursos assíncronos

Deadpool é uma família de crates para gerenciar objetos reutilizáveis em aplicações assíncronas. O crate base oferece a abstração; crates como `deadpool-postgres` e `deadpool-redis` conectam o pool a clientes concretos.

Ele costuma encaixar bem quando:

- o projeto usa `tokio-postgres` diretamente;
- há PostgreSQL e Redis no mesmo serviço;
- você quer configuração serializável e factories por adapter;
- a política de reciclagem precisa de ajuste;
- o time prefere uma API de pool consistente entre recursos diferentes.

### Exemplo com `deadpool-postgres`

O `Cargo.toml` varia conforme as versões estáveis e os recursos TLS escolhidos. A estrutura típica é:

```toml
[dependencies]
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
tokio-postgres = "0.7"
deadpool-postgres = "0.14"
```

Criação e uso do pool:

```rust
use deadpool_postgres::{Manager, ManagerConfig, Pool, RecyclingMethod};
use tokio_postgres::{Config, NoTls};

fn criar_pool(database_url: &str) -> Result<Pool, Box<dyn std::error::Error>> {
    let pg_config: Config = database_url.parse()?;
    let manager = Manager::from_config(
        pg_config,
        NoTls,
        ManagerConfig {
            recycling_method: RecyclingMethod::Fast,
        },
    );

    let pool = Pool::builder(manager)
        .max_size(16)
        .build()?;

    Ok(pool)
}

async fn buscar_nome(
    pool: &Pool,
    usuario_id: i64,
) -> Result<Option<String>, Box<dyn std::error::Error>> {
    let client = pool.get().await?;
    let row = client
        .query_opt(
            "select nome from usuarios where id = $1",
            &[&usuario_id],
        )
        .await?;

    Ok(row.map(|r| r.get("nome")))
}
```

`NoTls` simplifica o exemplo, mas não é recomendação universal. Em produção, configure TLS de acordo com o provedor e valide certificados. O ponto importante é que o valor retornado por `pool.get()` controla o empréstimo; quando sai de escopo, a conexão pode voltar ao pool.

### Reciclagem não é detalhe cosmético

Uma conexão pode continuar aberta no cliente e já estar inválida no servidor. Reinícios, proxies, timeouts de rede e mudanças de credencial criam conexões zumbis. A política de reciclagem decide quanto trabalho fazer antes de entregar o recurso novamente.

- **Validação mais leve:** menor overhead, mas o primeiro comando pode descobrir uma conexão quebrada.
- **Validação por query:** mais confiança antes do uso, ao custo de uma ida adicional ao banco.
- **Recriação após erro:** remove o recurso defeituoso e abre outro dentro do orçamento.

Não copie uma política sem medir. Uma query de validação em toda aquisição pode multiplicar tráfego; validação leve demais pode elevar erros logo após failover.

## bb8: uma abstração pequena com `ManageConnection`

bb8 modela o pool em torno de um manager que sabe:

- criar uma conexão;
- verificar se ela continua válida;
- decidir se ela está quebrada.

Essa separação deixa a biblioteca genérica e torna o contrato explícito. bb8 é uma boa escolha quando a stack já possui um adapter, como `bb8-postgres`, ou quando você quer implementar um manager pequeno para um protocolo interno.

### Exemplo com `bb8-postgres`

```toml
[dependencies]
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
tokio-postgres = "0.7"
bb8 = "0.8"
bb8-postgres = "0.8"
```

```rust
use bb8::Pool;
use bb8_postgres::PostgresConnectionManager;
use tokio_postgres::NoTls;

type PgPool = Pool<PostgresConnectionManager<NoTls>>;

async fn criar_pool(
    database_url: &str,
) -> Result<PgPool, Box<dyn std::error::Error>> {
    let manager = PostgresConnectionManager::new_from_stringlike(
        database_url,
        NoTls,
    )?;

    let pool = Pool::builder()
        .max_size(16)
        .build(manager)
        .await?;

    Ok(pool)
}

async fn buscar_email(
    pool: &PgPool,
    usuario_id: i64,
) -> Result<Option<String>, Box<dyn std::error::Error>> {
    let conn = pool.get().await?;
    let row = conn
        .query_opt(
            "select email from usuarios where id = $1",
            &[&usuario_id],
        )
        .await?;

    Ok(row.map(|r| r.get("email")))
}
```

A experiência no handler é parecida com Deadpool: adquirir, executar e liberar. A diferença aparece mais na configuração, no contrato do manager, nos adapters disponíveis e no modo como o projeto instrumenta estado e erros.

## Deadpool vs bb8: comparação prática

| Critério | Deadpool | bb8 |
|---|---|---|
| Abstração central | manager + objetos gerenciados | `ManageConnection` |
| PostgreSQL | `deadpool-postgres` | `bb8-postgres` |
| Redis | adapter dedicado popular | depende do adapter escolhido |
| Recursos não-SQL | um dos focos do ecossistema | possível com manager customizado |
| Curva inicial | simples com adapters prontos | simples quando já existe manager |
| Reciclagem | políticas e hooks do adapter | validação/quebra no manager |
| Melhor argumento | consistência entre recursos | contrato genérico enxuto |
| Razão ruim para escolher | “parece mais rápido” sem carga real | “tem menos código” ignorando operação |

Em aplicações normais, a latência da query e da rede domina diferenças minúsculas de bookkeeping do pool. Compare sob o workload real: quantidade de tasks, duração das queries, p95 de espera, falhas de conexão e comportamento durante restart do banco.

## Quando o pool nativo do SQLx é a resposta

Se o projeto já usa SQLx, `PgPool`, `MySqlPool` e `SqlitePool` integram aquisição, executors e transactions. O caminho idiomático é:

```rust
use sqlx::postgres::PgPoolOptions;
use std::time::Duration;

async fn criar_pool(database_url: &str) -> Result<sqlx::PgPool, sqlx::Error> {
    PgPoolOptions::new()
        .max_connections(16)
        .min_connections(2)
        .acquire_timeout(Duration::from_secs(2))
        .idle_timeout(Duration::from_secs(600))
        .connect(database_url)
        .await
}
```

Você ganha um único modelo de erro e integração direta com `query!`, migrations e transactions. Colocar Deadpool ou bb8 em volta de SQLx normalmente duplica responsabilidade.

Use outro pool apenas se ele estiver gerenciando **outro tipo de recurso**. Exemplo: SQLx para PostgreSQL e `deadpool-redis` para Redis é uma combinação coerente; SQLx dentro de bb8 dentro de um state Axum raramente é.

Para uma comparação de acesso a dados mais ampla, veja [Diesel vs SQLx](/artigos/diesel-vs-sqlx/) e o guia de [migrations SQLx com PostgreSQL](/blog/sqlx-migrations-rust-postgresql-ci-deploy-2026/).

## Redis com Deadpool: o pool também vale fora do SQL

Redis é um caso comum porque conexões e multiplexação dependem do cliente e do padrão de carga. Com `deadpool-redis`, a estrutura costuma ser:

```rust
use deadpool_redis::{Config, Pool, Runtime};
use deadpool_redis::redis::AsyncCommands;

fn criar_pool_redis(url: &str) -> Result<Pool, deadpool_redis::CreatePoolError> {
    let config = Config::from_url(url);
    config.create_pool(Some(Runtime::Tokio1))
}

async fn buscar_cache(
    pool: &Pool,
    chave: &str,
) -> Result<Option<String>, Box<dyn std::error::Error>> {
    let mut conn = pool.get().await?;
    let valor: Option<String> = conn.get(chave).await?;
    Ok(valor)
}
```

Antes de adotar um pool Redis, confira se o cliente e o modo escolhido já oferecem multiplexação adequada. Pooling não é reflexo automático: ele é útil quando a concorrência, o protocolo e as garantias do cliente justificam múltiplos recursos.

O guia de [Redis como cache em backends Rust](/blog/rust-redis-cache-backend-2026/) cobre TTL, stampede, invalidação e fallback — problemas que um pool sozinho não resolve.

## Integração com Axum sem espalhar o pool

O pool deve entrar no state da aplicação e permanecer na borda da infraestrutura. Handlers podem chamar um repositório; regras de negócio não precisam conhecer `deadpool_postgres::Pool`.

```rust
use axum::{extract::State, http::StatusCode, Json};
use serde::Serialize;
use std::sync::Arc;

#[derive(Clone)]
struct AppState {
    usuarios: Arc<dyn UsuarioRepository>,
}

#[derive(Serialize)]
struct UsuarioDto {
    id: i64,
    nome: String,
}

#[axum::async_trait]
trait UsuarioRepository: Send + Sync {
    async fn por_id(&self, id: i64) -> anyhow::Result<Option<UsuarioDto>>;
}

async fn obter_usuario(
    State(state): State<AppState>,
    axum::extract::Path(id): axum::extract::Path<i64>,
) -> Result<Json<UsuarioDto>, StatusCode> {
    state
        .usuarios
        .por_id(id)
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
        .map(Json)
        .ok_or(StatusCode::NOT_FOUND)
}
```

A implementação concreta guarda Deadpool, bb8 ou SQLx. Essa fronteira facilita testes e evita uma migração dolorosa se o acesso a dados mudar. Para o restante da stack web, continue pelo [tutorial de API REST com Axum](/tutoriais/api-rest-axum/) e pelo guia de [serviços resilientes com Tower e Axum](/blog/rust-servicos-resilientes-tower-axum-2026/).

## Timeout, backpressure e saturação

Um pool cheio cria uma fila invisível de tasks aguardando. Sem timeout, a latência cresce até o balanceador desistir. Sem limite externo, milhares de tasks podem consumir memória enquanto esperam 16 conexões.

Uma política robusta combina:

1. **`max_size` coerente** com a capacidade do banco;
2. **timeout curto de aquisição**, abaixo do timeout HTTP total;
3. **limite de concorrência** antes do handler ou repositório;
4. **timeout de query** no servidor/cliente;
5. **cancelamento** quando a requisição do usuário desaparece;
6. **resposta diferenciada** para saturação temporária, sem vazar detalhes.

Exemplo de timeout em torno da aquisição quando a API do pool/projeto não o aplica diretamente:

```rust
use std::time::Duration;

let conn = tokio::time::timeout(
    Duration::from_secs(2),
    pool.get(),
)
.await
.map_err(|_| AppError::PoolSaturado)??;
```

Não segure a conexão enquanto faz chamada HTTP externa, espera fila ou processa um arquivo. Adquira o mais tarde possível e libere logo após a transaction/query. Uma conexão presa durante trabalho não relacionado reduz throughput do sistema inteiro.

## Observabilidade: meça a espera, não apenas a query

Duas latências importam:

- **tempo de aquisição:** quanto uma task esperou pelo pool;
- **tempo de operação:** quanto a query/comando levou após obter conexão.

Se você mede só a query, uma requisição pode parecer ter executado SQL em 20 ms enquanto ficou 1,8 s esperando uma conexão. Registre:

- conexões em uso e ociosas;
- tamanho máximo configurado;
- aquisições bem-sucedidas;
- timeouts de aquisição;
- erros de criação/reciclagem;
- histograma de espera;
- histograma de query por operação normalizada.

Não use SQL completo, ID de usuário ou URL de banco como label. O guia de [Prometheus e métricas em Rust](/blog/prometheus-metrics-rust-axum-producao-2026/) mostra como evitar cardinalidade explosiva; [tracing](/ecossistema/tracing/) ajuda a correlacionar a espera do pool com a requisição.

## Como dimensionar o pool

Não existe fórmula universal, mas existe método:

1. descubra o limite seguro de conexões do banco;
2. reserve conexões para administração, migrations e jobs;
3. divida o restante pelo máximo de réplicas da aplicação;
4. comece conservador;
5. rode carga representativa;
6. observe espera, CPU do banco, locks, I/O e p95/p99;
7. aumente apenas se o banco tem folga e a espera é realmente o gargalo.

Exemplo: PostgreSQL aceita 100 conexões; você reserva 20; pode ter até quatro réplicas. O teto teórico seria 20 por réplica, mas começar com 12–16 deixa margem para deploy rolling e variação operacional.

Mais conexões podem piorar performance ao aumentar contenção, memória e concorrência de disco. Queries lentas, índices ausentes e transactions longas não são corrigidos com `max_size = 200`.

## Testes que valem mais que um benchmark sintético

### Esgotamento

Configure pool de tamanho 2, segure duas conexões e confirme que a terceira aquisição expira no tempo esperado.

### Conexão inválida

Reinicie o banco ou encerre uma sessão e verifique se o pool descarta/recicla o recurso sem envenenar requisições seguintes.

### Transaction longa

Force uma operação lenta e confirme que o limite de concorrência protege o restante da API.

### Shutdown

Interrompa o serviço durante carga e valide graceful shutdown: novas requisições param, operações em voo encerram dentro do prazo e recursos são liberados.

### Réplicas

Teste o total agregado. Um container local não revela o que acontece quando seis pods multiplicam o pool.

Combine esses testes com as estratégias de [testes em Rust](/blog/testes-rust-estrategias-boas-praticas-2026/) e instrumente o cenário antes de comparar bibliotecas.

## Erros comuns

### Escolher por stars ou microbenchmark

Adapters, manutenção e comportamento de falha pesam mais que nanossegundos no caminho feliz.

### Não configurar timeout de aquisição

A fila cresce e transforma saturação breve em cascata de timeouts.

### Abrir um pool por requisição

O pool deve ser criado no bootstrap e compartilhado. Criá-lo por handler elimina o benefício e pode atacar o banco com novas conexões.

### Manter conexão durante trabalho externo

Chamada a API, renderização, upload e processamento CPU-bound não deveriam prender uma conexão ociosa.

### Ignorar o número de réplicas

`max_size` é por processo. Autoscaling multiplica conexões.

### Tratar pool como cache de resultado

Pool reutiliza conexões; cache reutiliza dados. São problemas diferentes.

### Misturar três pools para o mesmo driver

Padronize por datastore. SQLx Pool para PostgreSQL e Deadpool para Redis pode fazer sentido; dois pools concorrentes para o mesmo PostgreSQL quase nunca fazem.

## Checklist de decisão para produção

- [ ] O driver já possui pool nativo bem integrado?
- [ ] Existe adapter mantido para Deadpool ou bb8?
- [ ] O pool suporta timeout de aquisição e limites explícitos?
- [ ] A reciclagem funciona após restart/failover?
- [ ] As métricas mostram espera, uso e erro?
- [ ] O tamanho considera todas as réplicas?
- [ ] Transactions são curtas e conexões são liberadas cedo?
- [ ] Há backpressure antes que milhares de tasks aguardem?
- [ ] TLS e credenciais são configurados fora do código?
- [ ] O shutdown foi testado sob carga?
- [ ] O repositório esconde o tipo concreto do pool do domínio?
- [ ] O time documentou por que escolheu Deadpool, bb8 ou SQLx?

## Deadpool e bb8 como sinal de carreira

Em entrevistas de [backend Rust](/carreira/entrevista-rust-backend/), dizer “usei PostgreSQL” é menos forte que explicar:

- como calculou o pool por réplica;
- por que definiu timeout de aquisição;
- como evitou segurar conexão durante I/O externo;
- quais métricas detectam saturação;
- como o serviço se comporta em failover;
- por que escolheu o pool nativo ou um adapter.

Um projeto de portfólio pode combinar Axum, PostgreSQL, migrations, métricas e teste de esgotamento. Isso demonstra engenharia de produção e conversa diretamente com [vagas Rust](/vagas/), [empresas que usam Rust](/empresas/) e a trilha de [carreira em backend web](/carreira/nicho-web-backend/).

## Perguntas frequentes

### Deadpool ou bb8: qual pool escolher em Rust?

Deadpool é uma escolha forte para adapters prontos e múltiplos tipos de recurso. bb8 é atraente quando a stack já oferece um `ManageConnection` ou quando você prefere seu contrato genérico. A compatibilidade com o driver deve decidir antes da preferência estética.

### Preciso de Deadpool ou bb8 se já uso SQLx?

Normalmente não. Comece com `sqlx::Pool`. Use Deadpool ou bb8 para outro recurso ou integração que realmente precise deles, não como camada redundante.

### Deadpool serve apenas para banco de dados?

Não. Ele gerencia recursos assíncronos reutilizáveis; PostgreSQL e Redis são adapters populares, mas managers customizados também são possíveis.

### Como evitar que o pool derrube a API sob carga?

Combine limite correto, timeout de aquisição, backpressure, queries rápidas e métricas de espera. Não tente resolver saturação apenas aumentando conexões.

### Qual é a diferença entre pool e semáforo?

Pool mantém recursos reutilizáveis. Semáforo limita concorrência. Os dois podem trabalhar juntos para proteger banco, memória e latência.

### Como testar um pool de conexões em Rust?

Force esgotamento, timeout, conexão inválida, shutdown e concorrência entre réplicas. Teste o caminho de falha, não apenas uma query bem-sucedida.

## Conclusão

A escolha entre **Deadpool e bb8** é menos dramática do que a operação correta do pool. Para SQLx, fique no pool nativo. Para `tokio-postgres`, compare os adapters e escolha a API que o time consegue configurar, observar e testar. Para Redis e recursos variados, Deadpool costuma oferecer um caminho prático; para managers pequenos e integrações existentes, bb8 continua uma opção sólida.

O resultado maduro tem quatro propriedades: limite global pensado por réplica, timeout de aquisição, conexões mantidas pelo menor tempo possível e métricas que separam espera de execução. Continue pelo guia de [SQLx em Rust](/ecossistema/sqlx/), [PostgreSQL com Rust](/tutoriais/rust-postgresql/), [métricas Prometheus](/blog/prometheus-metrics-rust-axum-producao-2026/) e [resiliência com Tower/Axum](/blog/rust-servicos-resilientes-tower-axum-2026/). Um pool bem escolhido ajuda; um pool bem operado mantém o serviço vivo.
