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.
# 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.
# 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.
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) 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.
# 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.
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 — 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.
CPFHub.io
Pronto para integrar a API?
50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.
Sobre a redação
Redação CPFHub.io
Time editorial especializado em APIs de CPF, identidade digital e compliance no mercado brasileiro. Produzimos guias técnicos, análises regulatórias e tutoriais sobre LGPD e KYC para desenvolvedores e líderes de produto.



