# Como Criar um Job Scheduler em Ruby para Processar Consultas de CPF Automaticamente

> Aprenda a criar um job scheduler em Ruby para processar consultas de CPF automaticamente, com agendamento, retry e monitoramento.

**Publicado:** 20/08/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/job-scheduler-ruby-processar-consultas-cpf-automaticamente

---


Um job scheduler em Ruby permite agendar tarefas recorrentes de consulta e manutenção de CPF — como revalidar cadastros antigos, processar lotes pendentes e limpar caches expirados — de forma confiável e sem intervenção manual, usando Sidekiq-Cron para aplicações Rails ou Rufus-Scheduler para projetos independentes.

## Introdução

Muitas operações com CPF precisam ser executadas de forma periódica e automática: revalidar cadastros antigos, processar lotes de novos clientes, limpar caches expirados ou gerar relatórios de conformidade. Um job scheduler em Ruby permite agendar essas tarefas para execução automática, garantindo que o processamento ocorra de forma confiável e sem intervenção manual.

## Arquitetura do scheduler

O scheduler coordena a execução de jobs periódicos sem bloquear a aplicação principal.

| Componente | Responsabilidade | Ferramenta |
|---|---|---|
| Scheduler | Define horários e frequência dos jobs | Sidekiq-Cron / Rufus-Scheduler |
| Worker | Executa a lógica de cada job | Sidekiq Worker |
| Queue | Gerencia jobs pendentes e em execução | Redis |
| Monitor | Acompanha saúde dos jobs | Sidekiq Web UI |
| Storage | Persiste resultados | PostgreSQL |

**Sidekiq-Cron** -- ideal para aplicações Rails que já usam Sidekiq.

**Rufus-Scheduler** -- scheduler leve e independente de framework.

**Whenever** -- gera crontabs para agendamento no nível do sistema operacional.

---

## Implementação com Sidekiq-Cron

Para aplicações Rails com Sidekiq, o sidekiq-cron oferece agendamento integrado.

```ruby
# Gemfile
gem "sidekiq", "~> 7.0"
gem "sidekiq-cron", "~> 1.12"
gem "faraday", "~> 2.0"

# config/initializers/sidekiq_cron.rb
schedule = [
 {
 "name" => "Revalidar CPFs antigos - diario",
 "cron" => "0 2 * * *", # Todo dia às 2h
 "class" => "RevalidarCpfsAntigosWorker",
 "queue" => "agendado"
 },
 {
 "name" => "Processar fila de CPFs pendentes - a cada 5min",
 "cron" => "*/5 * * * *",
 "class" => "ProcessarCpfsPendentesWorker",
 "queue" => "agendado"
 },
 {
 "name" => "Limpar consultas expiradas - semanal",
 "cron" => "0 3 * * 0", # Domingo às 3h
 "class" => "LimparConsultasExpiradasWorker",
 "queue" => "manutencao"
 },
 {
 "name" => "Relatorio de consultas - mensal",
 "cron" => "0 6 1 * *", # Dia 1 às 6h
 "class" => "RelatorioConsultasWorker",
 "queue" => "relatorios"
 }
]

Sidekiq::Cron::Job.load_from_array!(schedule)
```

---

## Workers para cada tipo de job

Cada worker implementa a lógica específica do seu tipo de processamento.

