temporalio 0.2.0-x86_64-darwin → 0.3.0-x86_64-darwin
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/.yardopts +2 -0
- data/Gemfile +3 -3
- data/Rakefile +10 -296
- data/lib/temporalio/activity/complete_async_error.rb +1 -1
- data/lib/temporalio/activity/context.rb +5 -2
- data/lib/temporalio/activity/definition.rb +163 -65
- data/lib/temporalio/activity/info.rb +22 -21
- data/lib/temporalio/activity.rb +2 -59
- data/lib/temporalio/api/activity/v1/message.rb +25 -0
- data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
- data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +34 -1
- data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +1 -1
- data/lib/temporalio/api/cloud/identity/v1/message.rb +6 -1
- data/lib/temporalio/api/cloud/namespace/v1/message.rb +8 -1
- data/lib/temporalio/api/cloud/nexus/v1/message.rb +31 -0
- data/lib/temporalio/api/cloud/operation/v1/message.rb +2 -1
- data/lib/temporalio/api/cloud/region/v1/message.rb +2 -1
- data/lib/temporalio/api/cloud/resource/v1/message.rb +23 -0
- data/lib/temporalio/api/cloud/sink/v1/message.rb +24 -0
- data/lib/temporalio/api/cloud/usage/v1/message.rb +31 -0
- data/lib/temporalio/api/common/v1/message.rb +7 -1
- 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/reset.rb +1 -1
- data/lib/temporalio/api/history/v1/message.rb +1 -1
- data/lib/temporalio/api/nexus/v1/message.rb +2 -2
- data/lib/temporalio/api/operatorservice/v1/service.rb +1 -1
- data/lib/temporalio/api/payload_visitor.rb +1513 -0
- data/lib/temporalio/api/schedule/v1/message.rb +2 -1
- data/lib/temporalio/api/testservice/v1/request_response.rb +31 -0
- data/lib/temporalio/api/testservice/v1/service.rb +23 -0
- data/lib/temporalio/api/workflow/v1/message.rb +1 -1
- data/lib/temporalio/api/workflowservice/v1/request_response.rb +17 -2
- data/lib/temporalio/api/workflowservice/v1/service.rb +1 -1
- data/lib/temporalio/api.rb +1 -0
- data/lib/temporalio/cancellation.rb +34 -14
- data/lib/temporalio/client/async_activity_handle.rb +12 -37
- data/lib/temporalio/client/connection/cloud_service.rb +309 -231
- data/lib/temporalio/client/connection/operator_service.rb +36 -84
- data/lib/temporalio/client/connection/service.rb +6 -5
- data/lib/temporalio/client/connection/test_service.rb +111 -0
- data/lib/temporalio/client/connection/workflow_service.rb +264 -441
- data/lib/temporalio/client/connection.rb +90 -44
- data/lib/temporalio/client/interceptor.rb +160 -60
- data/lib/temporalio/client/schedule.rb +967 -0
- data/lib/temporalio/client/schedule_handle.rb +126 -0
- data/lib/temporalio/client/workflow_execution.rb +7 -10
- data/lib/temporalio/client/workflow_handle.rb +38 -95
- data/lib/temporalio/client/workflow_update_handle.rb +3 -5
- data/lib/temporalio/client.rb +122 -42
- data/lib/temporalio/common_enums.rb +17 -0
- data/lib/temporalio/converters/data_converter.rb +4 -7
- data/lib/temporalio/converters/failure_converter.rb +5 -3
- data/lib/temporalio/converters/payload_converter/composite.rb +4 -0
- data/lib/temporalio/converters/payload_converter.rb +6 -8
- data/lib/temporalio/converters/raw_value.rb +20 -0
- data/lib/temporalio/error/failure.rb +1 -1
- data/lib/temporalio/error.rb +10 -2
- data/lib/temporalio/internal/bridge/3.2/temporalio_bridge.bundle +0 -0
- data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.bundle +0 -0
- data/lib/temporalio/internal/bridge/{3.1 → 3.4}/temporalio_bridge.bundle +0 -0
- data/lib/temporalio/internal/bridge/api/core_interface.rb +5 -1
- data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +33 -0
- data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +5 -1
- data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +4 -1
- data/lib/temporalio/internal/bridge/client.rb +11 -6
- data/lib/temporalio/internal/bridge/testing.rb +20 -0
- data/lib/temporalio/internal/bridge/worker.rb +2 -0
- data/lib/temporalio/internal/bridge.rb +1 -1
- data/lib/temporalio/internal/client/implementation.rb +245 -70
- data/lib/temporalio/internal/metric.rb +122 -0
- data/lib/temporalio/internal/proto_utils.rb +86 -7
- data/lib/temporalio/internal/worker/activity_worker.rb +52 -24
- data/lib/temporalio/internal/worker/multi_runner.rb +51 -7
- data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
- data/lib/temporalio/internal/worker/workflow_instance/context.rb +329 -0
- data/lib/temporalio/internal/worker/workflow_instance/details.rb +44 -0
- data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +32 -0
- data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +22 -0
- data/lib/temporalio/internal/worker/workflow_instance/handler_execution.rb +25 -0
- data/lib/temporalio/internal/worker/workflow_instance/handler_hash.rb +41 -0
- data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +97 -0
- data/lib/temporalio/internal/worker/workflow_instance/inbound_implementation.rb +62 -0
- data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +415 -0
- data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +37 -0
- data/lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb +40 -0
- data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +163 -0
- data/lib/temporalio/internal/worker/workflow_instance.rb +730 -0
- data/lib/temporalio/internal/worker/workflow_worker.rb +196 -0
- data/lib/temporalio/metric.rb +109 -0
- data/lib/temporalio/retry_policy.rb +37 -14
- data/lib/temporalio/runtime.rb +118 -75
- data/lib/temporalio/search_attributes.rb +80 -37
- data/lib/temporalio/testing/activity_environment.rb +2 -2
- data/lib/temporalio/testing/workflow_environment.rb +251 -5
- data/lib/temporalio/version.rb +1 -1
- data/lib/temporalio/worker/activity_executor/thread_pool.rb +9 -217
- data/lib/temporalio/worker/activity_executor.rb +3 -3
- data/lib/temporalio/worker/interceptor.rb +340 -66
- data/lib/temporalio/worker/thread_pool.rb +237 -0
- data/lib/temporalio/worker/workflow_executor/thread_pool.rb +230 -0
- data/lib/temporalio/worker/workflow_executor.rb +26 -0
- data/lib/temporalio/worker.rb +201 -30
- data/lib/temporalio/workflow/activity_cancellation_type.rb +20 -0
- data/lib/temporalio/workflow/child_workflow_cancellation_type.rb +21 -0
- data/lib/temporalio/workflow/child_workflow_handle.rb +43 -0
- data/lib/temporalio/workflow/definition.rb +566 -0
- data/lib/temporalio/workflow/external_workflow_handle.rb +41 -0
- data/lib/temporalio/workflow/future.rb +151 -0
- data/lib/temporalio/workflow/handler_unfinished_policy.rb +13 -0
- data/lib/temporalio/workflow/info.rb +82 -0
- data/lib/temporalio/workflow/parent_close_policy.rb +19 -0
- data/lib/temporalio/workflow/update_info.rb +20 -0
- data/lib/temporalio/workflow.rb +523 -0
- data/lib/temporalio.rb +4 -0
- data/temporalio.gemspec +2 -2
- metadata +52 -6
@@ -0,0 +1,329 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'temporalio/cancellation'
|
4
|
+
require 'temporalio/error'
|
5
|
+
require 'temporalio/internal/bridge/api'
|
6
|
+
require 'temporalio/internal/proto_utils'
|
7
|
+
require 'temporalio/internal/worker/workflow_instance'
|
8
|
+
require 'temporalio/internal/worker/workflow_instance/external_workflow_handle'
|
9
|
+
require 'temporalio/worker/interceptor'
|
10
|
+
require 'temporalio/workflow'
|
11
|
+
|
12
|
+
module Temporalio
|
13
|
+
module Internal
|
14
|
+
module Worker
|
15
|
+
class WorkflowInstance
|
16
|
+
# Context for all workflow calls. All calls in the {Workflow} class should call a method on this class and then
|
17
|
+
# this class can delegate the call as needed to other parts of the workflow instance system.
|
18
|
+
class Context
|
19
|
+
def initialize(instance)
|
20
|
+
@instance = instance
|
21
|
+
end
|
22
|
+
|
23
|
+
def all_handlers_finished?
|
24
|
+
@instance.in_progress_handlers.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
def cancellation
|
28
|
+
@instance.cancellation
|
29
|
+
end
|
30
|
+
|
31
|
+
def continue_as_new_suggested
|
32
|
+
@instance.continue_as_new_suggested
|
33
|
+
end
|
34
|
+
|
35
|
+
def current_history_length
|
36
|
+
@instance.current_history_length
|
37
|
+
end
|
38
|
+
|
39
|
+
def current_history_size
|
40
|
+
@instance.current_history_size
|
41
|
+
end
|
42
|
+
|
43
|
+
def current_update_info
|
44
|
+
Fiber[:__temporal_update_info]
|
45
|
+
end
|
46
|
+
|
47
|
+
def deprecate_patch(patch_id)
|
48
|
+
@instance.patch(patch_id:, deprecated: true)
|
49
|
+
end
|
50
|
+
|
51
|
+
def execute_activity(
|
52
|
+
activity,
|
53
|
+
*args,
|
54
|
+
task_queue:,
|
55
|
+
schedule_to_close_timeout:,
|
56
|
+
schedule_to_start_timeout:,
|
57
|
+
start_to_close_timeout:,
|
58
|
+
heartbeat_timeout:,
|
59
|
+
retry_policy:,
|
60
|
+
cancellation:,
|
61
|
+
cancellation_type:,
|
62
|
+
activity_id:,
|
63
|
+
disable_eager_execution:
|
64
|
+
)
|
65
|
+
@outbound.execute_activity(
|
66
|
+
Temporalio::Worker::Interceptor::Workflow::ExecuteActivityInput.new(
|
67
|
+
activity:,
|
68
|
+
args:,
|
69
|
+
task_queue: task_queue || info.task_queue,
|
70
|
+
schedule_to_close_timeout:,
|
71
|
+
schedule_to_start_timeout:,
|
72
|
+
start_to_close_timeout:,
|
73
|
+
heartbeat_timeout:,
|
74
|
+
retry_policy:,
|
75
|
+
cancellation:,
|
76
|
+
cancellation_type:,
|
77
|
+
activity_id:,
|
78
|
+
disable_eager_execution: disable_eager_execution || @instance.disable_eager_activity_execution,
|
79
|
+
headers: {}
|
80
|
+
)
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
def execute_local_activity(
|
85
|
+
activity,
|
86
|
+
*args,
|
87
|
+
schedule_to_close_timeout:,
|
88
|
+
schedule_to_start_timeout:,
|
89
|
+
start_to_close_timeout:,
|
90
|
+
retry_policy:,
|
91
|
+
local_retry_threshold:,
|
92
|
+
cancellation:,
|
93
|
+
cancellation_type:,
|
94
|
+
activity_id:
|
95
|
+
)
|
96
|
+
@outbound.execute_local_activity(
|
97
|
+
Temporalio::Worker::Interceptor::Workflow::ExecuteLocalActivityInput.new(
|
98
|
+
activity:,
|
99
|
+
args:,
|
100
|
+
schedule_to_close_timeout:,
|
101
|
+
schedule_to_start_timeout:,
|
102
|
+
start_to_close_timeout:,
|
103
|
+
retry_policy:,
|
104
|
+
local_retry_threshold:,
|
105
|
+
cancellation:,
|
106
|
+
cancellation_type:,
|
107
|
+
activity_id:,
|
108
|
+
headers: {}
|
109
|
+
)
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
113
|
+
def external_workflow_handle(workflow_id, run_id: nil)
|
114
|
+
ExternalWorkflowHandle.new(id: workflow_id, run_id:, instance: @instance)
|
115
|
+
end
|
116
|
+
|
117
|
+
def illegal_call_tracing_disabled(&)
|
118
|
+
@instance.illegal_call_tracing_disabled(&)
|
119
|
+
end
|
120
|
+
|
121
|
+
def info
|
122
|
+
@instance.info
|
123
|
+
end
|
124
|
+
|
125
|
+
def initialize_continue_as_new_error(error)
|
126
|
+
@outbound.initialize_continue_as_new_error(
|
127
|
+
Temporalio::Worker::Interceptor::Workflow::InitializeContinueAsNewErrorInput.new(error:)
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
def logger
|
132
|
+
@instance.logger
|
133
|
+
end
|
134
|
+
|
135
|
+
def memo
|
136
|
+
@instance.memo
|
137
|
+
end
|
138
|
+
|
139
|
+
def metric_meter
|
140
|
+
@instance.metric_meter
|
141
|
+
end
|
142
|
+
|
143
|
+
def now
|
144
|
+
@instance.now
|
145
|
+
end
|
146
|
+
|
147
|
+
def patched(patch_id)
|
148
|
+
@instance.patch(patch_id:, deprecated: false)
|
149
|
+
end
|
150
|
+
|
151
|
+
def payload_converter
|
152
|
+
@instance.payload_converter
|
153
|
+
end
|
154
|
+
|
155
|
+
def query_handlers
|
156
|
+
@instance.query_handlers
|
157
|
+
end
|
158
|
+
|
159
|
+
def random
|
160
|
+
@instance.random
|
161
|
+
end
|
162
|
+
|
163
|
+
def replaying?
|
164
|
+
@instance.replaying
|
165
|
+
end
|
166
|
+
|
167
|
+
def search_attributes
|
168
|
+
@instance.search_attributes
|
169
|
+
end
|
170
|
+
|
171
|
+
def signal_handlers
|
172
|
+
@instance.signal_handlers
|
173
|
+
end
|
174
|
+
|
175
|
+
def sleep(duration, summary:, cancellation:)
|
176
|
+
@outbound.sleep(
|
177
|
+
Temporalio::Worker::Interceptor::Workflow::SleepInput.new(
|
178
|
+
duration:,
|
179
|
+
summary:,
|
180
|
+
cancellation:
|
181
|
+
)
|
182
|
+
)
|
183
|
+
end
|
184
|
+
|
185
|
+
def start_child_workflow(
|
186
|
+
workflow,
|
187
|
+
*args,
|
188
|
+
id:,
|
189
|
+
task_queue:,
|
190
|
+
cancellation:,
|
191
|
+
cancellation_type:,
|
192
|
+
parent_close_policy:,
|
193
|
+
execution_timeout:,
|
194
|
+
run_timeout:,
|
195
|
+
task_timeout:,
|
196
|
+
id_reuse_policy:,
|
197
|
+
retry_policy:,
|
198
|
+
cron_schedule:,
|
199
|
+
memo:,
|
200
|
+
search_attributes:
|
201
|
+
)
|
202
|
+
@outbound.start_child_workflow(
|
203
|
+
Temporalio::Worker::Interceptor::Workflow::StartChildWorkflowInput.new(
|
204
|
+
workflow:,
|
205
|
+
args:,
|
206
|
+
id:,
|
207
|
+
task_queue:,
|
208
|
+
cancellation:,
|
209
|
+
cancellation_type:,
|
210
|
+
parent_close_policy:,
|
211
|
+
execution_timeout:,
|
212
|
+
run_timeout:,
|
213
|
+
task_timeout:,
|
214
|
+
id_reuse_policy:,
|
215
|
+
retry_policy:,
|
216
|
+
cron_schedule:,
|
217
|
+
memo:,
|
218
|
+
search_attributes:,
|
219
|
+
headers: {}
|
220
|
+
)
|
221
|
+
)
|
222
|
+
end
|
223
|
+
|
224
|
+
def timeout(duration, exception_class, *exception_args, summary:, &)
|
225
|
+
raise 'Block required for timeout' unless block_given?
|
226
|
+
|
227
|
+
# Run timer in background and block in foreground. This gives better stack traces than a future any-of race.
|
228
|
+
# We make a detached cancellation because we don't want to link to workflow cancellation.
|
229
|
+
sleep_cancel, sleep_cancel_proc = Cancellation.new
|
230
|
+
fiber = Fiber.current
|
231
|
+
Workflow::Future.new do
|
232
|
+
Workflow.sleep(duration, summary:, cancellation: sleep_cancel)
|
233
|
+
fiber.raise(exception_class, *exception_args) if fiber.alive? # steep:ignore
|
234
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
235
|
+
# Re-raise in fiber
|
236
|
+
fiber.raise(e) if fiber.alive?
|
237
|
+
end
|
238
|
+
|
239
|
+
begin
|
240
|
+
yield
|
241
|
+
ensure
|
242
|
+
sleep_cancel_proc.call
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def update_handlers
|
247
|
+
@instance.update_handlers
|
248
|
+
end
|
249
|
+
|
250
|
+
def upsert_memo(hash)
|
251
|
+
# Convert to memo, apply updates, then add the command (so command adding is post validation)
|
252
|
+
upserted_memo = ProtoUtils.memo_to_proto(hash, payload_converter)
|
253
|
+
memo._update do |new_hash|
|
254
|
+
hash.each do |key, val|
|
255
|
+
# Nil means delete
|
256
|
+
if val.nil?
|
257
|
+
new_hash.delete(key.to_s)
|
258
|
+
else
|
259
|
+
new_hash[key.to_s] = val
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
@instance.add_command(
|
264
|
+
Bridge::Api::WorkflowCommands::WorkflowCommand.new(
|
265
|
+
modify_workflow_properties: Bridge::Api::WorkflowCommands::ModifyWorkflowProperties.new(
|
266
|
+
upserted_memo:
|
267
|
+
)
|
268
|
+
)
|
269
|
+
)
|
270
|
+
end
|
271
|
+
|
272
|
+
def upsert_search_attributes(*updates)
|
273
|
+
# Apply updates then add the command (so command adding is post validation)
|
274
|
+
search_attributes._disable_mutations = false
|
275
|
+
search_attributes.update!(*updates)
|
276
|
+
@instance.add_command(
|
277
|
+
Bridge::Api::WorkflowCommands::WorkflowCommand.new(
|
278
|
+
upsert_workflow_search_attributes: Bridge::Api::WorkflowCommands::UpsertWorkflowSearchAttributes.new(
|
279
|
+
search_attributes: updates.to_h(&:_to_proto_pair)
|
280
|
+
)
|
281
|
+
)
|
282
|
+
)
|
283
|
+
ensure
|
284
|
+
search_attributes._disable_mutations = true
|
285
|
+
end
|
286
|
+
|
287
|
+
def wait_condition(cancellation:, &)
|
288
|
+
@instance.scheduler.wait_condition(cancellation:, &)
|
289
|
+
end
|
290
|
+
|
291
|
+
def _cancel_external_workflow(id:, run_id:)
|
292
|
+
@outbound.cancel_external_workflow(
|
293
|
+
Temporalio::Worker::Interceptor::Workflow::CancelExternalWorkflowInput.new(id:, run_id:)
|
294
|
+
)
|
295
|
+
end
|
296
|
+
|
297
|
+
def _outbound=(outbound)
|
298
|
+
@outbound = outbound
|
299
|
+
end
|
300
|
+
|
301
|
+
def _signal_child_workflow(id:, signal:, args:, cancellation:)
|
302
|
+
@outbound.signal_child_workflow(
|
303
|
+
Temporalio::Worker::Interceptor::Workflow::SignalChildWorkflowInput.new(
|
304
|
+
id:,
|
305
|
+
signal:,
|
306
|
+
args:,
|
307
|
+
cancellation:,
|
308
|
+
headers: {}
|
309
|
+
)
|
310
|
+
)
|
311
|
+
end
|
312
|
+
|
313
|
+
def _signal_external_workflow(id:, run_id:, signal:, args:, cancellation:)
|
314
|
+
@outbound.signal_external_workflow(
|
315
|
+
Temporalio::Worker::Interceptor::Workflow::SignalExternalWorkflowInput.new(
|
316
|
+
id:,
|
317
|
+
run_id:,
|
318
|
+
signal:,
|
319
|
+
args:,
|
320
|
+
cancellation:,
|
321
|
+
headers: {}
|
322
|
+
)
|
323
|
+
)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Temporalio
|
4
|
+
module Internal
|
5
|
+
module Worker
|
6
|
+
class WorkflowInstance
|
7
|
+
# Details needed to instantiate a {WorkflowInstance}.
|
8
|
+
class Details
|
9
|
+
attr_reader :namespace, :task_queue, :definition, :initial_activation, :logger, :metric_meter,
|
10
|
+
:payload_converter, :failure_converter, :interceptors, :disable_eager_activity_execution,
|
11
|
+
:illegal_calls, :workflow_failure_exception_types
|
12
|
+
|
13
|
+
def initialize(
|
14
|
+
namespace:,
|
15
|
+
task_queue:,
|
16
|
+
definition:,
|
17
|
+
initial_activation:,
|
18
|
+
logger:,
|
19
|
+
metric_meter:,
|
20
|
+
payload_converter:,
|
21
|
+
failure_converter:,
|
22
|
+
interceptors:,
|
23
|
+
disable_eager_activity_execution:,
|
24
|
+
illegal_calls:,
|
25
|
+
workflow_failure_exception_types:
|
26
|
+
)
|
27
|
+
@namespace = namespace
|
28
|
+
@task_queue = task_queue
|
29
|
+
@definition = definition
|
30
|
+
@initial_activation = initial_activation
|
31
|
+
@logger = logger
|
32
|
+
@metric_meter = metric_meter
|
33
|
+
@payload_converter = payload_converter
|
34
|
+
@failure_converter = failure_converter
|
35
|
+
@interceptors = interceptors
|
36
|
+
@disable_eager_activity_execution = disable_eager_activity_execution
|
37
|
+
@illegal_calls = illegal_calls
|
38
|
+
@workflow_failure_exception_types = workflow_failure_exception_types
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'temporalio/cancellation'
|
4
|
+
require 'temporalio/workflow'
|
5
|
+
require 'temporalio/workflow/external_workflow_handle'
|
6
|
+
|
7
|
+
module Temporalio
|
8
|
+
module Internal
|
9
|
+
module Worker
|
10
|
+
class WorkflowInstance
|
11
|
+
# Implementation of the external workflow handle.
|
12
|
+
class ExternalWorkflowHandle < Workflow::ExternalWorkflowHandle
|
13
|
+
attr_reader :id, :run_id
|
14
|
+
|
15
|
+
def initialize(id:, run_id:, instance:) # rubocop:disable Lint/MissingSuper
|
16
|
+
@id = id
|
17
|
+
@run_id = run_id
|
18
|
+
@instance = instance
|
19
|
+
end
|
20
|
+
|
21
|
+
def signal(signal, *args, cancellation: Workflow.cancellation)
|
22
|
+
@instance.context._signal_external_workflow(id:, run_id:, signal:, args:, cancellation:)
|
23
|
+
end
|
24
|
+
|
25
|
+
def cancel
|
26
|
+
@instance.context._cancel_external_workflow(id:, run_id:)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Temporalio
|
4
|
+
module Internal
|
5
|
+
module Worker
|
6
|
+
class WorkflowInstance
|
7
|
+
# Delegator to a hash that does not allow external mutations. Used for memo.
|
8
|
+
class ExternallyImmutableHash < SimpleDelegator
|
9
|
+
def initialize(initial_hash)
|
10
|
+
super(initial_hash.freeze)
|
11
|
+
end
|
12
|
+
|
13
|
+
def _update(&)
|
14
|
+
new_hash = __getobj__.dup
|
15
|
+
yield new_hash
|
16
|
+
__setobj__(new_hash.freeze)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Temporalio
|
4
|
+
module Internal
|
5
|
+
module Worker
|
6
|
+
class WorkflowInstance
|
7
|
+
# Representation of a currently-executing handler. Used to track whether any handlers are still running and warn
|
8
|
+
# on workflow complete as needed.
|
9
|
+
class HandlerExecution
|
10
|
+
attr_reader :name, :update_id, :unfinished_policy
|
11
|
+
|
12
|
+
def initialize(
|
13
|
+
name:,
|
14
|
+
update_id:,
|
15
|
+
unfinished_policy:
|
16
|
+
)
|
17
|
+
@name = name
|
18
|
+
@update_id = update_id
|
19
|
+
@unfinished_policy = unfinished_policy
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Temporalio
|
4
|
+
module Internal
|
5
|
+
module Worker
|
6
|
+
class WorkflowInstance
|
7
|
+
# Hash for handlers that notifies when one is added. Only `[]=` and `store` can be used to mutate it.
|
8
|
+
class HandlerHash < SimpleDelegator
|
9
|
+
def initialize(initial_frozen_hash, definition_class, &on_new_definition)
|
10
|
+
super(initial_frozen_hash)
|
11
|
+
@definition_class = definition_class
|
12
|
+
@on_new_definition = on_new_definition
|
13
|
+
end
|
14
|
+
|
15
|
+
def []=(name, definition)
|
16
|
+
store(name, definition)
|
17
|
+
end
|
18
|
+
|
19
|
+
# steep:ignore:start
|
20
|
+
def store(name, definition)
|
21
|
+
raise ArgumentError, 'Name must be a string or nil' unless name.nil? || name.is_a?(String)
|
22
|
+
|
23
|
+
unless definition.nil? || definition.is_a?(@definition_class)
|
24
|
+
raise ArgumentError,
|
25
|
+
"Value must be a #{@definition_class.name} or nil"
|
26
|
+
end
|
27
|
+
raise ArgumentError, 'Name does not match one in definition' if definition && name != definition.name
|
28
|
+
|
29
|
+
# Do a copy-on-write op on the underlying frozen hash
|
30
|
+
new_hash = __getobj__.dup
|
31
|
+
new_hash[name] = definition
|
32
|
+
__setobj__(new_hash.freeze)
|
33
|
+
@on_new_definition&.call(definition) unless definition.nil?
|
34
|
+
definition
|
35
|
+
end
|
36
|
+
# steep:ignore:end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'temporalio/workflow'
|
4
|
+
|
5
|
+
module Temporalio
|
6
|
+
module Internal
|
7
|
+
module Worker
|
8
|
+
class WorkflowInstance
|
9
|
+
# Class that installs {::TracePoint} to disallow illegal calls.
|
10
|
+
class IllegalCallTracer
|
11
|
+
def self.frozen_validated_illegal_calls(illegal_calls)
|
12
|
+
illegal_calls.to_h do |key, val|
|
13
|
+
raise TypeError, 'Invalid illegal call map, top-level key must be a String' unless key.is_a?(String)
|
14
|
+
|
15
|
+
# @type var fixed_val: :all | Hash[Symbol, bool]
|
16
|
+
fixed_val = case val
|
17
|
+
when Array
|
18
|
+
val.to_h do |sub_val|
|
19
|
+
unless sub_val.is_a?(Symbol)
|
20
|
+
raise TypeError,
|
21
|
+
'Invalid illegal call map, each value must be a Symbol'
|
22
|
+
end
|
23
|
+
|
24
|
+
[sub_val, true]
|
25
|
+
end.freeze
|
26
|
+
when :all
|
27
|
+
:all
|
28
|
+
else
|
29
|
+
raise TypeError, 'Invalid illegal call map, top-level value must be an Array or :all'
|
30
|
+
end
|
31
|
+
|
32
|
+
[key.frozen? ? key : key.dup.freeze, fixed_val]
|
33
|
+
end.freeze
|
34
|
+
end
|
35
|
+
|
36
|
+
# Illegal calls are Hash[String, :all | Hash[Symbol, Bool]]
|
37
|
+
def initialize(illegal_calls)
|
38
|
+
@tracepoint = TracePoint.new(:call, :c_call) do |tp|
|
39
|
+
# Manual check for proper thread since we have seen issues in Ruby 3.2 where it leaks
|
40
|
+
next unless Thread.current == @enabled_thread
|
41
|
+
|
42
|
+
cls = tp.defined_class
|
43
|
+
next unless cls.is_a?(Module)
|
44
|
+
|
45
|
+
# Extract the class name from the defined class. This is more difficult than it seems because you have to
|
46
|
+
# resolve the attached object of the singleton class. But in older Ruby (at least <= 3.1), the singleton
|
47
|
+
# class of things like `Date` does not have `attached_object` so you have to fall back in these rare cases
|
48
|
+
# to parsing the string output. Reaching the string parsing component is rare, so this should not have
|
49
|
+
# significant performance impact.
|
50
|
+
cls_name = if cls.singleton_class?
|
51
|
+
if cls.respond_to?(:attached_object)
|
52
|
+
cls = cls.attached_object # steep:ignore
|
53
|
+
next unless cls.is_a?(Module)
|
54
|
+
|
55
|
+
cls.name.to_s
|
56
|
+
else
|
57
|
+
cls.to_s.delete_prefix('#<Class:').delete_suffix('>')
|
58
|
+
end
|
59
|
+
else
|
60
|
+
cls.name.to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
# Check if the call is considered illegal
|
64
|
+
vals = illegal_calls[cls_name]
|
65
|
+
if vals == :all || vals&.[](tp.callee_id) # steep:ignore
|
66
|
+
raise Workflow::NondeterminismError,
|
67
|
+
"Cannot access #{cls_name} #{tp.callee_id} from inside a " \
|
68
|
+
'workflow. If this is known to be safe, the code can be run in ' \
|
69
|
+
'a Temporalio::Workflow::Unsafe.illegal_call_tracing_disabled block.'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def enable(&block)
|
75
|
+
# We've seen leaking issues in Ruby 3.2 where the TracePoint inadvertently remains enabled even for threads
|
76
|
+
# that it was not started on. So we will check the thread ourselves.
|
77
|
+
@enabled_thread = Thread.current
|
78
|
+
@tracepoint.enable do
|
79
|
+
block.call
|
80
|
+
ensure
|
81
|
+
@enabled_thread = nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def disable(&block)
|
86
|
+
previous_thread = @enabled_thread
|
87
|
+
@tracepoint.disable do
|
88
|
+
block.call
|
89
|
+
ensure
|
90
|
+
@enabled_thread = previous_thread
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'temporalio/internal/worker/workflow_instance'
|
4
|
+
require 'temporalio/worker/interceptor'
|
5
|
+
require 'temporalio/workflow'
|
6
|
+
|
7
|
+
module Temporalio
|
8
|
+
module Internal
|
9
|
+
module Worker
|
10
|
+
class WorkflowInstance
|
11
|
+
# Root implementation of the inbound interceptor.
|
12
|
+
class InboundImplementation < Temporalio::Worker::Interceptor::Workflow::Inbound
|
13
|
+
def initialize(instance)
|
14
|
+
super(nil) # steep:ignore
|
15
|
+
@instance = instance
|
16
|
+
end
|
17
|
+
|
18
|
+
def init(outbound)
|
19
|
+
@instance.context._outbound = outbound
|
20
|
+
end
|
21
|
+
|
22
|
+
def execute(input)
|
23
|
+
@instance.instance.execute(*input.args)
|
24
|
+
end
|
25
|
+
|
26
|
+
def handle_signal(input)
|
27
|
+
invoke_handler(input.signal, input)
|
28
|
+
end
|
29
|
+
|
30
|
+
def handle_query(input)
|
31
|
+
invoke_handler(input.query, input)
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate_update(input)
|
35
|
+
invoke_handler(input.update, input, to_invoke: input.definition.validator_to_invoke)
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_update(input)
|
39
|
+
invoke_handler(input.update, input)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def invoke_handler(name, input, to_invoke: input.definition.to_invoke)
|
45
|
+
args = input.args
|
46
|
+
# Add name as first param if dynamic
|
47
|
+
args = [name] + args if input.definition.name.nil?
|
48
|
+
# Assume symbol or proc
|
49
|
+
case to_invoke
|
50
|
+
when Symbol
|
51
|
+
@instance.instance.send(to_invoke, *args)
|
52
|
+
when Proc
|
53
|
+
to_invoke.call(*args)
|
54
|
+
else
|
55
|
+
raise "Unrecognized invocation type #{to_invoke.class}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|