Skip to content

Commit b6825d0

Browse files
rosaclaude
andcommitted
Clean up dead thread from process_instances when replacing it
When a thread dies, `replace_thread` deleted its entry from `configured_processes` but not from `process_instances`. On the next supervise loop, `check_and_replace_terminated_processes` would find the same dead thread again, call `replace_thread`, and `configured_processes.delete(thread_id)` would return nil — crashing with `undefined method 'instantiate' for nil`. Mirror the pattern already used in `ForkSupervisor#replace_fork`: delete from `process_instances` first and guard against the entry already being gone. Fixes #710 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5518a4d commit b6825d0

1 file changed

Lines changed: 8 additions & 6 deletions

File tree

lib/solid_queue/async_supervisor.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,19 @@ def supervise
1919

2020
def check_and_replace_terminated_processes
2121
terminated_threads = process_instances.select { |thread_id, instance| !instance.alive? }
22-
terminated_threads.each { |thread_id, instance| replace_thread(thread_id, instance) }
22+
terminated_threads.each { |thread_id, _| replace_thread(thread_id) }
2323
end
2424

25-
def replace_thread(thread_id, instance)
25+
def replace_thread(thread_id)
2626
SolidQueue.instrument(:replace_thread, supervisor_pid: ::Process.pid) do |payload|
27-
payload[:thread] = instance
27+
if (instance = process_instances.delete(thread_id))
28+
payload[:thread] = instance
2829

29-
error = Processes::ThreadTerminatedError.new(instance.name)
30-
release_claimed_jobs_by(instance, with_error: error)
30+
error = Processes::ThreadTerminatedError.new(instance.name)
31+
release_claimed_jobs_by(instance, with_error: error)
3132

32-
start_process(configured_processes.delete(thread_id))
33+
start_process(configured_processes.delete(thread_id))
34+
end
3335
end
3436
end
3537

0 commit comments

Comments
 (0)