# Como usar Oban para processar consultas de CPF em background no Elixir

> Use Oban para processar consultas de CPF em background no Elixir com filas, retry automático e agendamento.

**Publicado:** 27/11/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-usar-oban-processar-consultas-cpf-background-elixir

---


Usar o Oban para processar consultas de CPF em background no Elixir garante que nenhuma consulta se perca em caso de falha, mesmo com alto volume de requisições. Com filas persistentes no PostgreSQL, retry automático e agendamento nativo, o Oban torna o processamento assíncrono da API CPFHub.io confiável e observável. A documentação oficial do Oban está disponível em [hexdocs.pm/oban](https://hexdocs.pm/oban/Oban.html).

## Introdução

Processar consultas de CPF em background é essencial quando o volume de requisições é alto ou quando a resposta da API não precisa ser síncrona. O Oban é a solução mais robusta para processamento em background no Elixir, oferecendo filas persistentes com PostgreSQL, retry automático, agendamento e monitoramento.

---

## Configurando o Oban

Adicione o Oban ao projeto e configure as filas:

```elixir
# mix.exs
defp deps do
 [
 {:oban, "~> 2.17"},
 {:httpoison, "~> 2.0"},
 {:jason, "~> 1.4"}
 ]
end

# config/config.exs
config :minha_app, Oban,
 repo: MinhaApp.Repo,
 queues: [cpf_consultas: 5, cpf_lote: 2],
 plugins: [
 Oban.Plugins.Pruner,
 {Oban.Plugins.Cron, crontab: []}
 ]
```

Execute a migration do Oban para criar as tabelas necessárias:

```elixir
# Terminal
# mix ecto.gen.migration add_oban_jobs_table
# Na migration gerada:
defmodule MinhaApp.Repo.Migrations.AddObanJobsTable do
 use Ecto.Migration

 def up, do: Oban.Migration.up(version: 12)
 def down, do: Oban.Migration.down(version: 1)
end
```

| Fila | Concorrência | Uso |
|---|---|---|
| `cpf_consultas` | 5 workers | Consultas individuais |
| `cpf_lote` | 2 workers | Processamento em lote |

---

## Criando o worker de consulta

O worker do Oban define como cada job de consulta de CPF será processado. Note que a API CPFHub.io nunca retorna HTTP 429 — ao atingir o limite do plano, ela cobra R$0,15 por consulta adicional sem bloquear requisições:

```elixir
defmodule MinhaApp.Workers.CpfConsultaWorker do
 use Oban.Worker,
 queue: :cpf_consultas,
 max_attempts: 3,
 priority: 1

 @base_url "https://api.cpfhub.io"

 @impl Oban.Worker
 def perform(%Oban.Job{args: %{"cpf" => cpf, "callback_url" => callback_url} = args}) do
 api_key = Application.get_env(:minha_app, :cpfhub_api_key)
 headers = [{"x-api-key", api_key}]

 case HTTPoison.get("#{@base_url}/cpf/#{cpf}", headers, recv_timeout: 10_000) do
 {:ok, %{status_code: 200, body: body}} ->
 resultado = Jason.decode!(body)
 processar_resultado(cpf, resultado, callback_url)
 :ok

 {:ok, %{status_code: status}} ->
 {:error, "API retornou status #{status}"}

 {:error, %{reason: reason}} ->
 {:error, "Falha na requisição: #{inspect(reason)}"}
 end
 end

 defp processar_resultado(cpf, %{"success" => true, "data" => data}, callback_url) do
 resultado = %{
 cpf: cpf,
 nome: data["name"],
 data_nascimento: data["birthDate"],
 genero: data["gender"],
 status: "encontrado",
 processado_em: DateTime.utc_now()
 }

 if callback_url do
 HTTPoison.post(callback_url, Jason.encode!(resultado),
 [{"Content-Type", "application/json"}])
 end

 MinhaApp.Cpf.salvar_resultado(resultado)
 end

 defp processar_resultado(cpf, _, _callback_url) do
 MinhaApp.Cpf.salvar_resultado(%{cpf: cpf, status: "nao_encontrado"})
 end
end
```

---

## Worker de processamento em lote

Para processar múltiplos CPFs, crie um worker que divide o trabalho em jobs individuais:

```elixir
defmodule MinhaApp.Workers.CpfLoteWorker do
 use Oban.Worker,
 queue: :cpf_lote,
 max_attempts: 1,
 unique: [period: 300]

 alias MinhaApp.Workers.CpfConsultaWorker

 @impl Oban.Worker
 def perform(%Oban.Job{args: %{"cpfs" => cpfs, "lote_id" => lote_id}}) do
 jobs = Enum.map(cpfs, fn cpf ->
 CpfConsultaWorker.new(%{
 cpf: cpf,
 lote_id: lote_id,
 callback_url: nil
 })
 end)

 Oban.insert_all(jobs)
 :ok
 end
end
```

---

## Enfileirando jobs

Crie funções de conveniência para enfileirar consultas:

```elixir
defmodule MinhaApp.Cpf.Enqueuer do
 alias MinhaApp.Workers.{CpfConsultaWorker, CpfLoteWorker}

 def consultar_async(cpf, opts \\ []) do
 callback_url = Keyword.get(opts, :callback_url)
 prioridade = Keyword.get(opts, :prioridade, 1)
 agendar_em = Keyword.get(opts, :agendar_em)

 args = %{cpf: cpf, callback_url: callback_url}

 worker_opts = [priority: prioridade]
 worker_opts = if agendar_em, do: [{:scheduled_at, agendar_em} | worker_opts], else: worker_opts

 args
 |> CpfConsultaWorker.new(worker_opts)
 |> Oban.insert()
 end

 def processar_lote(cpfs) do
 lote_id = Ecto.UUID.generate()

 %{cpfs: cpfs, lote_id: lote_id}
 |> CpfLoteWorker.new()
 |> Oban.insert()

 {:ok, lote_id}
 end

 def consultar_agendado(cpf, data_hora) do
 consultar_async(cpf, agendar_em: data_hora)
 end
end

# Uso
MinhaApp.Cpf.Enqueuer.consultar_async("12345678900",
 callback_url: "https://meuapp.com/webhook/cpf"
)

MinhaApp.Cpf.Enqueuer.processar_lote([
 "12345678900", "98765432100", "11122233344"
])

# Agendar para daqui a 1 hora
MinhaApp.Cpf.Enqueuer.consultar_agendado(
 "12345678900",
 DateTime.add(DateTime.utc_now(), 3600, :second)
)
```

---

## Monitorando jobs

Acompanhe o status dos jobs de consulta de CPF:

```elixir
defmodule MinhaApp.Cpf.Monitor do
 import Ecto.Query
 alias MinhaApp.Repo

 def estatisticas do
 query = from j in Oban.Job,
 where: j.worker == "MinhaApp.Workers.CpfConsultaWorker",
 group_by: j.state,
 select: {j.state, count(j.id)}

 stats = Repo.all(query) |> Map.new()

 %{
 pendentes: Map.get(stats, "available", 0),
 executando: Map.get(stats, "executing", 0),
 concluidos: Map.get(stats, "completed", 0),
 com_erro: Map.get(stats, "retryable", 0) + Map.get(stats, "discarded", 0),
 agendados: Map.get(stats, "scheduled", 0)
 }
 end

 def jobs_com_erro do
 query = from j in Oban.Job,
 where: j.worker == "MinhaApp.Workers.CpfConsultaWorker",
 where: j.state in ["retryable", "discarded"],
 order_by: [desc: j.inserted_at],
 limit: 20

 Repo.all(query)
 end
end
```

---

## Perguntas frequentes

### O que é necessário para implementar consultas de CPF assíncronas com Oban?
Você precisa do Oban configurado com um repo PostgreSQL, de um worker que chama `GET https://api.cpfhub.io/cpf/{CPF}` com o header `x-api-key`, e de uma função de enfileiramento. O Oban cuida do retry automático em caso de falha de rede, garantindo que nenhuma consulta se perca.

### A API CPFHub.io funciona para todos os volumes de consulta?
Sim. O plano gratuito oferece 50 consultas por mês sem cartão de crédito. Para volumes maiores, o plano Pro inclui 1.000 consultas mensais por R$149. Se o limite for ultrapassado, a API não bloqueia: cobra R$0,15 por consulta adicional — portanto, não há necessidade de lógica de back-off por rate limit.

### Como definir o número ideal de workers por fila no Oban?
O número de workers (`concurrency`) deve ser calibrado com base na latência da API (~900ms) e no throughput desejado. Para 5 workers na fila `cpf_consultas`, você consegue processar aproximadamente 300 consultas por minuto. Monitore a fila com `MinhaApp.Cpf.Monitor.estatisticas/0` e ajuste conforme o volume real.

### Como garantir conformidade com a LGPD no processamento em background?
Garanta que os dados de CPF nos args do job sejam mascarados nos logs do Oban, defina um `max_attempts` razoável para evitar reprocessamentos desnecessários, e implemente um período de retenção para jobs concluídos usando `Oban.Plugins.Pruner`. Documente a finalidade de cada fila para atender ao artigo 37 da LGPD.

### Leia também

- [SLA de API de CPF: níveis de disponibilidade e o que exigir do seu provedor](https://cpfhub.io/blog/sla-api-cpf-niveis-disponibilidade)
- [Como validar CPF no frontend com React e API REST](https://cpfhub.io/blog/como-validar-cpf-no-frontend-com-react-e-api-rest)
- [Como fazer consulta de CPF em lote (batch) via API de forma eficiente](https://cpfhub.io/blog/como-fazer-consulta-de-cpf-em-lote-batch-via-api)
- [API de CPF grátis para desenvolvedores: como começar em 5 minutos](https://cpfhub.io/blog/api-cpf-gratis-desenvolvedores-comecar-5-minutos)

---

## Conclusão

O Oban transforma o processamento de consultas de CPF em uma operação confiável e observável. Com filas persistentes, retry automático, agendamento e monitoramento, você garante que nenhuma consulta se perca, mesmo em caso de falhas de rede ou reinicializações da aplicação.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e comece a integrar consultas de CPF em background na sua aplicação Elixir com Oban hoje mesmo.