```ruby
# app/workers/revalidar_cpfs_antigos_worker.rb
class RevalidarCpfsAntigosWorker
 include Sidekiq::Worker
 sidekiq_options queue: :agendado, retry: 3

 LIMITE_DIARIO = 500
 DIAS_PARA_REVALIDAR = 30

 def perform
 cpfs_antigos = ConsultaCpf
 .where(status: "sucesso")
 .where("consultado_em < ?", DIAS_PARA_REVALIDAR.days.ago)
 .order(consultado_em: :asc)
 .limit(LIMITE_DIARIO)

 logger.info(
 "[Revalidar] Iniciando revalidacao de #{cpfs_antigos.count} CPFs"
 )

 cliente = build_api_client
 resultados = { sucesso: 0, falha: 0, erro: 0 }

 cpfs_antigos.find_each do |consulta|
 resultado = revalidar_cpf(cliente, consulta)
 resultados[resultado] += 1
 sleep(0.2) # Respeitar rate limit
 end

 logger.info("[Revalidar] Concluido: #{resultados}")
 end

 private

 def build_api_client
 Faraday.new(url: "https://api.cpfhub.io") do |conn|
 conn.headers["x-api-key"] = ENV["CPFHUB_API_KEY"]
 conn.options.timeout = 10
 conn.adapter Faraday.default_adapter
 end
 end

 def revalidar_cpf(cliente, consulta)
 cpf = consulta.cpf_cifrado
 resposta = cliente.get("/cpf/#{cpf}")
 dados = JSON.parse(resposta.body)

 if dados["success"]
 consulta.update!(
 nome: dados["data"]["name"],
 genero: dados["data"]["gender"],
 data_nascimento: dados["data"]["birthDate"],
 consultado_em: Time.current,
 expira_em: 24.hours.from_now,
 status: "sucesso"
 )
 :sucesso
 else
 consulta.update!(status: "falha", motivo_falha: "Nao encontrado na revalidacao")
 :falha
 end
 rescue StandardError => e
 logger.error("[Revalidar] Erro no CPF #{consulta.id}: #{e.message}")
 :erro
 end
end

# app/workers/processar_cpfs_pendentes_worker.rb
class ProcessarCpfsPendentesWorker
 include Sidekiq::Worker
 sidekiq_options queue: :agendado, retry: 2

 def perform
 pendentes = ConsultaCpf.where(status: "pendente").limit(100)
 return if pendentes.empty?

 logger.info("[Pendentes] Processando #{pendentes.count} CPFs pendentes")

 cliente = Faraday.new(url: "https://api.cpfhub.io") do |conn|
 conn.headers["x-api-key"] = ENV["CPFHUB_API_KEY"]
 conn.options.timeout = 10
 conn.adapter Faraday.default_adapter
 end

 pendentes.find_each do |consulta|
 resposta = cliente.get("/cpf/#{consulta.cpf_cifrado}")
 dados = JSON.parse(resposta.body)

 if dados["success"]
 consulta.update!(
 nome: dados["data"]["name"],
 genero: dados["data"]["gender"],
 data_nascimento: dados["data"]["birthDate"],
 status: "sucesso",
 consultado_em: Time.current,
 expira_em: 24.hours.from_now
 )
 else
 consulta.update!(status: "falha", motivo_falha: "Nao encontrado")
 end

 sleep(0.1)
 rescue StandardError => e
 consulta.update!(status: "falha", motivo_falha: e.message)
 end
 end
end
```

---

## Scheduler independente com Rufus

Para aplicações fora do Rails ou que precisam de um scheduler independente.

```ruby
require "rufus-scheduler"
require "faraday"
require "json"

scheduler = Rufus::Scheduler.new

# Job a cada 5 minutos: processar CPFs pendentes
scheduler.every "5m", first: :now do
 puts "[#{Time.now}] Verificando CPFs pendentes..."
 processar_pendentes
end

# Job diário às 2h: revalidar CPFs antigos
scheduler.cron "0 2 * * *" do
 puts "[#{Time.now}] Iniciando revalidacao diaria..."
 revalidar_antigos
end

# Job semanal: gerar relatório
scheduler.cron "0 6 * * 1" do
 puts "[#{Time.now}] Gerando relatorio semanal..."
 gerar_relatorio
end

def processar_pendentes
 # Lógica de processamento
end

def revalidar_antigos
 # Lógica de revalidação
end

def gerar_relatorio
 # Lógica de relatório
end

scheduler.join
```

| Expressão Cron | Frequência | Uso Típico |
|---|---|---|
| `*/5 * * * *` | A cada 5 minutos | Processar CPFs pendentes |
| `0 2 * * *` | Diariamente às 2h | Revalidar CPFs antigos |
| `0 3 * * 0` | Domingos às 3h | Limpeza de registros expirados |
| `0 6 1 * *` | Dia 1 de cada mês às 6h | Relatórios mensais |
| `0 */4 * * *` | A cada 4 horas | Sincronização de dados |

---

## Monitoramento e alertas

Monitore a saúde dos jobs agendados para detectar falhas rapidamente. A [Lei Geral de Proteção de Dados (LGPD)](https://www.planalto.gov.br/ccivil_03/_ato2015-2018/2018/lei/l13709.htm) exige que sistemas que tratam dados pessoais mantenham logs e mecanismos de rastreabilidade — o relatório mensal gerado pelo worker é uma forma prática de demonstrar conformidade.

```ruby
# app/workers/relatorio_consultas_worker.rb
class RelatorioConsultasWorker
 include Sidekiq::Worker
 sidekiq_options queue: :relatorios, retry: 1

 def perform
 periodo = 1.month.ago..Time.current

 metricas = {
 total: ConsultaCpf.where(consultado_em: periodo).count,
 sucesso: ConsultaCpf.where(consultado_em: periodo, status: "sucesso").count,
 falha: ConsultaCpf.where(consultado_em: periodo, status: "falha").count,
 por_origem: ConsultaCpf.where(consultado_em: periodo)
 .group(:origem).count,
 por_genero: ConsultaCpf.where(consultado_em: periodo, status: "sucesso")
 .group(:genero).count
 }

 taxa_sucesso = metricas[:total].positive? ?
 (metricas[:sucesso].to_f / metricas[:total] * 100).round(2) : 0

 metricas[:taxa_sucesso] = "#{taxa_sucesso}%"

 logger.info("[Relatorio] Metricas do periodo: #{metricas}")

 # Alerta se taxa de sucesso cair abaixo do esperado
 if taxa_sucesso < 90
 NotificacaoService.alertar(
 "Taxa de sucesso de consulta CPF abaixo de 90%: #{taxa_sucesso}%"
 )
 end
 end
end
```

---

## Perguntas frequentes

### Qual é a diferença entre Sidekiq-Cron e Rufus-Scheduler para jobs de CPF?
Sidekiq-Cron é integrado ao Sidekiq e ao Redis, gerencia filas com persistência e suporta retry automático — ideal para aplicações Rails em produção com alto volume. Rufus-Scheduler é um scheduler em processo, leve e sem dependência de Redis, mais adequado para scripts standalone ou aplicações de baixo volume. Para jobs críticos de CPF em produção, Sidekiq-Cron oferece maior resiliência.

### Como evitar que o job de revalidação consuma todas as consultas do plano?
Defina um `LIMITE_DIARIO` no worker (o exemplo usa 500 CPFs/dia) e calcule o consumo mensal esperado. Com o plano Pro (1.000 consultas/mês), o limite diário de 33 CPFs já é suficiente para manter cadastros revalidados a cada 30 dias. Se o volume exceder o plano, a API não bloqueia: cobra R$0,15 por consulta adicional, o que é previsível e controlável.

### Como implementar retry seguro sem reprocessar o mesmo CPF duas vezes?
Use o campo `status` como lock: antes de processar, mude para `"processando"`. Ao concluir com sucesso, mude para `"sucesso"`. Em caso de erro, reverta para `"pendente"` ou `"falha"`. Assim, mesmo que o job seja reiniciado por um retry do Sidekiq, CPFs já processados não são consultados novamente.

### Como monitorar se os jobs estão executando corretamente?
A Sidekiq Web UI exibe filas, jobs em execução e histórico de falhas em tempo real. Complementarmente, o `RelatorioConsultasWorker` gera métricas mensais e emite alerta se a taxa de sucesso cair abaixo de 90%. Para monitoramento externo, ferramentas como Healthchecks.io podem ser integradas ao final de cada job para confirmar execução bem-sucedida.

### Leia também

- [Diferença entre validação de CPF e consulta de CPF: quando usar cada uma](https://cpfhub.io/blog/diferenca-entre-validacao-de-cpf-e-consulta-de-cpf-quando-usar-cada-uma)
- [API de CPF grátis para desenvolvedores: como começar em 5 minutos](https://cpfhub.io/blog/api-cpf-gratis-desenvolvedores-comecar-5-minutos)
- [Onboarding digital em fintechs: como validar CPF em menos de 30 segundos](https://cpfhub.io/blog/onboarding-digital-em-fintechs-como-validar-cpf-em-menos-de-30-segundos)
- [KYC no Brasil: quais setores são obrigados a validar CPF por lei](https://cpfhub.io/blog/kyc-no-brasil-quais-setores-sao-obrigados-a-validar-cpf-por-lei)

---

## Conclusão

Um job scheduler em Ruby automatiza tarefas recorrentes de consulta e manutenção de CPF, garantindo que revalidações, processamento de pendências e limpezas ocorram de forma confiável e sem intervenção manual. Seja com Sidekiq-Cron para aplicações Rails ou Rufus-Scheduler para aplicações independentes, o agendamento de jobs é uma peça fundamental da infraestrutura de qualquer sistema que consome APIs de CPF em escala.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e comece a automatizar suas consultas de CPF com a infraestrutura Ruby que você já usa.

