solid_queue 1.3.2 → 1.4.0
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 +4 -4
- data/README.md +54 -3
- data/app/models/solid_queue/blocked_execution.rb +3 -1
- data/app/models/solid_queue/ready_execution.rb +2 -1
- data/app/models/solid_queue/record.rb +7 -0
- data/app/models/solid_queue/recurring_task.rb +10 -1
- data/lib/solid_queue/configuration.rb +25 -7
- data/lib/solid_queue/processes/registrable.rb +4 -0
- data/lib/solid_queue/scheduler/recurring_schedule.rb +61 -11
- data/lib/solid_queue/scheduler.rb +23 -4
- data/lib/solid_queue/version.rb +1 -1
- data/lib/solid_queue.rb +8 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fd0590f46160c60f3a496158cf8dc2412803025cd06d94ac423c32f0b688dd77
|
|
4
|
+
data.tar.gz: 8c892f457280b1974908d2de0c3e5be5229ff6bcb448ed61c2937ff4566da796
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0a86b46d35b0cc8aef4a235e401b4470a6f6e64ff8c44ebdefc06f597d6cbe67ea76c786fc1e85caee1c3fc4dd492180530348078890ef74af90461705150a60
|
|
7
|
+
data.tar.gz: efeadbeb7dc0d044c801915d6ba4d8c249b249ef0dde8726454a359d3f1a4ce9ad4e2edb6cd19f8c371967059081c0ef2904ea630a0ae620123939ce881f0fdb
|
data/README.md
CHANGED
|
@@ -17,6 +17,7 @@ Solid Queue can be used with SQL databases such as MySQL, PostgreSQL, or SQLite,
|
|
|
17
17
|
- [Workers, dispatchers, and scheduler](#workers-dispatchers-and-scheduler)
|
|
18
18
|
- [Fork vs. async mode](#fork-vs-async-mode)
|
|
19
19
|
- [Configuration](#configuration)
|
|
20
|
+
- [Optional scheduler configuration](#optional-scheduler-configuration)
|
|
20
21
|
- [Queue order and priorities](#queue-order-and-priorities)
|
|
21
22
|
- [Queues specification and performance](#queues-specification-and-performance)
|
|
22
23
|
- [Threads, processes, and signals](#threads-processes-and-signals)
|
|
@@ -31,6 +32,7 @@ Solid Queue can be used with SQL databases such as MySQL, PostgreSQL, or SQLite,
|
|
|
31
32
|
- [Puma plugin](#puma-plugin)
|
|
32
33
|
- [Jobs and transactional integrity](#jobs-and-transactional-integrity)
|
|
33
34
|
- [Recurring tasks](#recurring-tasks)
|
|
35
|
+
- [Scheduling and unscheduling recurring tasks dynamically](#scheduling-and-unscheduling-recurring-tasks-dynamically)
|
|
34
36
|
- [Inspiration](#inspiration)
|
|
35
37
|
- [License](#license)
|
|
36
38
|
|
|
@@ -209,7 +211,7 @@ By default, Solid Queue will try to find your configuration under `config/queue.
|
|
|
209
211
|
bin/jobs -c config/calendar.yml
|
|
210
212
|
```
|
|
211
213
|
|
|
212
|
-
You can also skip
|
|
214
|
+
You can also skip the scheduler process by setting the environment variable `SOLID_QUEUE_SKIP_RECURRING=true`. This is useful for environments like staging, review apps, or development where you don't want any recurring jobs to run. This is equivalent to using the `--skip-recurring` option with `bin/jobs`.
|
|
213
215
|
|
|
214
216
|
This is what this configuration looks like:
|
|
215
217
|
|
|
@@ -227,6 +229,10 @@ production:
|
|
|
227
229
|
threads: 5
|
|
228
230
|
polling_interval: 0.1
|
|
229
231
|
processes: 3
|
|
232
|
+
scheduler:
|
|
233
|
+
dynamic_tasks_enabled: true
|
|
234
|
+
polling_interval: 5
|
|
235
|
+
|
|
230
236
|
```
|
|
231
237
|
|
|
232
238
|
Everything is optional. If no configuration at all is provided, Solid Queue will run with one dispatcher and one worker with default settings. If you want to run only dispatchers or workers, you just need to include that section alone in the configuration. For example, with the following configuration:
|
|
@@ -271,6 +277,19 @@ It is recommended to set this value less than or equal to the queue database's c
|
|
|
271
277
|
- `concurrency_maintenance`: whether the dispatcher will perform the concurrency maintenance work. This is `true` by default, and it's useful if you don't use any [concurrency controls](#concurrency-controls) and want to disable it or if you run multiple dispatchers and want some of them to just dispatch jobs without doing anything else.
|
|
272
278
|
|
|
273
279
|
|
|
280
|
+
### Optional scheduler configuration
|
|
281
|
+
|
|
282
|
+
Optionally, you can configure the scheduler process under the `scheduler` section in your `config/queue.yml` if you'd like to [schedule recurring tasks dynamically](#scheduling-and-unscheduling-recurring-tasks-dynamically).
|
|
283
|
+
|
|
284
|
+
```yaml
|
|
285
|
+
scheduler:
|
|
286
|
+
dynamic_tasks_enabled: true
|
|
287
|
+
polling_interval: 5
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
- `dynamic_tasks_enabled`: whether the scheduler should poll for [dynamically scheduled recurring tasks](#scheduling-and-unscheduling-recurring-tasks-dynamically). This is `false` by default. When enabled, the scheduler will poll the database at the given `polling_interval` to pick up tasks scheduled via `SolidQueue.schedule_recurring_task`.
|
|
291
|
+
- `polling_interval`: how frequently (in seconds) the scheduler checks for dynamic task changes. Defaults to `5`.
|
|
292
|
+
|
|
274
293
|
### Queue order and priorities
|
|
275
294
|
|
|
276
295
|
As mentioned above, if you specify a list of queues for a worker, these will be polled in the order given, such as for the list `real_time,background`, no jobs will be taken from `background` unless there aren't any more jobs waiting in `real_time`.
|
|
@@ -462,7 +481,7 @@ class MyJob < ApplicationJob
|
|
|
462
481
|
- `group` is used to control the concurrency of different job classes together. It defaults to the job class name.
|
|
463
482
|
- `on_conflict` controls behaviour when enqueuing a job that conflicts with the concurrency limits configured. It can be set to one of the following:
|
|
464
483
|
- (default) `:block`: the job is blocked and is dispatched when another job completes and unblocks it, or when the duration expires.
|
|
465
|
-
- `:discard`: the job is discarded. When you choose this option, bear in mind that if a job runs and fails to remove the concurrency lock (or _semaphore_, read below to know more about this), all jobs conflicting with it will be discarded
|
|
484
|
+
- `:discard`: the job is discarded. When you choose this option, bear in mind that if a job runs and fails to remove the concurrency lock (or _semaphore_, read below to know more about this), all jobs conflicting with it will be discarded until the interval defined by `duration` has elapsed.
|
|
466
485
|
|
|
467
486
|
When a job includes these controls, we'll ensure that, at most, the number of jobs (indicated as `to`) that yield the same `key` will be performed concurrently, and this guarantee will last for `duration` for each job enqueued. Note that there's no guarantee about _the order of execution_, only about jobs being performed at the same time (overlapping).
|
|
468
487
|
|
|
@@ -472,7 +491,7 @@ Since something can happen that prevents the first job from releasing the semaph
|
|
|
472
491
|
|
|
473
492
|
It's important to note that after one or more candidate jobs are unblocked (either because a job finishes or because `duration` expires and a semaphore is released), the `duration` timer for the still blocked jobs is reset. This happens indirectly via the expiration time of the semaphore, which is updated.
|
|
474
493
|
|
|
475
|
-
When using `discard` as the behaviour to handle conflicts, you might have jobs discarded for
|
|
494
|
+
When using `discard` as the behaviour to handle conflicts, you might have jobs discarded for until the `duration` interval if something happens and a running job fails to release the semaphore.
|
|
476
495
|
|
|
477
496
|
|
|
478
497
|
For example:
|
|
@@ -732,6 +751,38 @@ my_periodic_resque_job:
|
|
|
732
751
|
|
|
733
752
|
and the job will be enqueued via `perform_later` so it'll run in Resque. However, in this case we won't track any `solid_queue_recurring_execution` record for it and there won't be any guarantees that the job is enqueued only once each time.
|
|
734
753
|
|
|
754
|
+
### Scheduling and unscheduling recurring tasks dynamically
|
|
755
|
+
|
|
756
|
+
You can schedule and unschedule recurring tasks at runtime, without editing the configuration file. To enable this, you need to set `dynamic_tasks_enabled: true` in the `scheduler` section of your `config/queue.yml`, [as explained earlier](#optional-scheduler-configuration).
|
|
757
|
+
|
|
758
|
+
```yaml
|
|
759
|
+
scheduler:
|
|
760
|
+
dynamic_tasks_enabled: true
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
Then you can use the following methods to add recurring tasks dynamically:
|
|
764
|
+
|
|
765
|
+
```ruby
|
|
766
|
+
SolidQueue.schedule_recurring_task(
|
|
767
|
+
"my_dynamic_task",
|
|
768
|
+
class: "MyJob",
|
|
769
|
+
args: [1, 2],
|
|
770
|
+
schedule: "every 10 minutes"
|
|
771
|
+
)
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
This accepts the same options as the YAML configuration: `class`, `args`, `command`, `schedule`, `queue`, `priority`, and `description`.
|
|
775
|
+
|
|
776
|
+
To remove a dynamically scheduled task:
|
|
777
|
+
|
|
778
|
+
```ruby
|
|
779
|
+
SolidQueue.unschedule_recurring_task("my_dynamic_task")
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
Only dynamic tasks can be unscheduled at runtime. Attempting to unschedule a static task (defined in `config/recurring.yml`) will raise an `ActiveRecord::RecordNotFound` error.
|
|
783
|
+
|
|
784
|
+
Tasks scheduled like this persist between Solid Queue's restarts and won't stop running until you manually unschedule them.
|
|
785
|
+
|
|
735
786
|
## Inspiration
|
|
736
787
|
|
|
737
788
|
Solid Queue has been inspired by [resque](https://github.com/resque/resque) and [GoodJob](https://github.com/bensheldon/good_job). We recommend checking out these projects as they're great examples from which we've learnt a lot.
|
|
@@ -26,7 +26,9 @@ module SolidQueue
|
|
|
26
26
|
|
|
27
27
|
def release_one(concurrency_key)
|
|
28
28
|
transaction do
|
|
29
|
-
if execution = ordered.where(concurrency_key: concurrency_key).limit(1)
|
|
29
|
+
if execution = ordered.where(concurrency_key: concurrency_key).limit(1)
|
|
30
|
+
.use_index(:index_solid_queue_blocked_executions_for_release)
|
|
31
|
+
.non_blocking_lock.first
|
|
30
32
|
execution.release
|
|
31
33
|
end
|
|
32
34
|
end
|
|
@@ -30,7 +30,8 @@ module SolidQueue
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def select_candidates(queue_relation, limit)
|
|
33
|
-
|
|
33
|
+
# Force query execution here with #to_a to avoid unintended FOR UPDATE query executions
|
|
34
|
+
queue_relation.ordered.limit(limit).non_blocking_lock.select(:id, :job_id).to_a
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def lock_candidates(executions, process_id)
|
|
@@ -20,6 +20,13 @@ module SolidQueue
|
|
|
20
20
|
connection.supports_insert_conflict_target?
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
|
+
|
|
24
|
+
# Pass index hints to the query optimizer using SQL comment hints.
|
|
25
|
+
# Uses MySQL 8 optimizer hint query comments, which SQLite and
|
|
26
|
+
# PostgreSQL ignore.
|
|
27
|
+
def use_index(*indexes)
|
|
28
|
+
optimizer_hints "INDEX(#{quoted_table_name} #{indexes.join(', ')})"
|
|
29
|
+
end
|
|
23
30
|
end
|
|
24
31
|
end
|
|
25
32
|
end
|
|
@@ -11,6 +11,7 @@ module SolidQueue
|
|
|
11
11
|
validate :ensure_existing_job_class
|
|
12
12
|
|
|
13
13
|
scope :static, -> { where(static: true) }
|
|
14
|
+
scope :dynamic, -> { where(static: false) }
|
|
14
15
|
|
|
15
16
|
has_many :recurring_executions, foreign_key: :task_key, primary_key: :key
|
|
16
17
|
|
|
@@ -32,7 +33,15 @@ module SolidQueue
|
|
|
32
33
|
queue_name: options[:queue].presence,
|
|
33
34
|
priority: options[:priority].presence,
|
|
34
35
|
description: options[:description],
|
|
35
|
-
static: true
|
|
36
|
+
static: options.fetch(:static, true)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def create_dynamic_task(key, **options)
|
|
40
|
+
from_configuration(key, **options.merge(static: false)).save!
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def delete_dynamic_task(key)
|
|
44
|
+
RecurringTask.dynamic.find_by!(key: key).destroy
|
|
36
45
|
end
|
|
37
46
|
|
|
38
47
|
def create_or_update_all(tasks)
|
|
@@ -28,6 +28,11 @@ module SolidQueue
|
|
|
28
28
|
concurrency_maintenance_interval: 600
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
SCHEDULER_DEFAULTS = {
|
|
32
|
+
polling_interval: 5,
|
|
33
|
+
dynamic_tasks_enabled: false
|
|
34
|
+
}
|
|
35
|
+
|
|
31
36
|
DEFAULT_CONFIG_FILE_PATH = "config/queue.yml"
|
|
32
37
|
DEFAULT_RECURRING_SCHEDULE_FILE_PATH = "config/recurring.yml"
|
|
33
38
|
|
|
@@ -137,8 +142,10 @@ module SolidQueue
|
|
|
137
142
|
end
|
|
138
143
|
|
|
139
144
|
def schedulers
|
|
140
|
-
if
|
|
141
|
-
|
|
145
|
+
return [] if skip_recurring_tasks?
|
|
146
|
+
|
|
147
|
+
if recurring_tasks.any? || dynamic_recurring_tasks_enabled?
|
|
148
|
+
[ Process.new(:scheduler, { recurring_tasks: recurring_tasks, **scheduler_options.with_defaults(SCHEDULER_DEFAULTS) }) ]
|
|
142
149
|
else
|
|
143
150
|
[]
|
|
144
151
|
end
|
|
@@ -154,17 +161,29 @@ module SolidQueue
|
|
|
154
161
|
.map { |options| options.dup.symbolize_keys }
|
|
155
162
|
end
|
|
156
163
|
|
|
164
|
+
def scheduler_options
|
|
165
|
+
@scheduler_options ||= processes_config.fetch(:scheduler, {}).dup.symbolize_keys
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def dynamic_recurring_tasks_enabled?
|
|
169
|
+
scheduler_options.fetch(:dynamic_tasks_enabled, SCHEDULER_DEFAULTS[:dynamic_tasks_enabled])
|
|
170
|
+
end
|
|
171
|
+
|
|
157
172
|
def recurring_tasks
|
|
158
173
|
@recurring_tasks ||= recurring_tasks_config.map do |id, options|
|
|
159
|
-
RecurringTask.from_configuration(id, **options) if options&.has_key?(:schedule)
|
|
174
|
+
RecurringTask.from_configuration(id, **options.merge(static: true)) if options&.has_key?(:schedule)
|
|
160
175
|
end.compact
|
|
161
176
|
end
|
|
162
177
|
|
|
163
178
|
def processes_config
|
|
164
179
|
@processes_config ||= config_from \
|
|
165
|
-
options.slice(:workers, :dispatchers).presence || options[:config_file],
|
|
166
|
-
keys: [ :workers, :dispatchers ],
|
|
167
|
-
fallback: {
|
|
180
|
+
options.slice(:workers, :dispatchers, :scheduler).presence || options[:config_file],
|
|
181
|
+
keys: [ :workers, :dispatchers, :scheduler ],
|
|
182
|
+
fallback: {
|
|
183
|
+
workers: [ WORKER_DEFAULTS ],
|
|
184
|
+
dispatchers: [ DISPATCHER_DEFAULTS ],
|
|
185
|
+
scheduler: SCHEDULER_DEFAULTS
|
|
186
|
+
}
|
|
168
187
|
end
|
|
169
188
|
|
|
170
189
|
def recurring_tasks_config
|
|
@@ -173,7 +192,6 @@ module SolidQueue
|
|
|
173
192
|
end
|
|
174
193
|
end
|
|
175
194
|
|
|
176
|
-
|
|
177
195
|
def config_from(file_or_hash, keys: [], fallback: {}, env: Rails.env)
|
|
178
196
|
load_config_from(file_or_hash).then do |config|
|
|
179
197
|
config = config[env.to_sym] ? config[env.to_sym] : config
|
|
@@ -4,21 +4,28 @@ module SolidQueue
|
|
|
4
4
|
class Scheduler::RecurringSchedule
|
|
5
5
|
include AppExecutor
|
|
6
6
|
|
|
7
|
-
attr_reader :
|
|
7
|
+
attr_reader :scheduled_tasks
|
|
8
|
+
|
|
9
|
+
def initialize(static_tasks, dynamic_tasks_enabled: false)
|
|
10
|
+
@static_tasks = Array(static_tasks).map { |task| RecurringTask.wrap(task) }.select(&:valid?)
|
|
11
|
+
@dynamic_tasks_enabled = dynamic_tasks_enabled
|
|
8
12
|
|
|
9
|
-
def initialize(tasks)
|
|
10
|
-
@configured_tasks = Array(tasks).map { |task| SolidQueue::RecurringTask.wrap(task) }.select(&:valid?)
|
|
11
13
|
@scheduled_tasks = Concurrent::Hash.new
|
|
12
14
|
end
|
|
13
15
|
|
|
16
|
+
def configured_tasks
|
|
17
|
+
static_tasks + dynamic_tasks
|
|
18
|
+
end
|
|
19
|
+
|
|
14
20
|
def empty?
|
|
15
|
-
|
|
21
|
+
scheduled_tasks.empty? && dynamic_tasks.empty?
|
|
16
22
|
end
|
|
17
23
|
|
|
18
24
|
def schedule_tasks
|
|
19
25
|
wrap_in_app_executor do
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
persist_static_tasks
|
|
27
|
+
reload_static_tasks
|
|
28
|
+
reload_dynamic_tasks
|
|
22
29
|
end
|
|
23
30
|
|
|
24
31
|
configured_tasks.each do |task|
|
|
@@ -39,14 +46,57 @@ module SolidQueue
|
|
|
39
46
|
configured_tasks.map(&:key)
|
|
40
47
|
end
|
|
41
48
|
|
|
49
|
+
def reschedule_dynamic_tasks
|
|
50
|
+
wrap_in_app_executor do
|
|
51
|
+
reload_dynamic_tasks
|
|
52
|
+
schedule_created_dynamic_tasks
|
|
53
|
+
unschedule_deleted_dynamic_tasks
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
42
57
|
private
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
58
|
+
attr_reader :static_tasks
|
|
59
|
+
|
|
60
|
+
def static_task_keys
|
|
61
|
+
static_tasks.map(&:key)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def dynamic_tasks
|
|
65
|
+
@dynamic_tasks ||= load_dynamic_tasks
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def dynamic_tasks_enabled?
|
|
69
|
+
@dynamic_tasks_enabled
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def schedule_created_dynamic_tasks
|
|
73
|
+
RecurringTask.dynamic.where.not(key: scheduled_tasks.keys).each do |task|
|
|
74
|
+
schedule_task(task)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def unschedule_deleted_dynamic_tasks
|
|
79
|
+
(scheduled_tasks.keys - RecurringTask.pluck(:key)).each do |key|
|
|
80
|
+
scheduled_tasks[key].cancel
|
|
81
|
+
scheduled_tasks.delete(key)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def persist_static_tasks
|
|
86
|
+
RecurringTask.static.where.not(key: static_task_keys).delete_all
|
|
87
|
+
RecurringTask.create_or_update_all static_tasks
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def reload_static_tasks
|
|
91
|
+
@static_tasks = RecurringTask.static.where(key: static_task_keys).to_a
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def reload_dynamic_tasks
|
|
95
|
+
@dynamic_tasks = load_dynamic_tasks
|
|
46
96
|
end
|
|
47
97
|
|
|
48
|
-
def
|
|
49
|
-
|
|
98
|
+
def load_dynamic_tasks
|
|
99
|
+
dynamic_tasks_enabled? ? RecurringTask.dynamic.to_a : []
|
|
50
100
|
end
|
|
51
101
|
|
|
52
102
|
def schedule(task)
|
|
@@ -5,7 +5,7 @@ module SolidQueue
|
|
|
5
5
|
include Processes::Runnable
|
|
6
6
|
include LifecycleHooks
|
|
7
7
|
|
|
8
|
-
attr_reader :recurring_schedule
|
|
8
|
+
attr_reader :recurring_schedule, :polling_interval
|
|
9
9
|
|
|
10
10
|
after_boot :run_start_hooks
|
|
11
11
|
after_boot :schedule_recurring_tasks
|
|
@@ -14,7 +14,10 @@ module SolidQueue
|
|
|
14
14
|
after_shutdown :run_exit_hooks
|
|
15
15
|
|
|
16
16
|
def initialize(recurring_tasks:, **options)
|
|
17
|
-
|
|
17
|
+
options = options.dup.with_defaults(SolidQueue::Configuration::SCHEDULER_DEFAULTS)
|
|
18
|
+
@dynamic_tasks_enabled = options[:dynamic_tasks_enabled]
|
|
19
|
+
@polling_interval = options[:polling_interval]
|
|
20
|
+
@recurring_schedule = RecurringSchedule.new(recurring_tasks, dynamic_tasks_enabled: @dynamic_tasks_enabled)
|
|
18
21
|
|
|
19
22
|
super(**options)
|
|
20
23
|
end
|
|
@@ -24,13 +27,16 @@ module SolidQueue
|
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
private
|
|
27
|
-
|
|
30
|
+
|
|
31
|
+
STATIC_SLEEP_INTERVAL = 60
|
|
28
32
|
|
|
29
33
|
def run
|
|
30
34
|
loop do
|
|
31
35
|
break if shutting_down?
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
reload_dynamic_schedule if dynamic_tasks_enabled?
|
|
38
|
+
|
|
39
|
+
interruptible_sleep(sleep_interval)
|
|
34
40
|
end
|
|
35
41
|
ensure
|
|
36
42
|
SolidQueue.instrument(:shutdown_process, process: self) do
|
|
@@ -46,10 +52,23 @@ module SolidQueue
|
|
|
46
52
|
recurring_schedule.unschedule_tasks
|
|
47
53
|
end
|
|
48
54
|
|
|
55
|
+
def reload_dynamic_schedule
|
|
56
|
+
recurring_schedule.reschedule_dynamic_tasks
|
|
57
|
+
reload_metadata
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def dynamic_tasks_enabled?
|
|
61
|
+
@dynamic_tasks_enabled
|
|
62
|
+
end
|
|
63
|
+
|
|
49
64
|
def all_work_completed?
|
|
50
65
|
recurring_schedule.empty?
|
|
51
66
|
end
|
|
52
67
|
|
|
68
|
+
def sleep_interval
|
|
69
|
+
dynamic_tasks_enabled? ? polling_interval : STATIC_SLEEP_INTERVAL
|
|
70
|
+
end
|
|
71
|
+
|
|
53
72
|
def set_procline
|
|
54
73
|
procline "scheduling #{recurring_schedule.task_keys.join(",")}"
|
|
55
74
|
end
|
data/lib/solid_queue/version.rb
CHANGED
data/lib/solid_queue.rb
CHANGED
|
@@ -43,6 +43,14 @@ module SolidQueue
|
|
|
43
43
|
|
|
44
44
|
delegate :on_start, :on_stop, :on_exit, to: Supervisor
|
|
45
45
|
|
|
46
|
+
def schedule_recurring_task(key, **options)
|
|
47
|
+
RecurringTask.create_dynamic_task(key, **options)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def unschedule_recurring_task(key)
|
|
51
|
+
RecurringTask.delete_dynamic_task(key)
|
|
52
|
+
end
|
|
53
|
+
|
|
46
54
|
[ Dispatcher, Scheduler, Worker ].each do |process|
|
|
47
55
|
define_singleton_method(:"on_#{process.name.demodulize.downcase}_start") do |&block|
|
|
48
56
|
process.on_start(&block)
|
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.
|
|
4
|
+
version: 1.4.0
|
|
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-
|
|
11
|
+
date: 2026-03-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|