Class: SeoBatchPollWorker

Inherits:
Object
  • Object
show all
Includes:
Sidekiq::Job
Defined in:
app/workers/seo_batch_poll_worker.rb

Overview

Phase 2b: Poll the Batch API for completion.

Routes to the correct provider (Gemini or Anthropic) based on the
SeoBatchJob's model. Re-enqueues itself with exponential backoff
until the batch reaches a terminal state, then enqueues
SeoBatchResultsWorker to process results.

Polling schedule (exponential backoff):
2 min -> 4 min -> 8 min -> 15 min (capped)

Max polling duration: 24 hours (provider batch expiry limit).

Usage:
SeoBatchPollWorker.perform_async(batch_job_id)
SeoBatchPollWorker.perform_async(batch_job_id, { 'attempt' => 3 })

Constant Summary collapse

MAX_POLL_ATTEMPTS =

~24 hours at 15-min intervals

120
MIN_BACKOFF_SECONDS =

2 minutes

120
MAX_BACKOFF_SECONDS =

15 minutes

900

Instance Method Summary collapse

Instance Method Details

#perform(batch_job_id, options = {}) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'app/workers/seo_batch_poll_worker.rb', line 28

def perform(batch_job_id, options = {})
  attempt = (options['attempt'] || options[:attempt] || 1).to_i

  batch_job = SeoBatchJob.find(batch_job_id)

  unless batch_job.processing?
    log_info "Batch job #{batch_job_id} is #{batch_job.status} — stopping poll"
    return
  end

  if attempt > MAX_POLL_ATTEMPTS
    batch_job.mark_failed!("Polling timed out after #{MAX_POLL_ATTEMPTS} attempts")
    log_error "Polling timed out for batch job #{batch_job_id}"
    return
  end

  if batch_job.gemini?
    poll_gemini(batch_job, attempt)
  else
    poll_anthropic(batch_job, attempt)
  end
rescue Seo::GeminiBatchClient::BatchError, Seo::AnthropicBatchClient::BatchError => e
  log_error "Poll failed: #{e.message}"
  if attempt < MAX_POLL_ATTEMPTS
    self.class.perform_in(MAX_BACKOFF_SECONDS, batch_job_id, { 'attempt' => attempt + 1 })
  else
    batch_job.mark_failed!("Poll error after #{attempt} attempts: #{e.message}")
  end
end