solid_queue 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +1 -1
- data/app/models/solid_queue/claimed_execution.rb +13 -1
- data/app/models/solid_queue/execution/dispatching.rb +20 -0
- data/app/models/solid_queue/execution/job_attributes.rb +4 -4
- data/app/models/solid_queue/execution.rb +44 -2
- data/app/models/solid_queue/failed_execution.rb +26 -16
- data/app/models/solid_queue/job/clearable.rb +4 -1
- data/app/models/solid_queue/job/concurrency_controls.rb +17 -1
- data/app/models/solid_queue/job/executable.rb +20 -9
- data/app/models/solid_queue/job/schedulable.rb +9 -1
- data/app/models/solid_queue/job.rb +3 -0
- data/app/models/solid_queue/queue.rb +1 -1
- data/app/models/solid_queue/ready_execution.rb +10 -0
- data/app/models/solid_queue/scheduled_execution.rb +3 -11
- data/app/models/solid_queue/semaphore.rb +75 -46
- data/db/migrate/20240110143450_add_missing_index_to_blocked_executions.rb +5 -0
- data/lib/active_job/uniqueness.rb +41 -0
- data/lib/generators/solid_queue/install/USAGE +2 -2
- data/lib/generators/solid_queue/install/templates/config.yml +1 -1
- data/lib/solid_queue/dispatcher/scheduled_executions_dispatcher.rb +6 -0
- data/lib/solid_queue/processes/runnable.rb +2 -2
- data/lib/solid_queue/version.rb +1 -1
- data/lib/solid_queue/worker.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0eb31439e2768af5f5d3fb9289ac51056570c762ad140bc54aba81ee770d5a12
|
4
|
+
data.tar.gz: 4d3e4c2608b0a3ed2de82c7712c8a524e8745bc4e2ddf6907de8b3723560ab04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f4271aaf7b55b86d81f97bf19bbfa2b76de1ff994a6b71bb097b89ab9585212f28c0fdbd4fbb86b74c18b49cf229e7913eaa724784b3a62f000d60c2ade3263
|
7
|
+
data.tar.gz: a84d91b13db4a3ec96d1afa5fb5f39abc9ea0bf4bc216c14e20f4bf7e1bf2ff414e08f78166377950351b8170cbd734aa69322feb9076a67a3f9939ebacf6573
|
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
@@ -31,7 +31,7 @@ $ bin/rails generate solid_queue:install
|
|
31
31
|
|
32
32
|
This will set `solid_queue` as the Active Job's adapter in production, and will copy the required migration over to your app.
|
33
33
|
|
34
|
-
Alternatively, you can add
|
34
|
+
Alternatively, you can add only the migration to your app:
|
35
35
|
```bash
|
36
36
|
$ bin/rails solid_queue:install:migrations
|
37
37
|
```
|
@@ -23,6 +23,14 @@ class SolidQueue::ClaimedExecution < SolidQueue::Execution
|
|
23
23
|
def release_all
|
24
24
|
includes(:job).each(&:release)
|
25
25
|
end
|
26
|
+
|
27
|
+
def discard_all_in_batches(*)
|
28
|
+
raise UndiscardableError, "Can't discard jobs in progress"
|
29
|
+
end
|
30
|
+
|
31
|
+
def discard_all_from_jobs(*)
|
32
|
+
raise UndiscardableError, "Can't discard jobs in progress"
|
33
|
+
end
|
26
34
|
end
|
27
35
|
|
28
36
|
def perform
|
@@ -39,11 +47,15 @@ class SolidQueue::ClaimedExecution < SolidQueue::Execution
|
|
39
47
|
|
40
48
|
def release
|
41
49
|
transaction do
|
42
|
-
job.
|
50
|
+
job.dispatch_bypassing_concurrency_limits
|
43
51
|
destroy!
|
44
52
|
end
|
45
53
|
end
|
46
54
|
|
55
|
+
def discard
|
56
|
+
raise UndiscardableError, "Can't discard a job in progress"
|
57
|
+
end
|
58
|
+
|
47
59
|
private
|
48
60
|
def execute
|
49
61
|
ActiveJob::Base.execute(job.arguments)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidQueue
|
4
|
+
class Execution
|
5
|
+
module Dispatching
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
class_methods do
|
9
|
+
def dispatch_jobs(job_ids)
|
10
|
+
jobs = Job.where(id: job_ids)
|
11
|
+
|
12
|
+
Job.dispatch_all(jobs).map(&:id).tap do |dispatched_job_ids|
|
13
|
+
where(job_id: dispatched_job_ids).delete_all
|
14
|
+
SolidQueue.logger.info("[SolidQueue] Dispatched #{dispatched_job_ids.size} jobs")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -6,23 +6,23 @@ module SolidQueue
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
included do
|
9
|
-
class_attribute :
|
9
|
+
class_attribute :assumable_attributes_from_job, instance_accessor: false, default: %i[ queue_name priority ]
|
10
10
|
end
|
11
11
|
|
12
12
|
class_methods do
|
13
13
|
def assumes_attributes_from_job(*attribute_names)
|
14
|
-
self.
|
14
|
+
self.assumable_attributes_from_job |= attribute_names
|
15
15
|
before_create -> { assume_attributes_from_job }
|
16
16
|
end
|
17
17
|
|
18
18
|
def attributes_from_job(job)
|
19
|
-
job.attributes.symbolize_keys.slice(*
|
19
|
+
job.attributes.symbolize_keys.slice(*assumable_attributes_from_job)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
private
|
24
24
|
def assume_attributes_from_job
|
25
|
-
self.class.
|
25
|
+
self.class.assumable_attributes_from_job.each do |attribute|
|
26
26
|
send("#{attribute}=", job.send(attribute))
|
27
27
|
end
|
28
28
|
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module SolidQueue
|
4
4
|
class Execution < Record
|
5
|
+
class UndiscardableError < StandardError; end
|
6
|
+
|
5
7
|
include JobAttributes
|
6
8
|
|
7
9
|
self.abstract_class = true
|
@@ -10,8 +12,6 @@ module SolidQueue
|
|
10
12
|
|
11
13
|
belongs_to :job
|
12
14
|
|
13
|
-
alias_method :discard, :destroy
|
14
|
-
|
15
15
|
class << self
|
16
16
|
def create_all_from_jobs(jobs)
|
17
17
|
insert_all execution_data_from_jobs(jobs)
|
@@ -22,6 +22,48 @@ module SolidQueue
|
|
22
22
|
attributes_from_job(job).merge(job_id: job.id)
|
23
23
|
end
|
24
24
|
end
|
25
|
+
|
26
|
+
def discard_all_in_batches(batch_size: 500)
|
27
|
+
pending = count
|
28
|
+
discarded = 0
|
29
|
+
|
30
|
+
loop do
|
31
|
+
transaction do
|
32
|
+
job_ids = limit(batch_size).order(:job_id).lock.pluck(:job_id)
|
33
|
+
|
34
|
+
discard_jobs job_ids
|
35
|
+
discarded = where(job_id: job_ids).delete_all
|
36
|
+
pending -= discarded
|
37
|
+
end
|
38
|
+
|
39
|
+
break if pending <= 0 || discarded == 0
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def discard_all_from_jobs(jobs)
|
44
|
+
transaction do
|
45
|
+
job_ids = lock_all_from_jobs(jobs)
|
46
|
+
|
47
|
+
discard_jobs job_ids
|
48
|
+
where(job_id: job_ids).delete_all
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def lock_all_from_jobs(jobs)
|
54
|
+
where(job_id: jobs.map(&:id)).order(:job_id).lock.pluck(:job_id)
|
55
|
+
end
|
56
|
+
|
57
|
+
def discard_jobs(job_ids)
|
58
|
+
Job.where(id: job_ids).delete_all
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def discard
|
63
|
+
with_lock do
|
64
|
+
job.destroy
|
65
|
+
destroy
|
66
|
+
end
|
25
67
|
end
|
26
68
|
end
|
27
69
|
end
|
@@ -1,27 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
module SolidQueue
|
4
|
+
class FailedExecution < Execution
|
5
|
+
include Dispatching
|
5
6
|
|
6
|
-
|
7
|
+
serialize :error, coder: JSON
|
7
8
|
|
8
|
-
|
9
|
+
before_create :expand_error_details_from_exception
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
attr_accessor :exception
|
12
|
+
|
13
|
+
def self.retry_all(jobs)
|
14
|
+
transaction do
|
15
|
+
dispatch_jobs lock_all_from_jobs(jobs)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def retry
|
20
|
+
with_lock do
|
21
|
+
job.prepare_for_execution
|
22
|
+
destroy!
|
23
|
+
end
|
14
24
|
end
|
15
|
-
end
|
16
25
|
|
17
|
-
|
18
|
-
|
19
|
-
|
26
|
+
%i[ exception_class message backtrace ].each do |attribute|
|
27
|
+
define_method(attribute) { error.with_indifferent_access[attribute] }
|
28
|
+
end
|
20
29
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
30
|
+
private
|
31
|
+
def expand_error_details_from_exception
|
32
|
+
if exception
|
33
|
+
self.error = { exception_class: exception.class.name, message: exception.message, backtrace: exception.backtrace }
|
34
|
+
end
|
25
35
|
end
|
26
36
|
end
|
27
37
|
end
|
@@ -11,7 +11,10 @@ module SolidQueue
|
|
11
11
|
|
12
12
|
class_methods do
|
13
13
|
def clear_finished_in_batches(batch_size: 500, finished_before: SolidQueue.clear_finished_jobs_after.ago)
|
14
|
-
|
14
|
+
loop do
|
15
|
+
records_deleted = clearable(finished_before: finished_before).limit(batch_size).delete_all
|
16
|
+
break if records_deleted == 0
|
17
|
+
end
|
15
18
|
end
|
16
19
|
end
|
17
20
|
end
|
@@ -6,9 +6,17 @@ module SolidQueue
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
included do
|
9
|
-
has_one :blocked_execution
|
9
|
+
has_one :blocked_execution
|
10
10
|
|
11
11
|
delegate :concurrency_limit, :concurrency_duration, to: :job_class
|
12
|
+
|
13
|
+
before_destroy :unblock_next_blocked_job, if: -> { concurrency_limited? && ready? }
|
14
|
+
end
|
15
|
+
|
16
|
+
class_methods do
|
17
|
+
def release_all_concurrency_locks(jobs)
|
18
|
+
Semaphore.signal_all(jobs.select(&:concurrency_limited?))
|
19
|
+
end
|
12
20
|
end
|
13
21
|
|
14
22
|
def unblock_next_blocked_job
|
@@ -21,6 +29,10 @@ module SolidQueue
|
|
21
29
|
concurrency_key.present?
|
22
30
|
end
|
23
31
|
|
32
|
+
def blocked?
|
33
|
+
blocked_execution.present?
|
34
|
+
end
|
35
|
+
|
24
36
|
private
|
25
37
|
def acquire_concurrency_lock
|
26
38
|
return true unless concurrency_limited?
|
@@ -45,6 +57,10 @@ module SolidQueue
|
|
45
57
|
def job_class
|
46
58
|
@job_class ||= class_name.safe_constantize
|
47
59
|
end
|
60
|
+
|
61
|
+
def execution
|
62
|
+
super || blocked_execution
|
63
|
+
end
|
48
64
|
end
|
49
65
|
end
|
50
66
|
end
|
@@ -8,9 +8,9 @@ module SolidQueue
|
|
8
8
|
included do
|
9
9
|
include Clearable, ConcurrencyControls, Schedulable
|
10
10
|
|
11
|
-
has_one :ready_execution
|
12
|
-
has_one :claimed_execution
|
13
|
-
has_one :failed_execution
|
11
|
+
has_one :ready_execution
|
12
|
+
has_one :claimed_execution
|
13
|
+
has_one :failed_execution
|
14
14
|
|
15
15
|
after_create :prepare_for_execution
|
16
16
|
|
@@ -73,6 +73,10 @@ module SolidQueue
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
+
def dispatch_bypassing_concurrency_limits
|
77
|
+
ready
|
78
|
+
end
|
79
|
+
|
76
80
|
def finished!
|
77
81
|
if preserve_finished_jobs?
|
78
82
|
touch(:finished_at)
|
@@ -85,18 +89,22 @@ module SolidQueue
|
|
85
89
|
finished_at.present?
|
86
90
|
end
|
87
91
|
|
88
|
-
def
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
92
|
+
def status
|
93
|
+
if finished?
|
94
|
+
:finished
|
95
|
+
elsif execution.present?
|
96
|
+
execution.model_name.element.sub("_execution", "").to_sym
|
97
|
+
end
|
94
98
|
end
|
95
99
|
|
96
100
|
def retry
|
97
101
|
failed_execution&.retry
|
98
102
|
end
|
99
103
|
|
104
|
+
def discard
|
105
|
+
execution&.discard
|
106
|
+
end
|
107
|
+
|
100
108
|
def failed_with(exception)
|
101
109
|
FailedExecution.create_or_find_by!(job_id: id, exception: exception)
|
102
110
|
end
|
@@ -106,6 +114,9 @@ module SolidQueue
|
|
106
114
|
ReadyExecution.create_or_find_by!(job_id: id)
|
107
115
|
end
|
108
116
|
|
117
|
+
def execution
|
118
|
+
%w[ ready claimed failed ].reduce(nil) { |acc, status| acc || public_send("#{status}_execution") }
|
119
|
+
end
|
109
120
|
|
110
121
|
def preserve_finished_jobs?
|
111
122
|
SolidQueue.preserve_finished_jobs
|
@@ -6,7 +6,7 @@ module SolidQueue
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
included do
|
9
|
-
has_one :scheduled_execution
|
9
|
+
has_one :scheduled_execution
|
10
10
|
|
11
11
|
scope :scheduled, -> { where.not(finished_at: nil) }
|
12
12
|
end
|
@@ -31,10 +31,18 @@ module SolidQueue
|
|
31
31
|
scheduled_at.nil? || scheduled_at <= Time.current
|
32
32
|
end
|
33
33
|
|
34
|
+
def scheduled?
|
35
|
+
scheduled_execution.present?
|
36
|
+
end
|
37
|
+
|
34
38
|
private
|
35
39
|
def schedule
|
36
40
|
ScheduledExecution.create_or_find_by!(job_id: id)
|
37
41
|
end
|
42
|
+
|
43
|
+
def execution
|
44
|
+
super || scheduled_execution
|
45
|
+
end
|
38
46
|
end
|
39
47
|
end
|
40
48
|
end
|
@@ -15,9 +15,12 @@ module SolidQueue
|
|
15
15
|
prepare_all_for_execution(jobs).tap do |enqueued_jobs|
|
16
16
|
enqueued_jobs.each do |enqueued_job|
|
17
17
|
active_jobs_by_job_id[enqueued_job.active_job_id].provider_job_id = enqueued_job.id
|
18
|
+
active_jobs_by_job_id[enqueued_job.active_job_id].successfully_enqueued = true
|
18
19
|
end
|
19
20
|
end
|
20
21
|
end
|
22
|
+
|
23
|
+
active_jobs.count(&:successfully_enqueued?)
|
21
24
|
end
|
22
25
|
|
23
26
|
def enqueue(active_job, scheduled_at: Time.current)
|
@@ -15,6 +15,10 @@ module SolidQueue
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
def aggregated_count_across(queue_list)
|
19
|
+
QueueSelector.new(queue_list, self).scoped_relations.map(&:count).sum
|
20
|
+
end
|
21
|
+
|
18
22
|
private
|
19
23
|
def select_and_lock(queue_relation, process_id, limit)
|
20
24
|
return [] if limit <= 0
|
@@ -36,6 +40,12 @@ module SolidQueue
|
|
36
40
|
where(job_id: claimed.pluck(:job_id)).delete_all
|
37
41
|
end
|
38
42
|
end
|
43
|
+
|
44
|
+
|
45
|
+
def discard_jobs(job_ids)
|
46
|
+
Job.release_all_concurrency_locks Job.where(id: job_ids)
|
47
|
+
super
|
48
|
+
end
|
39
49
|
end
|
40
50
|
end
|
41
51
|
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module SolidQueue
|
4
4
|
class ScheduledExecution < Execution
|
5
|
+
include Dispatching
|
6
|
+
|
5
7
|
scope :due, -> { where(scheduled_at: ..Time.current) }
|
6
8
|
scope :ordered, -> { order(scheduled_at: :asc, priority: :asc) }
|
7
9
|
scope :next_batch, ->(batch_size) { due.ordered.limit(batch_size) }
|
@@ -14,20 +16,10 @@ module SolidQueue
|
|
14
16
|
job_ids = next_batch(batch_size).non_blocking_lock.pluck(:job_id)
|
15
17
|
if job_ids.empty? then []
|
16
18
|
else
|
17
|
-
|
19
|
+
dispatch_jobs(job_ids)
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
21
|
-
|
22
|
-
private
|
23
|
-
def dispatch_batch(job_ids)
|
24
|
-
jobs = Job.where(id: job_ids)
|
25
|
-
|
26
|
-
Job.dispatch_all(jobs).map(&:id).tap do |dispatched_job_ids|
|
27
|
-
where(job_id: dispatched_job_ids).delete_all
|
28
|
-
SolidQueue.logger.info("[SolidQueue] Dispatched scheduled batch with #{dispatched_job_ids.size} jobs")
|
29
|
-
end
|
30
|
-
end
|
31
23
|
end
|
32
24
|
end
|
33
25
|
end
|
@@ -1,65 +1,94 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
module SolidQueue
|
4
|
+
class Semaphore < Record
|
5
|
+
scope :available, -> { where("value > 0") }
|
6
|
+
scope :expired, -> { where(expires_at: ...Time.current) }
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
def signal(job)
|
13
|
-
Proxy.new(job, self).signal
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
class Proxy
|
18
|
-
def initialize(job, proxied_class)
|
19
|
-
@job = job
|
20
|
-
@proxied_class = proxied_class
|
21
|
-
end
|
8
|
+
class << self
|
9
|
+
def wait(job)
|
10
|
+
Proxy.new(job).wait
|
11
|
+
end
|
22
12
|
|
23
|
-
|
24
|
-
|
25
|
-
semaphore.value > 0 && attempt_decrement
|
26
|
-
else
|
27
|
-
attempt_creation
|
13
|
+
def signal(job)
|
14
|
+
Proxy.new(job).signal
|
28
15
|
end
|
29
|
-
end
|
30
16
|
|
31
|
-
|
32
|
-
|
17
|
+
def signal_all(jobs)
|
18
|
+
Proxy.signal_all(jobs)
|
19
|
+
end
|
33
20
|
end
|
34
21
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
def attempt_creation
|
39
|
-
proxied_class.create!(key: key, value: limit - 1, expires_at: expires_at)
|
40
|
-
true
|
41
|
-
rescue ActiveRecord::RecordNotUnique
|
42
|
-
attempt_decrement
|
22
|
+
class Proxy
|
23
|
+
def self.signal_all(jobs)
|
24
|
+
Semaphore.where(key: jobs.map(&:concurrency_key)).update_all("value = value + 1")
|
43
25
|
end
|
44
26
|
|
45
|
-
def
|
46
|
-
|
27
|
+
def initialize(job)
|
28
|
+
@job = job
|
29
|
+
@retries = 0
|
47
30
|
end
|
48
31
|
|
49
|
-
def
|
50
|
-
|
32
|
+
def wait
|
33
|
+
if semaphore = Semaphore.find_by(key: key)
|
34
|
+
semaphore.value > 0 && attempt_decrement
|
35
|
+
else
|
36
|
+
attempt_creation
|
37
|
+
end
|
51
38
|
end
|
52
39
|
|
53
|
-
def
|
54
|
-
|
40
|
+
def signal
|
41
|
+
attempt_increment
|
55
42
|
end
|
56
43
|
|
57
|
-
|
58
|
-
job
|
59
|
-
end
|
44
|
+
private
|
45
|
+
attr_accessor :job, :retries
|
60
46
|
|
61
|
-
|
62
|
-
|
63
|
-
|
47
|
+
def attempt_creation
|
48
|
+
Semaphore.create!(key: key, value: limit - 1, expires_at: expires_at)
|
49
|
+
true
|
50
|
+
rescue ActiveRecord::RecordNotUnique
|
51
|
+
attempt_decrement
|
52
|
+
end
|
53
|
+
|
54
|
+
def attempt_decrement
|
55
|
+
Semaphore.available.where(key: key).update_all([ "value = value - 1, expires_at = ?", expires_at ]) > 0
|
56
|
+
rescue ActiveRecord::Deadlocked
|
57
|
+
if retriable? then attempt_retry
|
58
|
+
else
|
59
|
+
raise
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def attempt_increment
|
64
|
+
Semaphore.where(key: key, value: ...limit).update_all([ "value = value + 1, expires_at = ?", expires_at ]) > 0
|
65
|
+
end
|
66
|
+
|
67
|
+
def attempt_retry
|
68
|
+
self.retries += 1
|
69
|
+
|
70
|
+
if semaphore = Semaphore.find_by(key: key)
|
71
|
+
semaphore.value > 0 && attempt_decrement
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
MAX_RETRIES = 1
|
76
|
+
|
77
|
+
def retriable?
|
78
|
+
retries < MAX_RETRIES
|
79
|
+
end
|
80
|
+
|
81
|
+
def key
|
82
|
+
job.concurrency_key
|
83
|
+
end
|
84
|
+
|
85
|
+
def expires_at
|
86
|
+
job.concurrency_duration.from_now
|
87
|
+
end
|
88
|
+
|
89
|
+
def limit
|
90
|
+
job.concurrency_limit
|
91
|
+
end
|
92
|
+
end
|
64
93
|
end
|
65
94
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Uniqueness
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
DEFAULT_UNIQUENESS_GROUP = ->(*) { self.class.name }
|
8
|
+
|
9
|
+
included do
|
10
|
+
class_attribute :uniqueness_key, instance_accessor: false
|
11
|
+
class_attribute :uniqueness_group, default: DEFAULT_UNIQUENESS_GROUP, instance_accessor: false
|
12
|
+
|
13
|
+
class_attribute :uniqueness_duration
|
14
|
+
end
|
15
|
+
|
16
|
+
class_methods do
|
17
|
+
def enqueued_uniquely_by(key:, group: DEFAULT_UNIQUENESS_GROUP, duration: SolidQueue.default_uniqueness_period)
|
18
|
+
self.uniqueness_key = key
|
19
|
+
self.uniqueness_group = group
|
20
|
+
self.uniqueness_duration = duration
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def uniqueness_key
|
25
|
+
if self.class.uniqueness_key
|
26
|
+
param = compute_concurrency_parameter(self.class.concurrency_key)
|
27
|
+
|
28
|
+
case param
|
29
|
+
when ActiveRecord::Base
|
30
|
+
[ concurrency_group, param.class.name, param.id ]
|
31
|
+
else
|
32
|
+
[ concurrency_group, param ]
|
33
|
+
end.compact.join("/")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def enqueued_uniquely?
|
38
|
+
uniqueness_key.present?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -4,6 +4,6 @@ Description:
|
|
4
4
|
Example:
|
5
5
|
bin/rails generate solid_queue:install
|
6
6
|
|
7
|
-
This will
|
7
|
+
This will perform the following:
|
8
8
|
Installs solid_queue migrations
|
9
|
-
Replaces Active Job's adapter in
|
9
|
+
Replaces Active Job's adapter in environment configuration
|
@@ -4,6 +4,8 @@ module SolidQueue::Processes
|
|
4
4
|
module Runnable
|
5
5
|
include Supervised
|
6
6
|
|
7
|
+
attr_writer :mode
|
8
|
+
|
7
9
|
def start
|
8
10
|
@stopping = false
|
9
11
|
|
@@ -19,8 +21,6 @@ module SolidQueue::Processes
|
|
19
21
|
end
|
20
22
|
|
21
23
|
private
|
22
|
-
attr_writer :mode
|
23
|
-
|
24
24
|
DEFAULT_MODE = :async
|
25
25
|
|
26
26
|
def mode
|
data/lib/solid_queue/version.rb
CHANGED
data/lib/solid_queue/worker.rb
CHANGED
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: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rosa Gutierrez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -79,6 +79,7 @@ files:
|
|
79
79
|
- app/models/solid_queue/blocked_execution.rb
|
80
80
|
- app/models/solid_queue/claimed_execution.rb
|
81
81
|
- app/models/solid_queue/execution.rb
|
82
|
+
- app/models/solid_queue/execution/dispatching.rb
|
82
83
|
- app/models/solid_queue/execution/job_attributes.rb
|
83
84
|
- app/models/solid_queue/failed_execution.rb
|
84
85
|
- app/models/solid_queue/job.rb
|
@@ -97,8 +98,10 @@ files:
|
|
97
98
|
- app/models/solid_queue/semaphore.rb
|
98
99
|
- config/routes.rb
|
99
100
|
- db/migrate/20231211200639_create_solid_queue_tables.rb
|
101
|
+
- db/migrate/20240110143450_add_missing_index_to_blocked_executions.rb
|
100
102
|
- lib/active_job/concurrency_controls.rb
|
101
103
|
- lib/active_job/queue_adapters/solid_queue_adapter.rb
|
104
|
+
- lib/active_job/uniqueness.rb
|
102
105
|
- lib/generators/solid_queue/install/USAGE
|
103
106
|
- lib/generators/solid_queue/install/install_generator.rb
|
104
107
|
- lib/generators/solid_queue/install/templates/config.yml
|
@@ -107,6 +110,7 @@ files:
|
|
107
110
|
- lib/solid_queue/app_executor.rb
|
108
111
|
- lib/solid_queue/configuration.rb
|
109
112
|
- lib/solid_queue/dispatcher.rb
|
113
|
+
- lib/solid_queue/dispatcher/scheduled_executions_dispatcher.rb
|
110
114
|
- lib/solid_queue/engine.rb
|
111
115
|
- lib/solid_queue/pool.rb
|
112
116
|
- lib/solid_queue/processes/base.rb
|