solid_queue 1.3.1 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 740ecc6a7761906fecdf41fd7cd12bc4d94f2dff846857148d5f7b189bd0c095
4
- data.tar.gz: b317f64446891ae6aa889492fee0331e96ca962aeeb61a677119800f3f0c8faa
3
+ metadata.gz: fb6ce5453b198c213a9de3fd6214ad410e158f1941a024fff5256f5895532523
4
+ data.tar.gz: 8d9172270e0cbabbfde5c5084c4da52d615a787921acb1c837bccd494556d977
5
5
  SHA512:
6
- metadata.gz: '068a183750afec605e3ce5064cffc067b7ae75c8bd4f14d8052f993393a99595405cdf6f5bbef28236c80518d3941d49ab92c3ef5060093b23febda6c6076917'
7
- data.tar.gz: ddb517d7b26b779f6ee33b0316c671f494b2785404f2ec305c707e8bbe927496837d25ea787fd5f8c314d7a62b731a8a9df8cef37ecea9321d9602260eb572e1
6
+ metadata.gz: 3c9155032132f14e03132e651846fa4b2bec944f96eb62d988dc75a82abfcb5f4e3a1fde9f4bb4591866d8cb230bd188df4cb9f028b6adcdf09f161d9a48f2e0
7
+ data.tar.gz: 0aecdd6b91de9f6026b9ea041a673709e348ddaac093ad6ebb57b5bdd98cd6b4f255e8e9350d790a8635703d0249407871f2c6258594593871c56b690fc8f3e7
@@ -6,7 +6,7 @@ module SolidQueue
6
6
 
7
7
  serialize :error, coder: JSON
8
8
 
9
- before_create :expand_error_details_from_exception
9
+ before_save :expand_error_details_from_exception, if: :exception
10
10
 
11
11
  attr_accessor :exception
12
12
 
@@ -26,7 +26,7 @@ module SolidQueue
26
26
  end
27
27
 
28
28
  def concurrency_limited?
29
- concurrency_key.present?
29
+ concurrency_key.present? && job_class.present?
30
30
  end
31
31
 
32
32
  def blocked?
@@ -16,7 +16,16 @@ module SolidQueue
16
16
  end
17
17
 
18
18
  def failed_with(exception)
19
- FailedExecution.create_or_find_by!(job_id: id, exception: exception)
19
+ FailedExecution.transaction(requires_new: true) do
20
+ FailedExecution.create!(job_id: id, exception: exception)
21
+ end
22
+ rescue ActiveRecord::RecordNotUnique
23
+ if (failed_execution = FailedExecution.find_by(job_id: id))
24
+ failed_execution.exception = exception
25
+ failed_execution.save!
26
+ else
27
+ retry
28
+ end
20
29
  end
21
30
 
22
31
  def reset_execution_counters
@@ -6,9 +6,9 @@ module SolidQueue
6
6
  class RecurringTask < Record
7
7
  serialize :arguments, coder: Arguments, default: []
8
8
 
9
- validate :supported_schedule
9
+ validate :ensure_schedule_supported
10
10
  validate :ensure_command_or_class_present
11
- validate :existing_job_class
11
+ validate :ensure_existing_job_class
12
12
 
13
13
  scope :static, -> { where(static: true) }
14
14
 
@@ -102,10 +102,19 @@ module SolidQueue
102
102
  end
103
103
 
104
104
  private
105
- def supported_schedule
105
+ def ensure_schedule_supported
106
106
  unless parsed_schedule.instance_of?(Fugit::Cron)
107
107
  errors.add :schedule, :unsupported, message: "is not a supported recurring schedule"
108
108
  end
109
+ rescue ArgumentError => error
110
+ message = if error.message.include?("multiple crons")
111
+ "generates multiple cron schedules. Please use separate recurring tasks for each schedule, " +
112
+ "or use explicit cron syntax (e.g., '40 0,15 * * *' for multiple times with the same minutes)"
113
+ else
114
+ error.message
115
+ end
116
+
117
+ errors.add :schedule, :unsupported, message: message
109
118
  end
110
119
 
111
120
  def ensure_command_or_class_present
@@ -114,7 +123,7 @@ module SolidQueue
114
123
  end
115
124
  end
116
125
 
117
- def existing_job_class
126
+ def ensure_existing_job_class
118
127
  if class_name.present? && job_class.nil?
119
128
  errors.add :class_name, :undefined, message: "doesn't correspond to an existing class"
120
129
  end
@@ -152,7 +161,7 @@ module SolidQueue
152
161
 
153
162
 
154
163
  def parsed_schedule
155
- @parsed_schedule ||= Fugit.parse(schedule)
164
+ @parsed_schedule ||= Fugit.parse(schedule, multi: :fail)
156
165
  end
157
166
 
158
167
  def job_class
@@ -40,7 +40,7 @@ module SolidQueue
40
40
  end
41
41
 
42
42
  def wait
43
- if semaphore = Semaphore.find_by(key: key)
43
+ if semaphore = Semaphore.lock.find_by(key: key)
44
44
  semaphore.value > 0 && attempt_decrement
45
45
  else
46
46
  attempt_creation
@@ -82,9 +82,11 @@ Puma::Plugin.create do
82
82
  end
83
83
 
84
84
  def stop_solid_queue_fork
85
+ return unless solid_queue_pid
86
+
85
87
  Process.waitpid(solid_queue_pid, Process::WNOHANG)
86
88
  log "Stopping Solid Queue..."
87
- Process.kill(:INT, solid_queue_pid) if solid_queue_pid
89
+ Process.kill(:INT, solid_queue_pid)
88
90
  Process.wait(solid_queue_pid)
89
91
  rescue Errno::ECHILD, Errno::ESRCH
90
92
  end
@@ -19,17 +19,19 @@ module SolidQueue
19
19
 
20
20
  def check_and_replace_terminated_processes
21
21
  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) }
23
23
  end
24
24
 
25
- def replace_thread(thread_id, instance)
25
+ def replace_thread(thread_id)
26
26
  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
28
29
 
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)
31
32
 
32
- start_process(configured_processes.delete(thread_id))
33
+ start_process(configured_processes.delete(thread_id))
34
+ end
33
35
  end
34
36
  end
35
37
 
@@ -8,7 +8,7 @@ module SolidQueue
8
8
  desc: "Path to config file (default: #{Configuration::DEFAULT_CONFIG_FILE_PATH}).",
9
9
  banner: "SOLID_QUEUE_CONFIG"
10
10
 
11
- class_option :mode, type: :string, default: "fork", enum: %w[ fork async ],
11
+ class_option :mode, type: :string, enum: %w[ fork async ],
12
12
  desc: "Whether to fork processes for workers and dispatchers (fork) or to run these in the same process as the supervisor (async) (default: fork).",
13
13
  banner: "SOLID_QUEUE_SUPERVISOR_MODE"
14
14
 
@@ -54,7 +54,7 @@ module SolidQueue::Processes
54
54
  end
55
55
 
56
56
  def heartbeat
57
- process.heartbeat
57
+ process&.heartbeat
58
58
  rescue ActiveRecord::RecordNotFound
59
59
  self.process = nil
60
60
  wake_up
@@ -1,3 +1,3 @@
1
1
  module SolidQueue
2
- VERSION = "1.3.1"
2
+ VERSION = "1.3.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid_queue
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rosa Gutierrez
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-01-15 00:00:00.000000000 Z
11
+ date: 2026-02-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord