temporalio 1.1.0-aarch64-linux-musl → 1.2.0-aarch64-linux-musl
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/Gemfile +6 -4
- data/lib/temporalio/activity/definition.rb +6 -1
- data/lib/temporalio/api/activity/v1/message.rb +11 -2
- data/lib/temporalio/api/command/v1/message.rb +1 -1
- data/lib/temporalio/api/common/v1/message.rb +2 -1
- data/lib/temporalio/api/deployment/v1/message.rb +2 -1
- data/lib/temporalio/api/enums/v1/activity.rb +23 -0
- data/lib/temporalio/api/enums/v1/event_type.rb +1 -1
- data/lib/temporalio/api/enums/v1/failed_cause.rb +1 -1
- data/lib/temporalio/api/enums/v1/task_queue.rb +2 -1
- data/lib/temporalio/api/enums/v1/workflow.rb +3 -1
- data/lib/temporalio/api/errordetails/v1/message.rb +2 -1
- data/lib/temporalio/api/history/v1/message.rb +3 -1
- data/lib/temporalio/api/namespace/v1/message.rb +2 -1
- data/lib/temporalio/api/nexus/v1/message.rb +1 -1
- data/lib/temporalio/api/operatorservice/v1/request_response.rb +1 -1
- data/lib/temporalio/api/payload_visitor.rb +64 -0
- data/lib/temporalio/api/taskqueue/v1/message.rb +1 -1
- data/lib/temporalio/api/worker/v1/message.rb +1 -1
- data/lib/temporalio/api/workflow/v1/message.rb +2 -1
- data/lib/temporalio/api/workflowservice/v1/request_response.rb +23 -1
- data/lib/temporalio/api/workflowservice/v1/service.rb +1 -1
- data/lib/temporalio/client/connection/workflow_service.rb +150 -0
- data/lib/temporalio/client/connection.rb +17 -3
- data/lib/temporalio/client/plugin.rb +42 -0
- data/lib/temporalio/client.rb +82 -13
- data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +4 -1
- data/lib/temporalio/converters/payload_converter/json_protobuf.rb +4 -1
- data/lib/temporalio/env_config.rb +2 -11
- data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.so +0 -0
- data/lib/temporalio/internal/bridge/3.4/temporalio_bridge.so +0 -0
- data/lib/temporalio/internal/bridge/{3.2 → 4.0}/temporalio_bridge.so +0 -0
- data/lib/temporalio/internal/bridge/api/core_interface.rb +3 -1
- data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +2 -1
- data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +1 -1
- data/lib/temporalio/internal/bridge/worker.rb +1 -0
- data/lib/temporalio/internal/worker/workflow_instance/context.rb +4 -0
- data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +1 -1
- data/lib/temporalio/internal/worker/workflow_instance/handler_hash.rb +1 -1
- data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +8 -4
- data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +32 -11
- data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +2 -1
- data/lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb +2 -1
- data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +10 -1
- data/lib/temporalio/internal/worker/workflow_instance.rb +15 -11
- data/lib/temporalio/simple_plugin.rb +192 -0
- data/lib/temporalio/version.rb +1 -1
- data/lib/temporalio/worker/plugin.rb +88 -0
- data/lib/temporalio/worker/workflow_replayer.rb +28 -5
- data/lib/temporalio/worker.rb +116 -42
- data/lib/temporalio/workflow/definition.rb +3 -1
- data/lib/temporalio/workflow.rb +8 -1
- data/temporalio.gemspec +1 -1
- metadata +9 -5
|
@@ -13,7 +13,7 @@ module Temporalio
|
|
|
13
13
|
illegal_calls.to_h do |key, val|
|
|
14
14
|
raise TypeError, 'Invalid illegal call map, top-level key must be a String' unless key.is_a?(String)
|
|
15
15
|
|
|
16
|
-
# @type var fixed_val: :all | Worker::IllegalWorkflowCallValidator | Hash[Symbol,
|
|
16
|
+
# @type var fixed_val: :all | Temporalio::Worker::IllegalWorkflowCallValidator | Hash[Symbol, (true | Temporalio::Worker::IllegalWorkflowCallValidator)]
|
|
17
17
|
fixed_val = case val
|
|
18
18
|
when Temporalio::Worker::IllegalWorkflowCallValidator
|
|
19
19
|
if val.method_name
|
|
@@ -86,21 +86,25 @@ module Temporalio
|
|
|
86
86
|
when Temporalio::Worker::IllegalWorkflowCallValidator
|
|
87
87
|
disable_temporarily do
|
|
88
88
|
vals.block.call(Temporalio::Worker::IllegalWorkflowCallValidator::CallInfo.new(
|
|
89
|
-
class_name:,
|
|
89
|
+
class_name:,
|
|
90
|
+
method_name: tp.callee_id || :__unknown__,
|
|
91
|
+
trace_point: tp
|
|
90
92
|
))
|
|
91
93
|
nil
|
|
92
94
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
93
95
|
", reason: #{e}"
|
|
94
96
|
end
|
|
95
97
|
else
|
|
96
|
-
per_method = vals&.[](tp.callee_id)
|
|
98
|
+
per_method = vals&.[](tp.callee_id || :__unknown__)
|
|
97
99
|
case per_method
|
|
98
100
|
when true
|
|
99
101
|
''
|
|
100
102
|
when Temporalio::Worker::IllegalWorkflowCallValidator
|
|
101
103
|
disable_temporarily do
|
|
102
104
|
per_method.block.call(Temporalio::Worker::IllegalWorkflowCallValidator::CallInfo.new(
|
|
103
|
-
class_name:,
|
|
105
|
+
class_name:,
|
|
106
|
+
method_name: tp.callee_id || :__unknown__,
|
|
107
|
+
trace_point: tp
|
|
104
108
|
))
|
|
105
109
|
nil
|
|
106
110
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
@@ -43,7 +43,12 @@ module Temporalio
|
|
|
43
43
|
@instance.pending_external_cancels[seq] = Fiber.current
|
|
44
44
|
|
|
45
45
|
# Wait
|
|
46
|
-
resolution =
|
|
46
|
+
resolution = begin
|
|
47
|
+
Fiber.yield
|
|
48
|
+
ensure
|
|
49
|
+
# Remove pending
|
|
50
|
+
@instance.pending_external_cancels.delete(seq)
|
|
51
|
+
end
|
|
47
52
|
|
|
48
53
|
# Raise if resolution has failure
|
|
49
54
|
return unless resolution.failure
|
|
@@ -169,10 +174,13 @@ module Temporalio
|
|
|
169
174
|
end
|
|
170
175
|
|
|
171
176
|
# Wait
|
|
172
|
-
resolution =
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
177
|
+
resolution = begin
|
|
178
|
+
Fiber.yield
|
|
179
|
+
ensure
|
|
180
|
+
# Remove pending and cancel callback
|
|
181
|
+
@instance.pending_activities.delete(seq)
|
|
182
|
+
cancellation.remove_cancel_callback(cancel_callback_key)
|
|
183
|
+
end
|
|
176
184
|
|
|
177
185
|
case resolution.status
|
|
178
186
|
when :completed
|
|
@@ -254,10 +262,13 @@ module Temporalio
|
|
|
254
262
|
end
|
|
255
263
|
|
|
256
264
|
# Wait
|
|
257
|
-
resolution =
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
265
|
+
resolution = begin
|
|
266
|
+
Fiber.yield
|
|
267
|
+
ensure
|
|
268
|
+
# Remove pending and cancel callback
|
|
269
|
+
@instance.pending_external_signals.delete(seq)
|
|
270
|
+
cancellation.remove_cancel_callback(cancel_callback_key)
|
|
271
|
+
end
|
|
261
272
|
|
|
262
273
|
# Raise if resolution has failure
|
|
263
274
|
return unless resolution.failure
|
|
@@ -317,7 +328,12 @@ module Temporalio
|
|
|
317
328
|
end
|
|
318
329
|
|
|
319
330
|
# Wait
|
|
320
|
-
|
|
331
|
+
begin
|
|
332
|
+
Fiber.yield
|
|
333
|
+
ensure
|
|
334
|
+
# Remove pending
|
|
335
|
+
@instance.pending_timers.delete(seq)
|
|
336
|
+
end
|
|
321
337
|
|
|
322
338
|
# Remove cancellation callback (only needed on success)
|
|
323
339
|
input.cancellation.remove_cancel_callback(cancel_callback_key)
|
|
@@ -374,7 +390,12 @@ module Temporalio
|
|
|
374
390
|
end
|
|
375
391
|
|
|
376
392
|
# Wait for start
|
|
377
|
-
resolution =
|
|
393
|
+
resolution = begin
|
|
394
|
+
Fiber.yield
|
|
395
|
+
ensure
|
|
396
|
+
# Remove pending
|
|
397
|
+
@instance.pending_child_workflow_starts.delete(seq)
|
|
398
|
+
end
|
|
378
399
|
|
|
379
400
|
case resolution.status
|
|
380
401
|
when :succeeded
|
|
@@ -23,7 +23,8 @@ module Temporalio
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def add(...)
|
|
26
|
-
if !@replay_safety_disabled && Temporalio::Workflow.in_workflow? &&
|
|
26
|
+
if !@replay_safety_disabled && Temporalio::Workflow.in_workflow? &&
|
|
27
|
+
Temporalio::Workflow::Unsafe.replaying_history_events?
|
|
27
28
|
return true
|
|
28
29
|
end
|
|
29
30
|
|
|
@@ -9,7 +9,8 @@ module Temporalio
|
|
|
9
9
|
# Wrapper for a metric that does not log on replay.
|
|
10
10
|
class ReplaySafeMetric < SimpleDelegator
|
|
11
11
|
def record(value, additional_attributes: nil)
|
|
12
|
-
return if Temporalio::Workflow.in_workflow? &&
|
|
12
|
+
return if Temporalio::Workflow.in_workflow? &&
|
|
13
|
+
Temporalio::Workflow::Unsafe.replaying_history_events?
|
|
13
14
|
|
|
14
15
|
super
|
|
15
16
|
end
|
|
@@ -78,7 +78,12 @@ module Temporalio
|
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
# This blocks until a resume is called on this fiber
|
|
81
|
-
result =
|
|
81
|
+
result = begin
|
|
82
|
+
Fiber.yield
|
|
83
|
+
ensure
|
|
84
|
+
# Remove pending
|
|
85
|
+
@wait_conditions.delete(seq)
|
|
86
|
+
end
|
|
82
87
|
|
|
83
88
|
# Remove cancellation callback (only needed on success)
|
|
84
89
|
cancellation&.remove_cancel_callback(cancel_callback_key) if cancel_callback_key
|
|
@@ -141,6 +146,10 @@ module Temporalio
|
|
|
141
146
|
fiber
|
|
142
147
|
end
|
|
143
148
|
|
|
149
|
+
def fiber_interrupt(fiber, exception)
|
|
150
|
+
fiber.raise(exception) if fiber.alive?
|
|
151
|
+
end
|
|
152
|
+
|
|
144
153
|
def io_wait(io, events, timeout)
|
|
145
154
|
# Do not allow if IO disabled
|
|
146
155
|
unless @instance.io_enabled
|
|
@@ -57,7 +57,8 @@ module Temporalio
|
|
|
57
57
|
:pending_external_signals, :pending_external_cancels, :in_progress_handlers, :payload_converter,
|
|
58
58
|
:failure_converter, :cancellation, :continue_as_new_suggested, :current_deployment_version,
|
|
59
59
|
:current_history_length, :current_history_size, :replaying, :random,
|
|
60
|
-
:signal_handlers, :query_handlers, :update_handlers, :context_frozen, :assert_valid_local_activity
|
|
60
|
+
:signal_handlers, :query_handlers, :update_handlers, :context_frozen, :assert_valid_local_activity,
|
|
61
|
+
:in_query_or_validator
|
|
61
62
|
attr_accessor :io_enabled, :current_details
|
|
62
63
|
|
|
63
64
|
def initialize(details)
|
|
@@ -91,6 +92,7 @@ module Temporalio
|
|
|
91
92
|
@current_history_length = 0
|
|
92
93
|
@current_history_size = 0
|
|
93
94
|
@replaying = false
|
|
95
|
+
@in_query_or_validator = false
|
|
94
96
|
@workflow_failure_exception_types = details.workflow_failure_exception_types
|
|
95
97
|
@signal_handlers = HandlerHash.new(
|
|
96
98
|
details.definition.signals,
|
|
@@ -182,7 +184,7 @@ module Temporalio
|
|
|
182
184
|
# Apply jobs and run event loop
|
|
183
185
|
begin
|
|
184
186
|
# Create instance if it doesn't already exist
|
|
185
|
-
@instance ||= with_context_frozen { create_instance }
|
|
187
|
+
@instance ||= with_context_frozen(in_query_or_validator: false) { create_instance }
|
|
186
188
|
|
|
187
189
|
# Apply jobs
|
|
188
190
|
activation.jobs.each { |job| apply(job) }
|
|
@@ -345,7 +347,7 @@ module Temporalio
|
|
|
345
347
|
when :initialize_workflow
|
|
346
348
|
# Ignore
|
|
347
349
|
when :fire_timer
|
|
348
|
-
pending_timers
|
|
350
|
+
pending_timers[job.fire_timer.seq]&.resume
|
|
349
351
|
when :update_random_seed
|
|
350
352
|
@random = illegal_call_tracing_disabled { Random.new(job.update_random_seed.randomness_seed) }
|
|
351
353
|
when :query_workflow
|
|
@@ -356,23 +358,23 @@ module Temporalio
|
|
|
356
358
|
when :signal_workflow
|
|
357
359
|
apply_signal(job.signal_workflow)
|
|
358
360
|
when :resolve_activity
|
|
359
|
-
pending_activities
|
|
361
|
+
pending_activities[job.resolve_activity.seq]&.resume(job.resolve_activity.result)
|
|
360
362
|
when :notify_has_patch
|
|
361
363
|
@patches_notified << job.notify_has_patch.patch_id
|
|
362
364
|
when :resolve_child_workflow_execution_start
|
|
363
|
-
pending_child_workflow_starts
|
|
365
|
+
pending_child_workflow_starts[job.resolve_child_workflow_execution_start.seq]&.resume(
|
|
364
366
|
job.resolve_child_workflow_execution_start
|
|
365
367
|
)
|
|
366
368
|
when :resolve_child_workflow_execution
|
|
367
|
-
pending_child_workflows
|
|
369
|
+
pending_child_workflows[job.resolve_child_workflow_execution.seq]&._resolve(
|
|
368
370
|
job.resolve_child_workflow_execution.result
|
|
369
371
|
)
|
|
370
372
|
when :resolve_signal_external_workflow
|
|
371
|
-
pending_external_signals
|
|
373
|
+
pending_external_signals[job.resolve_signal_external_workflow.seq]&.resume(
|
|
372
374
|
job.resolve_signal_external_workflow
|
|
373
375
|
)
|
|
374
376
|
when :resolve_request_cancel_external_workflow
|
|
375
|
-
pending_external_cancels
|
|
377
|
+
pending_external_cancels[job.resolve_request_cancel_external_workflow.seq]&.resume(
|
|
376
378
|
job.resolve_request_cancel_external_workflow
|
|
377
379
|
)
|
|
378
380
|
when :do_update
|
|
@@ -439,7 +441,7 @@ module Temporalio
|
|
|
439
441
|
end
|
|
440
442
|
result_hint = defn.result_hint
|
|
441
443
|
|
|
442
|
-
with_context_frozen do
|
|
444
|
+
with_context_frozen(in_query_or_validator: true) do
|
|
443
445
|
@inbound.handle_query(
|
|
444
446
|
Temporalio::Worker::Interceptor::Workflow::HandleQueryInput.new(
|
|
445
447
|
id: job.query_id,
|
|
@@ -502,7 +504,7 @@ module Temporalio
|
|
|
502
504
|
# other SDKs, we are re-converting the args between validate and update to disallow user mutation in
|
|
503
505
|
# validator/interceptor.
|
|
504
506
|
if job.run_validator && defn.validator_to_invoke
|
|
505
|
-
with_context_frozen do
|
|
507
|
+
with_context_frozen(in_query_or_validator: true) do
|
|
506
508
|
@inbound.validate_update(
|
|
507
509
|
Temporalio::Worker::Interceptor::Workflow::HandleUpdateInput.new(
|
|
508
510
|
id: job.id,
|
|
@@ -663,11 +665,13 @@ module Temporalio
|
|
|
663
665
|
@definition_options.failure_exception_types&.any? { |cls| err.is_a?(cls) }
|
|
664
666
|
end
|
|
665
667
|
|
|
666
|
-
def with_context_frozen(&)
|
|
668
|
+
def with_context_frozen(in_query_or_validator:, &)
|
|
667
669
|
@context_frozen = true
|
|
670
|
+
@in_query_or_validator = in_query_or_validator
|
|
668
671
|
yield
|
|
669
672
|
ensure
|
|
670
673
|
@context_frozen = false
|
|
674
|
+
@in_query_or_validator = false
|
|
671
675
|
end
|
|
672
676
|
|
|
673
677
|
def convert_handler_args(payload_array:, defn:)
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'temporalio/client'
|
|
4
|
+
require 'temporalio/worker'
|
|
5
|
+
|
|
6
|
+
module Temporalio
|
|
7
|
+
# Plugin that implements both {Client::Plugin} and {Worker::Plugin} and provides a simplified common set of settings
|
|
8
|
+
# for configuring both.
|
|
9
|
+
#
|
|
10
|
+
# WARNING: Plugins are experimental.
|
|
11
|
+
class SimplePlugin
|
|
12
|
+
include Client::Plugin
|
|
13
|
+
include Worker::Plugin
|
|
14
|
+
|
|
15
|
+
Options = Data.define(
|
|
16
|
+
:name,
|
|
17
|
+
:data_converter,
|
|
18
|
+
:client_interceptors,
|
|
19
|
+
:activities,
|
|
20
|
+
:workflows,
|
|
21
|
+
:worker_interceptors,
|
|
22
|
+
:workflow_failure_exception_types,
|
|
23
|
+
:run_context
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Options as returned from {options} representing the options passed to the constructor.
|
|
27
|
+
class Options; end # rubocop:disable Lint/EmptyClass
|
|
28
|
+
|
|
29
|
+
# @return [Options] Frozen options for this plugin which has the same attributes as {initialize}.
|
|
30
|
+
attr_reader :options
|
|
31
|
+
|
|
32
|
+
# Create a simple plugin.
|
|
33
|
+
#
|
|
34
|
+
# @param name [String] Required string name for this plugin.
|
|
35
|
+
# @param data_converter [Converters::DataConverter, Proc, nil] Data converter to apply to clients and workflow
|
|
36
|
+
# replayers. This can be a proc that accepts the existing data converter and returns a new one.
|
|
37
|
+
# @param client_interceptors [Array<Client::Integerceptor>, Proc, nil] Client interceptors that are appended to the
|
|
38
|
+
# existing client set (which means if they implement worker interceptors they are applied for the workers too). A
|
|
39
|
+
# proc can be provided that accepts the existing array and returns a new one.
|
|
40
|
+
# @param activities [Array<Activity::Definition, Class<Activity::Definition>, Activity::Definition::Info>, Proc,
|
|
41
|
+
# nil] Activities to append to each worker activity set. A proc can be provided that accepts the existing array
|
|
42
|
+
# and returns a new one.
|
|
43
|
+
# @param workflows [Array<Class<Workflow::Definition>>, Proc, nil] Workflows to append to each worker workflow set.
|
|
44
|
+
# A proc can be provided that accepts the existing array and returns a new one.
|
|
45
|
+
# @param worker_interceptors [Array<Interceptor::Activity, Interceptor::Workflow>, Proc, nil] Worker interceptors
|
|
46
|
+
# that are appended to the existing worker or workflow replayer set. A proc can be provided that accepts the
|
|
47
|
+
# existing array and returns a new one.
|
|
48
|
+
# @param workflow_failure_exception_types [Array<Class<Exception>>] Workflow failure exception types that are
|
|
49
|
+
# appended to the existing worker or workflow replayer set. A proc can be provided that accepts the existing array
|
|
50
|
+
# and returns a new one.
|
|
51
|
+
# @param run_context [Proc, nil] A proc that intercepts both {run_worker} or {with_workflow_replay_worker}. The proc
|
|
52
|
+
# should accept two positional parameters: options and next_call. The options are either
|
|
53
|
+
# {Worker::Plugin::RunWorkerOptions} or {Worker::Plugin::WithWorkflowReplayWorkerOptions}. The next_call is a proc
|
|
54
|
+
# itself that accepts the options and returns a value. This run_context proc should return the result of the
|
|
55
|
+
# next_call.
|
|
56
|
+
def initialize(
|
|
57
|
+
name:,
|
|
58
|
+
data_converter: nil,
|
|
59
|
+
client_interceptors: nil,
|
|
60
|
+
activities: nil,
|
|
61
|
+
workflows: nil,
|
|
62
|
+
worker_interceptors: nil,
|
|
63
|
+
workflow_failure_exception_types: nil,
|
|
64
|
+
run_context: nil
|
|
65
|
+
)
|
|
66
|
+
@options = Options.new(
|
|
67
|
+
name:,
|
|
68
|
+
data_converter:,
|
|
69
|
+
client_interceptors:,
|
|
70
|
+
activities:,
|
|
71
|
+
workflows:,
|
|
72
|
+
worker_interceptors:,
|
|
73
|
+
workflow_failure_exception_types:,
|
|
74
|
+
run_context:
|
|
75
|
+
).freeze
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Implements {Client::Plugin#name} and {Worker::Plugin#name}.
|
|
79
|
+
def name
|
|
80
|
+
@options.name
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Implements {Client::Plugin#configure_client}.
|
|
84
|
+
def configure_client(options)
|
|
85
|
+
if (data_converter = _single_option(new: @options.data_converter, existing: options.data_converter,
|
|
86
|
+
type: Converters::DataConverter, name: 'data converter'))
|
|
87
|
+
options = options.with(data_converter:)
|
|
88
|
+
end
|
|
89
|
+
if (interceptors = _array_option(new: @options.client_interceptors, existing: options.interceptors,
|
|
90
|
+
name: 'client interceptor'))
|
|
91
|
+
options = options.with(interceptors:)
|
|
92
|
+
end
|
|
93
|
+
options
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Implements {Client::Plugin#connect_client}.
|
|
97
|
+
def connect_client(options, next_call)
|
|
98
|
+
next_call.call(options)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Implements {Worker::Plugin#configure_worker}.
|
|
102
|
+
def configure_worker(options)
|
|
103
|
+
if (activities = _array_option(new: @options.activities, existing: options.activities, name: 'activity'))
|
|
104
|
+
options = options.with(activities:)
|
|
105
|
+
end
|
|
106
|
+
if (workflows = _array_option(new: @options.workflows, existing: options.workflows, name: 'workflow'))
|
|
107
|
+
options = options.with(workflows:)
|
|
108
|
+
end
|
|
109
|
+
if (interceptors = _array_option(new: @options.worker_interceptors, existing: options.interceptors,
|
|
110
|
+
name: 'worker interceptor'))
|
|
111
|
+
options = options.with(interceptors:)
|
|
112
|
+
end
|
|
113
|
+
if (workflow_failure_exception_types = _array_option(new: @options.workflow_failure_exception_types,
|
|
114
|
+
existing: options.workflow_failure_exception_types,
|
|
115
|
+
name: 'workflow failure exception types'))
|
|
116
|
+
options = options.with(workflow_failure_exception_types:)
|
|
117
|
+
end
|
|
118
|
+
options
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Implements {Worker::Plugin#run_worker}.
|
|
122
|
+
def run_worker(options, next_call)
|
|
123
|
+
if @options.run_context
|
|
124
|
+
@options.run_context.call(options, next_call) # steep:ignore NoMethod
|
|
125
|
+
else
|
|
126
|
+
next_call.call(options)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Implements {Worker::Plugin#configure_workflow_replayer}.
|
|
131
|
+
def configure_workflow_replayer(options)
|
|
132
|
+
if (data_converter = _single_option(new: @options.data_converter, existing: options.data_converter,
|
|
133
|
+
type: Converters::DataConverter, name: 'data converter'))
|
|
134
|
+
options = options.with(data_converter:)
|
|
135
|
+
end
|
|
136
|
+
if (workflows = _array_option(new: @options.workflows, existing: options.workflows, name: 'workflow'))
|
|
137
|
+
options = options.with(workflows:)
|
|
138
|
+
end
|
|
139
|
+
if (interceptors = _array_option(new: @options.worker_interceptors, existing: options.interceptors,
|
|
140
|
+
name: 'worker interceptor'))
|
|
141
|
+
options = options.with(interceptors:)
|
|
142
|
+
end
|
|
143
|
+
if (workflow_failure_exception_types = _array_option(new: @options.workflow_failure_exception_types,
|
|
144
|
+
existing: options.workflow_failure_exception_types,
|
|
145
|
+
name: 'workflow failure exception types'))
|
|
146
|
+
options = options.with(workflow_failure_exception_types:)
|
|
147
|
+
end
|
|
148
|
+
options
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Implements {Worker::Plugin#with_workflow_replay_worker}.
|
|
152
|
+
def with_workflow_replay_worker(options, next_call)
|
|
153
|
+
if @options.run_context
|
|
154
|
+
@options.run_context.call(options, next_call) # steep:ignore NoMethod
|
|
155
|
+
else
|
|
156
|
+
next_call.call(options)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# @!visibility private
|
|
161
|
+
def _single_option(new:, existing:, type:, name:)
|
|
162
|
+
case new
|
|
163
|
+
when nil
|
|
164
|
+
nil
|
|
165
|
+
when Proc
|
|
166
|
+
new.call(existing).tap do |val| # steep:ignore NoMethod
|
|
167
|
+
raise "Instance of #{name} required" unless val.is_a?(type)
|
|
168
|
+
end
|
|
169
|
+
when type
|
|
170
|
+
new
|
|
171
|
+
else
|
|
172
|
+
raise "Unrecognized #{name} type #{new.class}"
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# @!visibility private
|
|
177
|
+
def _array_option(new:, existing:, name:)
|
|
178
|
+
case new
|
|
179
|
+
when nil
|
|
180
|
+
nil
|
|
181
|
+
when Proc
|
|
182
|
+
new.call(existing).tap do |conv| # steep:ignore NoMethod
|
|
183
|
+
raise "Array for #{name} required" unless conv.is_a?(Array)
|
|
184
|
+
end
|
|
185
|
+
when Array
|
|
186
|
+
existing + new # steep:ignore NoMethod
|
|
187
|
+
else
|
|
188
|
+
raise "Unrecognized #{name} type #{new.class}"
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
data/lib/temporalio/version.rb
CHANGED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Temporalio
|
|
4
|
+
class Worker
|
|
5
|
+
# Plugin mixin to include for configuring workers and workflow replayers, and intercepting the running of them.
|
|
6
|
+
#
|
|
7
|
+
# This is a low-level implementation that requires abstract methods herein to be implemented. Many implementers may
|
|
8
|
+
# prefer {SimplePlugin} which includes this.
|
|
9
|
+
#
|
|
10
|
+
# WARNING: Plugins are experimental.
|
|
11
|
+
module Plugin
|
|
12
|
+
RunWorkerOptions = Data.define(
|
|
13
|
+
:worker,
|
|
14
|
+
:cancellation,
|
|
15
|
+
:shutdown_signals,
|
|
16
|
+
:raise_in_block_on_shutdown
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
# Options for {run_worker}.
|
|
20
|
+
#
|
|
21
|
+
# The options contain the worker and some other options from {Worker#run}/{Worker.run_all}. Unlike other memebers
|
|
22
|
+
# in this class, mutating the worker member before invoking the next call in the chain has no effect.
|
|
23
|
+
#
|
|
24
|
+
# @note Additional required attributes of this class may be added in the future. Users should never instantiate
|
|
25
|
+
# this class, but instead use `with` on it in {run_worker}.
|
|
26
|
+
class RunWorkerOptions; end # rubocop:disable Lint/EmptyClass
|
|
27
|
+
|
|
28
|
+
WithWorkflowReplayWorkerOptions = Data.define(
|
|
29
|
+
:worker
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Options for {with_workflow_replay_worker}.
|
|
33
|
+
#
|
|
34
|
+
# @note Additional required attributes of this class may be added in the future. Users should never instantiate
|
|
35
|
+
# this class, but instead use `with` on it in {with_workflow_replay_worker}.
|
|
36
|
+
#
|
|
37
|
+
# @!attribute worker
|
|
38
|
+
# @return [WorkflowReplayer::ReplayWorker] Replay worker.
|
|
39
|
+
class WithWorkflowReplayWorkerOptions; end # rubocop:disable Lint/EmptyClass
|
|
40
|
+
|
|
41
|
+
# @abstract
|
|
42
|
+
# @return [String] Name of the plugin.
|
|
43
|
+
def name
|
|
44
|
+
raise NotImplementedError
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Configure a worker.
|
|
48
|
+
#
|
|
49
|
+
# @abstract
|
|
50
|
+
# @param options [Options] Current immutable options set.
|
|
51
|
+
# @return [Options] Options to use, possibly updated from original.
|
|
52
|
+
def configure_worker(options)
|
|
53
|
+
raise NotImplementedError
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Run a worker.
|
|
57
|
+
#
|
|
58
|
+
# @abstract
|
|
59
|
+
# @param options [RunWorkerOptions] Current immutable options set.
|
|
60
|
+
# @param next_call [Proc] Proc for the next plugin in the chain to call. It accepts the options and returns an
|
|
61
|
+
# arbitrary object that should also be returned from this method.
|
|
62
|
+
# @return [Object] Result of next_call.
|
|
63
|
+
def run_worker(options, next_call)
|
|
64
|
+
raise NotImplementedError
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Configure a workflow replayer.
|
|
68
|
+
#
|
|
69
|
+
# @abstract
|
|
70
|
+
# @param options [WorkflowReplayer::Options] Current immutable options set.
|
|
71
|
+
# @return [WorkflowReplayer::Options] Options to use, possibly updated from original.
|
|
72
|
+
def configure_workflow_replayer(options)
|
|
73
|
+
raise NotImplementedError
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Run a replay worker.
|
|
77
|
+
#
|
|
78
|
+
# @abstract
|
|
79
|
+
# @param options [WithWorkflowReplayWorkerOptions] Current immutable options set.
|
|
80
|
+
# @param next_call [Proc] Proc for the next plugin in the chain to call. It accepts the options and returns an
|
|
81
|
+
# arbitrary object that should also be returned from this method.
|
|
82
|
+
# @return [Object] Result of next_call.
|
|
83
|
+
def with_workflow_replay_worker(options, next_call)
|
|
84
|
+
raise NotImplementedError
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -7,6 +7,7 @@ require 'temporalio/internal/bridge/worker'
|
|
|
7
7
|
require 'temporalio/internal/worker/multi_runner'
|
|
8
8
|
require 'temporalio/internal/worker/workflow_worker'
|
|
9
9
|
require 'temporalio/worker/interceptor'
|
|
10
|
+
require 'temporalio/worker/plugin'
|
|
10
11
|
require 'temporalio/worker/poller_behavior'
|
|
11
12
|
require 'temporalio/worker/thread_pool'
|
|
12
13
|
require 'temporalio/worker/tuner'
|
|
@@ -24,6 +25,7 @@ module Temporalio
|
|
|
24
25
|
:task_queue,
|
|
25
26
|
:data_converter,
|
|
26
27
|
:workflow_executor,
|
|
28
|
+
:plugins,
|
|
27
29
|
:interceptors,
|
|
28
30
|
:identity,
|
|
29
31
|
:logger,
|
|
@@ -50,6 +52,8 @@ module Temporalio
|
|
|
50
52
|
# payloads.
|
|
51
53
|
# @param workflow_executor [WorkflowExecutor] Workflow executor that workflow tasks run within. This must be a
|
|
52
54
|
# {WorkflowExecutor::ThreadPool} currently.
|
|
55
|
+
# @param plugins [Array<Plugin>] Plugins to use for configuring replayer and intercepting replay. WARNING: Plugins
|
|
56
|
+
# are experimental.
|
|
53
57
|
# @param interceptors [Array<Interceptor::Workflow>] Workflow interceptors.
|
|
54
58
|
# @param identity [String, nil] Override the identity for this replater.
|
|
55
59
|
# @param logger [Logger] Logger to use. Defaults to stdout with warn level. Callers setting this logger are
|
|
@@ -83,6 +87,7 @@ module Temporalio
|
|
|
83
87
|
task_queue: 'ReplayTaskQueue',
|
|
84
88
|
data_converter: Converters::DataConverter.default,
|
|
85
89
|
workflow_executor: WorkflowExecutor::ThreadPool.default,
|
|
90
|
+
plugins: [],
|
|
86
91
|
interceptors: [],
|
|
87
92
|
identity: nil,
|
|
88
93
|
logger: Logger.new($stdout, level: Logger::WARN),
|
|
@@ -100,6 +105,7 @@ module Temporalio
|
|
|
100
105
|
task_queue:,
|
|
101
106
|
data_converter:,
|
|
102
107
|
workflow_executor:,
|
|
108
|
+
plugins:,
|
|
103
109
|
interceptors:,
|
|
104
110
|
identity:,
|
|
105
111
|
logger:,
|
|
@@ -110,13 +116,18 @@ module Temporalio
|
|
|
110
116
|
debug_mode:,
|
|
111
117
|
runtime:
|
|
112
118
|
).freeze
|
|
119
|
+
# Apply plugins
|
|
120
|
+
Worker._validate_plugins!(plugins)
|
|
121
|
+
@options = plugins.reduce(@options) { |options, plugin| plugin.configure_workflow_replayer(options) }
|
|
122
|
+
|
|
113
123
|
# Preload definitions and other settings
|
|
114
124
|
@workflow_definitions = Internal::Worker::WorkflowWorker.workflow_definitions(
|
|
115
|
-
workflows, should_enforce_versioning_behavior: false
|
|
125
|
+
@options.workflows, should_enforce_versioning_behavior: false
|
|
116
126
|
)
|
|
117
127
|
@nondeterminism_as_workflow_fail, @nondeterminism_as_workflow_fail_for_types =
|
|
118
128
|
Internal::Worker::WorkflowWorker.bridge_workflow_failure_exception_type_options(
|
|
119
|
-
workflow_failure_exception_types
|
|
129
|
+
workflow_failure_exception_types: @options.workflow_failure_exception_types,
|
|
130
|
+
workflow_definitions: @workflow_definitions
|
|
120
131
|
)
|
|
121
132
|
# If there is a block, we'll go ahead and assume it's for with_replay_worker
|
|
122
133
|
with_replay_worker(&) if block_given? # steep:ignore
|
|
@@ -154,7 +165,18 @@ module Temporalio
|
|
|
154
165
|
# @yield Block of code to run with a replay worker.
|
|
155
166
|
# @yieldparam [ReplayWorker] Worker to run replays on. Note, only one workflow can replay at a time.
|
|
156
167
|
# @yieldreturn [Object] Result of the block.
|
|
157
|
-
def with_replay_worker(&)
|
|
168
|
+
def with_replay_worker(&block)
|
|
169
|
+
# Apply plugins
|
|
170
|
+
run_block = proc do |options|
|
|
171
|
+
# @type var options: Plugin::WithWorkflowReplayWorkerOptions
|
|
172
|
+
block.call(options.worker)
|
|
173
|
+
end
|
|
174
|
+
run_block = options.plugins.reverse_each.reduce(run_block) do |next_call, plugin|
|
|
175
|
+
proc do |options|
|
|
176
|
+
plugin.with_workflow_replay_worker(options, next_call) # steep:ignore
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
158
180
|
worker = ReplayWorker.new(
|
|
159
181
|
options:,
|
|
160
182
|
workflow_definitions: @workflow_definitions,
|
|
@@ -162,7 +184,7 @@ module Temporalio
|
|
|
162
184
|
nondeterminism_as_workflow_fail_for_types: @nondeterminism_as_workflow_fail_for_types
|
|
163
185
|
)
|
|
164
186
|
begin
|
|
165
|
-
|
|
187
|
+
run_block.call(Plugin::WithWorkflowReplayWorkerOptions.new(worker:))
|
|
166
188
|
ensure
|
|
167
189
|
worker._shutdown
|
|
168
190
|
end
|
|
@@ -221,7 +243,8 @@ module Temporalio
|
|
|
221
243
|
graceful_shutdown_period: 0.0,
|
|
222
244
|
nondeterminism_as_workflow_fail:,
|
|
223
245
|
nondeterminism_as_workflow_fail_for_types:,
|
|
224
|
-
deployment_options: Worker.default_deployment_options._to_bridge_options
|
|
246
|
+
deployment_options: Worker.default_deployment_options._to_bridge_options,
|
|
247
|
+
plugins: options.plugins.map(&:name).uniq.sort
|
|
225
248
|
)
|
|
226
249
|
)
|
|
227
250
|
|