Class: DailyFocusOrphanReaperWorker

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

Overview

Recovers Daily Focus briefings orphaned in processing.

When a DailyFocusAnalysisRepWorker is killed mid-run (deploy restart, OOM,
hard kill), the briefing is stranded: Sidekiq::Shutdown is an Interrupt,
not a StandardError, so the worker's rescue never advances the status to
failed, and the ensure releases the column lock — leaving
daily_focus_status='processing' with no worker on it. AssistantLockCleanupWorker
clears the stale lock but never touches the status, and the next scheduled run
skips the rep because a briefing already exists. The rep sits in "Processing"
forever (2026-06-16 10:05 UTC deploy stranded 3 reps).

This sweep re-enqueues DailyFocusAnalysisRepWorker for each orphan, preserving
its covered reps. The rep worker reclaims the orphan and regenerates in place
(under a per-rep advisory lock, so this can't create duplicates even if it
races a Sidekiq retry).

Runs every 10 minutes on weekday business hours via sidekiq-scheduler.

Instance Method Summary collapse

Instance Method Details

#performObject



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'app/workers/daily_focus_orphan_reaper_worker.rb', line 25

def perform
  cutoff = DailyFocus::ORPHAN_AFTER.ago
  recovered = 0

  AssistantConversation
    .daily_focus_processing(Date.current)
    .where(updated_at: ...cutoff)
    .find_each do |conversation|
      # Belt-and-suspenders: skip anything a live worker is still on (the
      # updated_at filter already excludes recent activity, but a fresh column
      # lock means hands off regardless).
      next if DailyFocus.briefing_live?(conversation)

      target_id = conversation.daily_focus_target_employee_id
      next unless target_id

      DailyFocusAnalysisRepWorker.perform_async(
        target_id, nil, Array(conversation.daily_focus_covered_employee_ids)
      )
      recovered += 1
    end

  Rails.logger.info("[DailyFocusOrphanReaperWorker] Re-enqueued #{recovered} orphaned daily-focus briefing(s)") if recovered.positive?
end