activejob 8.1.0.beta1 → 8.1.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/CHANGELOG.md +17 -1
- data/lib/active_job/arguments.rb +7 -3
- data/lib/active_job/base.rb +1 -0
- data/lib/active_job/core.rb +10 -2
- data/lib/active_job/gem_version.rb +1 -1
- data/lib/active_job/serializers/action_controller_parameters_serializer.rb +1 -1
- data/lib/active_job/serializers/object_serializer.rb +6 -7
- data/lib/active_job/serializers/range_serializer.rb +2 -2
- data/lib/active_job/serializers.rb +8 -0
- data/lib/active_job/structured_event_subscriber.rb +220 -0
- data/lib/active_job.rb +1 -0
- metadata +7 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bc270930c258d16e62b3f01ee4ed3591e91b680ddea4c43ddba1870206bda41f
|
|
4
|
+
data.tar.gz: 6bc1d66fb46babd36a3959754021ddbc6d115fbeef7e78d025b4880e48c63db1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aed3444d15f38a03c9df4321d898958c8c4611c427dd4ad6ada5d14804db2dced96aa035d98e3d777c0280d1c7c0433a1888cb930ff55692882eb1dbecfb729e
|
|
7
|
+
data.tar.gz: 47396127fb20f53dc5ec782619913145fa911128b04ab3e8f80dc52d4b3071634653f49c6271f86a1fe1ccf58595d1dd6a7fc0453d37eb2a3a9a250fa601ded8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
|
-
## Rails 8.1.0
|
|
1
|
+
## Rails 8.1.0 (October 22, 2025) ##
|
|
2
|
+
|
|
3
|
+
* Add structured events for Active Job:
|
|
4
|
+
- `active_job.enqueued`
|
|
5
|
+
- `active_job.bulk_enqueued`
|
|
6
|
+
- `active_job.started`
|
|
7
|
+
- `active_job.completed`
|
|
8
|
+
- `active_job.retry_scheduled`
|
|
9
|
+
- `active_job.retry_stopped`
|
|
10
|
+
- `active_job.discarded`
|
|
11
|
+
- `active_job.interrupt`
|
|
12
|
+
- `active_job.resume`
|
|
13
|
+
- `active_job.step_skipped`
|
|
14
|
+
- `active_job.step_started`
|
|
15
|
+
- `active_job.step`
|
|
16
|
+
|
|
17
|
+
*Adrianna Chang*
|
|
2
18
|
|
|
3
19
|
* Deprecate built-in `sidekiq` adapter.
|
|
4
20
|
|
data/lib/active_job/arguments.rb
CHANGED
|
@@ -31,7 +31,11 @@ module ActiveJob
|
|
|
31
31
|
# serialized without mutation are returned as-is. Arrays/Hashes are
|
|
32
32
|
# serialized element by element. All other types are serialized using
|
|
33
33
|
# GlobalID.
|
|
34
|
-
def serialize(
|
|
34
|
+
def serialize(arguments)
|
|
35
|
+
arguments.map { |argument| serialize_argument(argument) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def serialize_argument(argument) # :nodoc:
|
|
35
39
|
case argument
|
|
36
40
|
when nil, true, false, Integer, Float # Types that can hardly be subclassed
|
|
37
41
|
argument
|
|
@@ -50,7 +54,7 @@ module ActiveJob
|
|
|
50
54
|
when GlobalID::Identification
|
|
51
55
|
convert_to_global_id_hash(argument)
|
|
52
56
|
when Array
|
|
53
|
-
argument.map { |arg|
|
|
57
|
+
argument.map { |arg| serialize_argument(arg) }
|
|
54
58
|
when ActiveSupport::HashWithIndifferentAccess
|
|
55
59
|
serialize_indifferent_hash(argument)
|
|
56
60
|
when Hash
|
|
@@ -137,7 +141,7 @@ module ActiveJob
|
|
|
137
141
|
|
|
138
142
|
def serialize_hash(argument)
|
|
139
143
|
argument.each_with_object({}) do |(key, value), hash|
|
|
140
|
-
hash[serialize_hash_key(key)] =
|
|
144
|
+
hash[serialize_hash_key(key)] = serialize_argument(value)
|
|
141
145
|
end
|
|
142
146
|
end
|
|
143
147
|
|
data/lib/active_job/base.rb
CHANGED
|
@@ -9,6 +9,7 @@ require "active_job/execution"
|
|
|
9
9
|
require "active_job/callbacks"
|
|
10
10
|
require "active_job/exceptions"
|
|
11
11
|
require "active_job/log_subscriber"
|
|
12
|
+
require "active_job/structured_event_subscriber"
|
|
12
13
|
require "active_job/logging"
|
|
13
14
|
require "active_job/instrumentation"
|
|
14
15
|
require "active_job/execution_state"
|
data/lib/active_job/core.rb
CHANGED
|
@@ -167,8 +167,8 @@ module ActiveJob
|
|
|
167
167
|
self.exception_executions = job_data["exception_executions"]
|
|
168
168
|
self.locale = job_data["locale"] || I18n.locale.to_s
|
|
169
169
|
self.timezone = job_data["timezone"] || Time.zone&.name
|
|
170
|
-
self.enqueued_at =
|
|
171
|
-
self.scheduled_at =
|
|
170
|
+
self.enqueued_at = deserialize_time(job_data["enqueued_at"]) if job_data["enqueued_at"]
|
|
171
|
+
self.scheduled_at = deserialize_time(job_data["scheduled_at"]) if job_data["scheduled_at"]
|
|
172
172
|
end
|
|
173
173
|
|
|
174
174
|
# Configures the job with the given options.
|
|
@@ -208,5 +208,13 @@ module ActiveJob
|
|
|
208
208
|
def arguments_serialized?
|
|
209
209
|
@serialized_arguments
|
|
210
210
|
end
|
|
211
|
+
|
|
212
|
+
def deserialize_time(time)
|
|
213
|
+
if time.is_a?(Time)
|
|
214
|
+
time
|
|
215
|
+
else
|
|
216
|
+
Time.iso8601(time)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
211
219
|
end
|
|
212
220
|
end
|
|
@@ -4,7 +4,7 @@ module ActiveJob
|
|
|
4
4
|
module Serializers
|
|
5
5
|
class ActionControllerParametersSerializer < ObjectSerializer
|
|
6
6
|
def serialize(argument)
|
|
7
|
-
Arguments.
|
|
7
|
+
Arguments.serialize_argument(argument.to_h.with_indifferent_access)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def deserialize(hash)
|
|
@@ -28,6 +28,11 @@ module ActiveJob
|
|
|
28
28
|
delegate :serialize?, :serialize, :deserialize, to: :instance
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
def initialize
|
|
32
|
+
super
|
|
33
|
+
@template = { Arguments::OBJECT_SERIALIZER_KEY => self.class.name }.freeze
|
|
34
|
+
end
|
|
35
|
+
|
|
31
36
|
# Determines if an argument should be serialized by a serializer.
|
|
32
37
|
def serialize?(argument)
|
|
33
38
|
argument.is_a?(klass)
|
|
@@ -35,19 +40,13 @@ module ActiveJob
|
|
|
35
40
|
|
|
36
41
|
# Serializes an argument to a JSON primitive type.
|
|
37
42
|
def serialize(hash)
|
|
38
|
-
hash
|
|
39
|
-
hash
|
|
43
|
+
@template.merge(hash)
|
|
40
44
|
end
|
|
41
45
|
|
|
42
46
|
# Deserializes an argument from a JSON primitive type.
|
|
43
47
|
def deserialize(hash)
|
|
44
48
|
raise NotImplementedError, "#{self.class.name} should implement a public #deserialize(hash) method"
|
|
45
49
|
end
|
|
46
|
-
|
|
47
|
-
# The class of the object that will be serialized.
|
|
48
|
-
def klass
|
|
49
|
-
raise NotImplementedError, "#{self.class.name} should implement a public #klass method"
|
|
50
|
-
end
|
|
51
50
|
end
|
|
52
51
|
end
|
|
53
52
|
end
|
|
@@ -5,8 +5,8 @@ module ActiveJob
|
|
|
5
5
|
class RangeSerializer < ObjectSerializer
|
|
6
6
|
def serialize(range)
|
|
7
7
|
super(
|
|
8
|
-
"begin" => Arguments.
|
|
9
|
-
"end" => Arguments.
|
|
8
|
+
"begin" => Arguments.serialize_argument(range.begin),
|
|
9
|
+
"end" => Arguments.serialize_argument(range.end),
|
|
10
10
|
"exclude_end" => range.exclude_end?, # Always boolean, no need to serialize
|
|
11
11
|
)
|
|
12
12
|
end
|
|
@@ -81,8 +81,16 @@ module ActiveJob
|
|
|
81
81
|
klass = s.send(:klass)
|
|
82
82
|
ActiveJob.deprecator.warn(<<~MSG.squish)
|
|
83
83
|
#{s.class.name}#klass method should be public.
|
|
84
|
+
This will raise an error in Rails 8.2.
|
|
84
85
|
MSG
|
|
85
86
|
@serializers_index[klass] = s
|
|
87
|
+
else
|
|
88
|
+
ActiveJob.deprecator.warn(
|
|
89
|
+
<<~MSG.squish
|
|
90
|
+
#{s.class.name} should implement a public #klass method.
|
|
91
|
+
This will raise an error in Rails 8.2.
|
|
92
|
+
MSG
|
|
93
|
+
)
|
|
86
94
|
end
|
|
87
95
|
end
|
|
88
96
|
end
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/structured_event_subscriber"
|
|
4
|
+
|
|
5
|
+
module ActiveJob
|
|
6
|
+
class StructuredEventSubscriber < ActiveSupport::StructuredEventSubscriber # :nodoc:
|
|
7
|
+
def enqueue(event)
|
|
8
|
+
job = event.payload[:job]
|
|
9
|
+
adapter = event.payload[:adapter]
|
|
10
|
+
exception = event.payload[:exception_object] || job.enqueue_error
|
|
11
|
+
payload = {
|
|
12
|
+
job_class: job.class.name,
|
|
13
|
+
job_id: job.job_id,
|
|
14
|
+
queue: job.queue_name,
|
|
15
|
+
adapter: ActiveJob.adapter_name(adapter),
|
|
16
|
+
aborted: event.payload[:aborted],
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if exception
|
|
20
|
+
payload[:exception_class] = exception.class.name
|
|
21
|
+
payload[:exception_message] = exception.message
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if job.class.log_arguments?
|
|
25
|
+
payload[:arguments] = job.arguments
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
emit_event("active_job.enqueued", payload)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def enqueue_at(event)
|
|
32
|
+
job = event.payload[:job]
|
|
33
|
+
adapter = event.payload[:adapter]
|
|
34
|
+
exception = event.payload[:exception_object] || job.enqueue_error
|
|
35
|
+
payload = {
|
|
36
|
+
job_class: job.class.name,
|
|
37
|
+
job_id: job.job_id,
|
|
38
|
+
queue: job.queue_name,
|
|
39
|
+
scheduled_at: job.scheduled_at,
|
|
40
|
+
adapter: ActiveJob.adapter_name(adapter),
|
|
41
|
+
aborted: event.payload[:aborted],
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if exception
|
|
45
|
+
payload[:exception_class] = exception.class.name
|
|
46
|
+
payload[:exception_message] = exception.message
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if job.class.log_arguments?
|
|
50
|
+
payload[:arguments] = job.arguments
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
emit_event("active_job.enqueued_at", payload)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def enqueue_all(event)
|
|
57
|
+
jobs = event.payload[:jobs]
|
|
58
|
+
adapter = event.payload[:adapter]
|
|
59
|
+
enqueued_count = event.payload[:enqueued_count].to_i
|
|
60
|
+
failed_count = jobs.size - enqueued_count
|
|
61
|
+
|
|
62
|
+
emit_event("active_job.bulk_enqueued",
|
|
63
|
+
adapter: ActiveJob.adapter_name(adapter),
|
|
64
|
+
job_count: jobs.size,
|
|
65
|
+
enqueued_count: enqueued_count,
|
|
66
|
+
failed_enqueue_count: failed_count,
|
|
67
|
+
enqueued_classes: jobs.filter_map do |job|
|
|
68
|
+
job.class.name if jobs.count == enqueued_count || job.successfully_enqueued?
|
|
69
|
+
end.tally
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def perform_start(event)
|
|
74
|
+
job = event.payload[:job]
|
|
75
|
+
payload = {
|
|
76
|
+
job_class: job.class.name,
|
|
77
|
+
job_id: job.job_id,
|
|
78
|
+
queue: job.queue_name,
|
|
79
|
+
enqueued_at: job.enqueued_at&.utc&.iso8601(9),
|
|
80
|
+
}
|
|
81
|
+
if job.class.log_arguments?
|
|
82
|
+
payload[:arguments] = job.arguments
|
|
83
|
+
end
|
|
84
|
+
emit_event("active_job.started", payload)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def perform(event)
|
|
88
|
+
job = event.payload[:job]
|
|
89
|
+
exception = event.payload[:exception_object]
|
|
90
|
+
adapter = event.payload[:adapter]
|
|
91
|
+
payload = {
|
|
92
|
+
job_class: job.class.name,
|
|
93
|
+
job_id: job.job_id,
|
|
94
|
+
queue: job.queue_name,
|
|
95
|
+
adapter: ActiveJob.adapter_name(adapter),
|
|
96
|
+
aborted: event.payload[:aborted],
|
|
97
|
+
duration: event.duration.round(2),
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if exception
|
|
101
|
+
payload[:exception_class] = exception.class.name
|
|
102
|
+
payload[:exception_message] = exception.message
|
|
103
|
+
payload[:exception_backtrace] = exception.backtrace
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
emit_event("active_job.completed", payload)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def enqueue_retry(event)
|
|
110
|
+
job = event.payload[:job]
|
|
111
|
+
exception = event.payload[:error]
|
|
112
|
+
wait = event.payload[:wait]
|
|
113
|
+
|
|
114
|
+
emit_event("active_job.retry_scheduled",
|
|
115
|
+
job_class: job.class.name,
|
|
116
|
+
job_id: job.job_id,
|
|
117
|
+
executions: job.executions,
|
|
118
|
+
wait_seconds: wait.to_i,
|
|
119
|
+
exception_class: exception&.class&.name,
|
|
120
|
+
exception_message: exception&.message
|
|
121
|
+
)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def retry_stopped(event)
|
|
125
|
+
job = event.payload[:job]
|
|
126
|
+
exception = event.payload[:error]
|
|
127
|
+
|
|
128
|
+
emit_event("active_job.retry_stopped",
|
|
129
|
+
job_class: job.class.name,
|
|
130
|
+
job_id: job.job_id,
|
|
131
|
+
executions: job.executions,
|
|
132
|
+
exception_class: exception.class.name,
|
|
133
|
+
exception_message: exception.message
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def discard(event)
|
|
138
|
+
job = event.payload[:job]
|
|
139
|
+
exception = event.payload[:error]
|
|
140
|
+
|
|
141
|
+
emit_event("active_job.discarded",
|
|
142
|
+
job_class: job.class.name,
|
|
143
|
+
job_id: job.job_id,
|
|
144
|
+
exception_class: exception.class.name,
|
|
145
|
+
exception_message: exception.message
|
|
146
|
+
)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def interrupt(event)
|
|
150
|
+
job = event.payload[:job]
|
|
151
|
+
description = event.payload[:description]
|
|
152
|
+
reason = event.payload[:reason]
|
|
153
|
+
|
|
154
|
+
emit_event("active_job.interrupt",
|
|
155
|
+
job_class: job.class.name,
|
|
156
|
+
job_id: job.job_id,
|
|
157
|
+
description: description,
|
|
158
|
+
reason: reason,
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def resume(event)
|
|
163
|
+
job = event.payload[:job]
|
|
164
|
+
description = event.payload[:description]
|
|
165
|
+
|
|
166
|
+
emit_event("active_job.resume",
|
|
167
|
+
job_class: job.class.name,
|
|
168
|
+
job_id: job.job_id,
|
|
169
|
+
description: description,
|
|
170
|
+
)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def step_skipped(event)
|
|
174
|
+
job = event.payload[:job]
|
|
175
|
+
step = event.payload[:step]
|
|
176
|
+
|
|
177
|
+
emit_event("active_job.step_skipped",
|
|
178
|
+
job_class: job.class.name,
|
|
179
|
+
job_id: job.job_id,
|
|
180
|
+
step: step.name,
|
|
181
|
+
)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def step_started(event)
|
|
185
|
+
job = event.payload[:job]
|
|
186
|
+
step = event.payload[:step]
|
|
187
|
+
|
|
188
|
+
emit_event("active_job.step_started",
|
|
189
|
+
job_class: job.class.name,
|
|
190
|
+
job_id: job.job_id,
|
|
191
|
+
step: step.name,
|
|
192
|
+
cursor: step.cursor,
|
|
193
|
+
resumed: step.resumed?,
|
|
194
|
+
)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def step(event)
|
|
198
|
+
job = event.payload[:job]
|
|
199
|
+
step = event.payload[:step]
|
|
200
|
+
exception = event.payload[:exception_object]
|
|
201
|
+
payload = {
|
|
202
|
+
job_class: job.class.name,
|
|
203
|
+
job_id: job.job_id,
|
|
204
|
+
step: step.name,
|
|
205
|
+
cursor: step.cursor,
|
|
206
|
+
interrupted: event.payload[:interrupted],
|
|
207
|
+
duration: event.duration.round(2),
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if exception
|
|
211
|
+
payload[:exception_class] = exception.class.name
|
|
212
|
+
payload[:exception_message] = exception.message
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
emit_event("active_job.step", payload)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
ActiveJob::StructuredEventSubscriber.attach_to :active_job
|
data/lib/active_job.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activejob
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 8.1.0
|
|
4
|
+
version: 8.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Heinemeier Hansson
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - '='
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 8.1.0
|
|
18
|
+
version: 8.1.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - '='
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 8.1.0
|
|
25
|
+
version: 8.1.0
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: globalid
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -95,6 +95,7 @@ files:
|
|
|
95
95
|
- lib/active_job/serializers/time_object_serializer.rb
|
|
96
96
|
- lib/active_job/serializers/time_serializer.rb
|
|
97
97
|
- lib/active_job/serializers/time_with_zone_serializer.rb
|
|
98
|
+
- lib/active_job/structured_event_subscriber.rb
|
|
98
99
|
- lib/active_job/test_case.rb
|
|
99
100
|
- lib/active_job/test_helper.rb
|
|
100
101
|
- lib/active_job/version.rb
|
|
@@ -107,10 +108,10 @@ licenses:
|
|
|
107
108
|
- MIT
|
|
108
109
|
metadata:
|
|
109
110
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
|
110
|
-
changelog_uri: https://github.com/rails/rails/blob/v8.1.0
|
|
111
|
-
documentation_uri: https://api.rubyonrails.org/v8.1.0
|
|
111
|
+
changelog_uri: https://github.com/rails/rails/blob/v8.1.0/activejob/CHANGELOG.md
|
|
112
|
+
documentation_uri: https://api.rubyonrails.org/v8.1.0/
|
|
112
113
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
|
113
|
-
source_code_uri: https://github.com/rails/rails/tree/v8.1.0
|
|
114
|
+
source_code_uri: https://github.com/rails/rails/tree/v8.1.0/activejob
|
|
114
115
|
rubygems_mfa_required: 'true'
|
|
115
116
|
rdoc_options: []
|
|
116
117
|
require_paths:
|