temporalio 0.4.0 → 0.5.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/.yardopts +1 -1
- data/Cargo.lock +679 -437
- data/Cargo.toml +5 -5
- data/README.md +98 -34
- data/ext/Cargo.toml +3 -3
- data/lib/temporalio/activity/cancellation_details.rb +58 -0
- data/lib/temporalio/activity/context.rb +10 -1
- data/lib/temporalio/activity/definition.rb +41 -3
- data/lib/temporalio/activity/info.rb +25 -4
- data/lib/temporalio/activity.rb +2 -0
- data/lib/temporalio/api/activity/v1/message.rb +1 -1
- data/lib/temporalio/api/batch/v1/message.rb +4 -2
- data/lib/temporalio/api/cloud/account/v1/message.rb +1 -1
- data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +11 -2
- data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +2 -2
- data/lib/temporalio/api/cloud/identity/v1/message.rb +7 -2
- data/lib/temporalio/api/cloud/namespace/v1/message.rb +6 -2
- data/lib/temporalio/api/cloud/nexus/v1/message.rb +3 -2
- data/lib/temporalio/api/cloud/operation/v1/message.rb +1 -1
- data/lib/temporalio/api/cloud/region/v1/message.rb +1 -1
- data/lib/temporalio/api/cloud/resource/v1/message.rb +1 -1
- data/lib/temporalio/api/cloud/sink/v1/message.rb +1 -1
- data/lib/temporalio/api/cloud/usage/v1/message.rb +1 -1
- data/lib/temporalio/api/command/v1/message.rb +2 -2
- data/lib/temporalio/api/common/v1/grpc_status.rb +1 -1
- data/lib/temporalio/api/common/v1/message.rb +3 -2
- data/lib/temporalio/api/deployment/v1/message.rb +3 -2
- data/lib/temporalio/api/enums/v1/batch_operation.rb +1 -1
- data/lib/temporalio/api/enums/v1/command_type.rb +1 -1
- data/lib/temporalio/api/enums/v1/common.rb +5 -2
- data/lib/temporalio/api/enums/v1/deployment.rb +3 -2
- data/lib/temporalio/api/enums/v1/event_type.rb +2 -2
- data/lib/temporalio/api/enums/v1/failed_cause.rb +2 -2
- data/lib/temporalio/api/enums/v1/namespace.rb +1 -1
- data/lib/temporalio/api/enums/v1/nexus.rb +1 -1
- data/lib/temporalio/api/enums/v1/query.rb +1 -1
- data/lib/temporalio/api/enums/v1/reset.rb +1 -1
- data/lib/temporalio/api/enums/v1/schedule.rb +1 -1
- data/lib/temporalio/api/enums/v1/task_queue.rb +1 -1
- data/lib/temporalio/api/enums/v1/update.rb +1 -1
- data/lib/temporalio/api/enums/v1/workflow.rb +2 -2
- data/lib/temporalio/api/errordetails/v1/message.rb +1 -1
- data/lib/temporalio/api/export/v1/message.rb +1 -1
- data/lib/temporalio/api/failure/v1/message.rb +3 -2
- data/lib/temporalio/api/filter/v1/message.rb +1 -1
- data/lib/temporalio/api/history/v1/message.rb +4 -2
- data/lib/temporalio/api/namespace/v1/message.rb +1 -1
- data/lib/temporalio/api/nexus/v1/message.rb +2 -2
- data/lib/temporalio/api/operatorservice/v1/request_response.rb +1 -1
- data/lib/temporalio/api/operatorservice/v1/service.rb +1 -1
- data/lib/temporalio/api/payload_visitor.rb +87 -0
- data/lib/temporalio/api/protocol/v1/message.rb +1 -1
- data/lib/temporalio/api/query/v1/message.rb +1 -1
- data/lib/temporalio/api/replication/v1/message.rb +1 -1
- data/lib/temporalio/api/rules/v1/message.rb +27 -0
- data/lib/temporalio/api/schedule/v1/message.rb +2 -2
- data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +1 -1
- data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +1 -1
- data/lib/temporalio/api/sdk/v1/user_metadata.rb +1 -1
- data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +1 -1
- data/lib/temporalio/api/taskqueue/v1/message.rb +2 -2
- data/lib/temporalio/api/testservice/v1/request_response.rb +1 -1
- data/lib/temporalio/api/testservice/v1/service.rb +1 -1
- data/lib/temporalio/api/update/v1/message.rb +1 -1
- data/lib/temporalio/api/version/v1/message.rb +1 -1
- data/lib/temporalio/api/worker/v1/message.rb +30 -0
- data/lib/temporalio/api/workflow/v1/message.rb +14 -2
- data/lib/temporalio/api/workflowservice/v1/request_response.rb +19 -2
- data/lib/temporalio/api/workflowservice/v1/service.rb +2 -2
- data/lib/temporalio/client/async_activity_handle.rb +12 -4
- data/lib/temporalio/client/connection/cloud_service.rb +60 -0
- data/lib/temporalio/client/connection/workflow_service.rb +105 -0
- data/lib/temporalio/client/interceptor.rb +25 -7
- data/lib/temporalio/client/schedule.rb +10 -2
- data/lib/temporalio/client/with_start_workflow_operation.rb +9 -1
- data/lib/temporalio/client/workflow_handle.rb +50 -10
- data/lib/temporalio/client/workflow_update_handle.rb +9 -3
- data/lib/temporalio/client.rb +110 -6
- data/lib/temporalio/common_enums.rb +14 -0
- data/lib/temporalio/contrib/open_telemetry.rb +13 -9
- data/lib/temporalio/converters/data_converter.rb +18 -8
- data/lib/temporalio/converters/failure_converter.rb +6 -3
- data/lib/temporalio/converters/payload_converter/binary_null.rb +2 -2
- data/lib/temporalio/converters/payload_converter/binary_plain.rb +2 -2
- data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +2 -2
- data/lib/temporalio/converters/payload_converter/composite.rb +6 -4
- data/lib/temporalio/converters/payload_converter/encoding.rb +4 -2
- data/lib/temporalio/converters/payload_converter/json_plain.rb +2 -2
- data/lib/temporalio/converters/payload_converter/json_protobuf.rb +2 -2
- data/lib/temporalio/converters/payload_converter.rb +16 -6
- data/lib/temporalio/error/failure.rb +19 -1
- data/lib/temporalio/error.rb +1 -1
- data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +1 -1
- data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +3 -2
- data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +1 -1
- data/lib/temporalio/internal/bridge/api/common/common.rb +1 -1
- data/lib/temporalio/internal/bridge/api/core_interface.rb +1 -1
- data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +1 -1
- data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +3 -2
- data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +2 -2
- data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +3 -2
- data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +1 -1
- data/lib/temporalio/internal/bridge/worker.rb +28 -4
- data/lib/temporalio/internal/bridge.rb +1 -1
- data/lib/temporalio/internal/client/implementation.rb +60 -52
- data/lib/temporalio/internal/proto_utils.rb +4 -4
- data/lib/temporalio/internal/worker/activity_worker.rb +93 -20
- data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +8 -6
- data/lib/temporalio/internal/worker/workflow_instance/context.rb +65 -24
- data/lib/temporalio/internal/worker/workflow_instance/details.rb +5 -2
- data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +2 -2
- data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +64 -18
- data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +28 -14
- data/lib/temporalio/internal/worker/workflow_instance.rb +58 -23
- data/lib/temporalio/internal/worker/workflow_worker.rb +16 -6
- data/lib/temporalio/priority.rb +59 -0
- data/lib/temporalio/testing/activity_environment.rb +17 -2
- data/lib/temporalio/testing/workflow_environment.rb +3 -3
- data/lib/temporalio/version.rb +1 -1
- data/lib/temporalio/versioning_override.rb +56 -0
- data/lib/temporalio/worker/deployment_options.rb +45 -0
- data/lib/temporalio/worker/illegal_workflow_call_validator.rb +64 -0
- data/lib/temporalio/worker/interceptor.rb +13 -1
- data/lib/temporalio/worker/poller_behavior.rb +61 -0
- data/lib/temporalio/worker/thread_pool.rb +1 -1
- data/lib/temporalio/worker/workflow_executor/thread_pool.rb +2 -1
- data/lib/temporalio/worker/workflow_replayer.rb +12 -13
- data/lib/temporalio/worker.rb +63 -27
- data/lib/temporalio/worker_deployment_version.rb +67 -0
- data/lib/temporalio/workflow/child_workflow_handle.rb +10 -2
- data/lib/temporalio/workflow/definition.rb +183 -33
- data/lib/temporalio/workflow/external_workflow_handle.rb +3 -1
- data/lib/temporalio/workflow/info.rb +4 -1
- data/lib/temporalio/workflow.rb +61 -9
- data/lib/temporalio.rb +1 -0
- data/temporalio.gemspec +1 -0
- metadata +12 -3
@@ -94,16 +94,16 @@ module Temporalio
|
|
94
94
|
enum_val
|
95
95
|
end
|
96
96
|
|
97
|
-
def self.convert_from_payload_array(converter, payloads)
|
97
|
+
def self.convert_from_payload_array(converter, payloads, hints:)
|
98
98
|
return [] if payloads.empty?
|
99
99
|
|
100
|
-
converter.from_payloads(Api::Common::V1::Payloads.new(payloads:))
|
100
|
+
converter.from_payloads(Api::Common::V1::Payloads.new(payloads:), hints:)
|
101
101
|
end
|
102
102
|
|
103
|
-
def self.convert_to_payload_array(converter, values)
|
103
|
+
def self.convert_to_payload_array(converter, values, hints:)
|
104
104
|
return [] if values.empty?
|
105
105
|
|
106
|
-
converter.to_payloads(values).payloads.to_ary
|
106
|
+
converter.to_payloads(values, hints:).payloads.to_ary
|
107
107
|
end
|
108
108
|
|
109
109
|
def self.assert_non_reserved_name(name)
|
@@ -148,10 +148,18 @@ module Temporalio
|
|
148
148
|
@scoped_logger.warn("Cannot find activity to cancel for token #{task_token}")
|
149
149
|
return
|
150
150
|
end
|
151
|
-
activity._server_requested_cancel = true
|
152
|
-
_, cancel_proc = activity.cancellation
|
153
151
|
begin
|
154
|
-
|
152
|
+
activity._cancel(
|
153
|
+
reason: cancel.reason.to_s,
|
154
|
+
details: Activity::CancellationDetails.new(
|
155
|
+
gone_from_server: cancel.details.is_not_found,
|
156
|
+
cancel_requested: cancel.details.is_cancelled,
|
157
|
+
timed_out: cancel.details.is_timed_out,
|
158
|
+
worker_shutdown: cancel.details.is_worker_shutdown,
|
159
|
+
paused: cancel.details.is_paused,
|
160
|
+
reset: cancel.details.is_reset
|
161
|
+
)
|
162
|
+
)
|
155
163
|
rescue StandardError => e
|
156
164
|
@scoped_logger.warn("Failed cancelling activity #{activity.info.activity_type} \
|
157
165
|
with ID #{activity.info.activity_id}")
|
@@ -168,12 +176,15 @@ module Temporalio
|
|
168
176
|
current_attempt_scheduled_time: Internal::ProtoUtils.timestamp_to_time(
|
169
177
|
start.current_attempt_scheduled_time
|
170
178
|
) || raise, # Never nil
|
171
|
-
heartbeat_details: ProtoUtils.convert_from_payload_array(
|
172
|
-
@worker.options.client.data_converter,
|
173
|
-
start.heartbeat_details.to_ary
|
174
|
-
),
|
175
179
|
heartbeat_timeout: Internal::ProtoUtils.duration_to_seconds(start.heartbeat_timeout),
|
176
180
|
local?: start.is_local,
|
181
|
+
priority: Priority._from_proto(start.priority),
|
182
|
+
raw_heartbeat_details: begin
|
183
|
+
payloads = start.heartbeat_details.to_ary
|
184
|
+
codec = @worker.options.client.data_converter.payload_codec
|
185
|
+
payloads = codec.decode(payloads) if codec
|
186
|
+
payloads.map { |p| Temporalio::Converters::RawValue.new(p) }
|
187
|
+
end,
|
177
188
|
schedule_to_close_timeout: Internal::ProtoUtils.duration_to_seconds(start.schedule_to_close_timeout),
|
178
189
|
scheduled_time: Internal::ProtoUtils.timestamp_to_time(start.scheduled_time) || raise, # Never nil
|
179
190
|
start_to_close_timeout: Internal::ProtoUtils.duration_to_seconds(start.start_to_close_timeout),
|
@@ -184,7 +195,7 @@ module Temporalio
|
|
184
195
|
workflow_namespace: start.workflow_namespace,
|
185
196
|
workflow_run_id: start.workflow_execution.run_id,
|
186
197
|
workflow_type: start.workflow_type
|
187
|
-
)
|
198
|
+
)
|
188
199
|
|
189
200
|
# Build input
|
190
201
|
input = Temporalio::Worker::Interceptor::Activity::ExecuteInput.new(
|
@@ -196,8 +207,13 @@ module Temporalio
|
|
196
207
|
payloads = codec.decode(payloads) if codec
|
197
208
|
payloads.map { |p| Temporalio::Converters::RawValue.new(p) }
|
198
209
|
else
|
199
|
-
ProtoUtils.convert_from_payload_array(
|
210
|
+
ProtoUtils.convert_from_payload_array(
|
211
|
+
@worker.options.client.data_converter,
|
212
|
+
start.input.to_ary,
|
213
|
+
hints: defn.arg_hints
|
214
|
+
)
|
200
215
|
end,
|
216
|
+
result_hint: defn.result_hint,
|
201
217
|
headers: ProtoUtils.headers_from_proto_map(start.header_fields, @worker.options.client.data_converter) || {}
|
202
218
|
)
|
203
219
|
|
@@ -250,7 +266,7 @@ module Temporalio
|
|
250
266
|
impl = @worker._activity_interceptors.reverse_each.reduce(impl) do |acc, int|
|
251
267
|
int.intercept_activity(acc)
|
252
268
|
end
|
253
|
-
impl.init(OutboundImplementation.new(self))
|
269
|
+
impl.init(OutboundImplementation.new(self, activity.info.task_token))
|
254
270
|
|
255
271
|
# Execute
|
256
272
|
result = impl.execute(input)
|
@@ -258,7 +274,7 @@ module Temporalio
|
|
258
274
|
# Success
|
259
275
|
Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
260
276
|
completed: Bridge::Api::ActivityResult::Success.new(
|
261
|
-
result: @worker.options.client.data_converter.to_payload(result)
|
277
|
+
result: @worker.options.client.data_converter.to_payload(result, hint: input.result_hint)
|
262
278
|
)
|
263
279
|
)
|
264
280
|
rescue Exception => e # rubocop:disable Lint/RescueException -- We are intending to catch everything here
|
@@ -268,6 +284,30 @@ module Temporalio
|
|
268
284
|
Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
269
285
|
will_complete_async: Bridge::Api::ActivityResult::WillCompleteAsync.new
|
270
286
|
)
|
287
|
+
elsif e.is_a?(Error::CanceledError) && activity.cancellation_details&.paused?
|
288
|
+
# Server requested pause
|
289
|
+
@scoped_logger.debug('Completing activity as failed due to exception caused by pause')
|
290
|
+
Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
291
|
+
failed: Bridge::Api::ActivityResult::Failure.new(
|
292
|
+
failure: @worker.options.client.data_converter.to_failure(
|
293
|
+
Error._with_backtrace_and_cause(
|
294
|
+
Error::ApplicationError.new('Activity paused', type: 'ActivityPause'), backtrace: nil, cause: e
|
295
|
+
)
|
296
|
+
)
|
297
|
+
)
|
298
|
+
)
|
299
|
+
elsif e.is_a?(Error::CanceledError) && activity.cancellation_details&.reset?
|
300
|
+
# Server requested reset
|
301
|
+
@scoped_logger.debug('Completing activity as failed due to exception caused by reset')
|
302
|
+
Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
303
|
+
failed: Bridge::Api::ActivityResult::Failure.new(
|
304
|
+
failure: @worker.options.client.data_converter.to_failure(
|
305
|
+
Error._with_backtrace_and_cause(
|
306
|
+
Error::ApplicationError.new('Activity reset', type: 'ActivityReset'), backtrace: nil, cause: e
|
307
|
+
)
|
308
|
+
)
|
309
|
+
)
|
310
|
+
)
|
271
311
|
elsif e.is_a?(Error::CanceledError) && activity._server_requested_cancel
|
272
312
|
# Server requested cancel
|
273
313
|
@scoped_logger.debug('Completing activity as canceled')
|
@@ -278,8 +318,13 @@ module Temporalio
|
|
278
318
|
)
|
279
319
|
else
|
280
320
|
# General failure
|
281
|
-
|
282
|
-
|
321
|
+
log_level = if e.is_a?(Error::ApplicationError) && e.category == Error::ApplicationError::Category::BENIGN
|
322
|
+
Logger::DEBUG
|
323
|
+
else
|
324
|
+
Logger::WARN
|
325
|
+
end
|
326
|
+
@scoped_logger.add(log_level, 'Completing activity as failed')
|
327
|
+
@scoped_logger.add(log_level, e)
|
283
328
|
Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
284
329
|
failed: Bridge::Api::ActivityResult::Failure.new(
|
285
330
|
failure: @worker.options.client.data_converter.to_failure(e)
|
@@ -297,9 +342,21 @@ module Temporalio
|
|
297
342
|
)
|
298
343
|
end
|
299
344
|
|
345
|
+
def assert_valid_activity(activity)
|
346
|
+
defn = @activities[activity]
|
347
|
+
defn = @activities[nil] if !defn && !Internal::ProtoUtils.reserved_name?(activity)
|
348
|
+
|
349
|
+
return unless defn.nil?
|
350
|
+
|
351
|
+
raise ArgumentError,
|
352
|
+
"Activity #{activity} " \
|
353
|
+
"is not registered on this worker, available activities: #{@activities.keys.sort.join(', ')}"
|
354
|
+
end
|
355
|
+
|
300
356
|
class RunningActivity < Activity::Context
|
301
|
-
attr_reader :info, :cancellation, :
|
302
|
-
|
357
|
+
attr_reader :info, :cancellation, :cancellation_details, :worker_shutdown_cancellation,
|
358
|
+
:payload_converter, :logger, :_server_requested_cancel
|
359
|
+
attr_accessor :instance, :_outbound_impl
|
303
360
|
|
304
361
|
def initialize( # rubocop:disable Lint/MissingSuper
|
305
362
|
worker:,
|
@@ -313,6 +370,7 @@ module Temporalio
|
|
313
370
|
@worker = worker
|
314
371
|
@info = info
|
315
372
|
@cancellation = cancellation
|
373
|
+
@cancellation_details = nil
|
316
374
|
@worker_shutdown_cancellation = worker_shutdown_cancellation
|
317
375
|
@payload_converter = payload_converter
|
318
376
|
@logger = logger
|
@@ -321,13 +379,15 @@ module Temporalio
|
|
321
379
|
@_server_requested_cancel = false
|
322
380
|
end
|
323
381
|
|
324
|
-
def heartbeat(*details)
|
382
|
+
def heartbeat(*details, detail_hints: nil)
|
325
383
|
raise 'Implementation not set yet' if _outbound_impl.nil?
|
326
384
|
|
327
385
|
# No-op if local
|
328
386
|
return if info.local?
|
329
387
|
|
330
|
-
_outbound_impl.heartbeat(
|
388
|
+
_outbound_impl.heartbeat(
|
389
|
+
Temporalio::Worker::Interceptor::Activity::HeartbeatInput.new(details:, detail_hints:)
|
390
|
+
)
|
331
391
|
end
|
332
392
|
|
333
393
|
def metric_meter
|
@@ -343,6 +403,17 @@ module Temporalio
|
|
343
403
|
def client
|
344
404
|
@worker.client
|
345
405
|
end
|
406
|
+
|
407
|
+
def _cancel(reason:, details:)
|
408
|
+
# Do not issue cancel if already canceled
|
409
|
+
return if @cancellation_details
|
410
|
+
|
411
|
+
@_server_requested_cancel = true
|
412
|
+
# Set the cancellation details _before_ issuing the cancel itself
|
413
|
+
@cancellation_details = details
|
414
|
+
_, cancel_proc = cancellation
|
415
|
+
cancel_proc.call(reason:)
|
416
|
+
end
|
346
417
|
end
|
347
418
|
|
348
419
|
class InboundImplementation < Temporalio::Worker::Interceptor::Activity::Inbound
|
@@ -364,17 +435,19 @@ module Temporalio
|
|
364
435
|
end
|
365
436
|
|
366
437
|
class OutboundImplementation < Temporalio::Worker::Interceptor::Activity::Outbound
|
367
|
-
def initialize(worker)
|
438
|
+
def initialize(worker, task_token)
|
368
439
|
super(nil) # steep:ignore
|
369
440
|
@worker = worker
|
441
|
+
@task_token = task_token
|
370
442
|
end
|
371
443
|
|
372
444
|
def heartbeat(input)
|
373
445
|
@worker.bridge_worker.record_activity_heartbeat(
|
374
446
|
Bridge::Api::CoreInterface::ActivityHeartbeat.new(
|
375
|
-
task_token:
|
447
|
+
task_token: @task_token,
|
376
448
|
details: ProtoUtils.convert_to_payload_array(@worker.worker.options.client.data_converter,
|
377
|
-
input.details
|
449
|
+
input.details,
|
450
|
+
hints: input.detail_hints)
|
378
451
|
).to_proto
|
379
452
|
)
|
380
453
|
end
|
@@ -10,18 +10,20 @@ module Temporalio
|
|
10
10
|
class WorkflowInstance
|
11
11
|
# Implementation of the child workflow handle.
|
12
12
|
class ChildWorkflowHandle < Workflow::ChildWorkflowHandle
|
13
|
-
attr_reader :id, :first_execution_run_id
|
13
|
+
attr_reader :id, :first_execution_run_id, :result_hint
|
14
14
|
|
15
|
-
def initialize(id:, first_execution_run_id:, instance:,
|
15
|
+
def initialize(id:, first_execution_run_id:, instance:, # rubocop:disable Lint/MissingSuper
|
16
|
+
cancellation:, cancel_callback_key:, result_hint:)
|
16
17
|
@id = id
|
17
18
|
@first_execution_run_id = first_execution_run_id
|
18
19
|
@instance = instance
|
19
20
|
@cancellation = cancellation
|
20
21
|
@cancel_callback_key = cancel_callback_key
|
22
|
+
@result_hint = result_hint
|
21
23
|
@resolution = nil
|
22
24
|
end
|
23
25
|
|
24
|
-
def result
|
26
|
+
def result(result_hint: nil)
|
25
27
|
# Notice that we actually provide a detached cancellation here instead of defaulting to workflow
|
26
28
|
# cancellation because we don't want workflow cancellation (or a user-provided cancellation to this result
|
27
29
|
# call) to be able to interrupt waiting on a child that may be processing the cancellation.
|
@@ -29,7 +31,7 @@ module Temporalio
|
|
29
31
|
|
30
32
|
case @resolution.status
|
31
33
|
when :completed
|
32
|
-
@instance.payload_converter.from_payload(@resolution.completed.result)
|
34
|
+
@instance.payload_converter.from_payload(@resolution.completed.result, hint: result_hint || @result_hint)
|
33
35
|
when :failed
|
34
36
|
raise @instance.failure_converter.from_failure(@resolution.failed.failure, @instance.payload_converter)
|
35
37
|
when :cancelled
|
@@ -44,8 +46,8 @@ module Temporalio
|
|
44
46
|
@resolution = resolution
|
45
47
|
end
|
46
48
|
|
47
|
-
def signal(signal, *args, cancellation: Workflow.cancellation)
|
48
|
-
@instance.context._signal_child_workflow(id:, signal:, args:, cancellation:)
|
49
|
+
def signal(signal, *args, cancellation: Workflow.cancellation, arg_hints: nil)
|
50
|
+
@instance.context._signal_child_workflow(id:, signal:, args:, cancellation:, arg_hints:)
|
49
51
|
end
|
50
52
|
end
|
51
53
|
end
|
@@ -42,6 +42,10 @@ module Temporalio
|
|
42
42
|
@instance.current_details = (details || '')
|
43
43
|
end
|
44
44
|
|
45
|
+
def current_deployment_version
|
46
|
+
@instance.current_deployment_version
|
47
|
+
end
|
48
|
+
|
45
49
|
def current_history_length
|
46
50
|
@instance.current_history_length
|
47
51
|
end
|
@@ -58,6 +62,16 @@ module Temporalio
|
|
58
62
|
@instance.patch(patch_id:, deprecated: true)
|
59
63
|
end
|
60
64
|
|
65
|
+
def durable_scheduler_disabled(&)
|
66
|
+
prev = Fiber.current_scheduler
|
67
|
+
illegal_call_tracing_disabled { Fiber.set_scheduler(nil) }
|
68
|
+
begin
|
69
|
+
yield
|
70
|
+
ensure
|
71
|
+
illegal_call_tracing_disabled { Fiber.set_scheduler(prev) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
61
75
|
def execute_activity(
|
62
76
|
activity,
|
63
77
|
*args,
|
@@ -71,16 +85,22 @@ module Temporalio
|
|
71
85
|
cancellation:,
|
72
86
|
cancellation_type:,
|
73
87
|
activity_id:,
|
74
|
-
disable_eager_execution
|
88
|
+
disable_eager_execution:,
|
89
|
+
priority:,
|
90
|
+
arg_hints:,
|
91
|
+
result_hint:
|
75
92
|
)
|
76
|
-
activity
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
93
|
+
activity, defn_arg_hints, defn_result_hint =
|
94
|
+
case activity
|
95
|
+
when Class
|
96
|
+
defn = Activity::Definition::Info.from_activity(activity)
|
97
|
+
[defn.name&.to_s, defn.arg_hints, defn.result_hint]
|
98
|
+
when Symbol, String
|
99
|
+
[activity.to_s, nil, nil]
|
100
|
+
else
|
101
|
+
raise ArgumentError,
|
102
|
+
'Activity must be a definition class, or a symbol/string'
|
103
|
+
end
|
84
104
|
raise 'Cannot invoke dynamic activities' unless activity
|
85
105
|
|
86
106
|
@outbound.execute_activity(
|
@@ -98,6 +118,9 @@ module Temporalio
|
|
98
118
|
cancellation_type:,
|
99
119
|
activity_id:,
|
100
120
|
disable_eager_execution: disable_eager_execution || @instance.disable_eager_activity_execution,
|
121
|
+
priority:,
|
122
|
+
arg_hints: arg_hints || defn_arg_hints,
|
123
|
+
result_hint: result_hint || defn_result_hint,
|
101
124
|
headers: {}
|
102
125
|
)
|
103
126
|
)
|
@@ -113,16 +136,20 @@ module Temporalio
|
|
113
136
|
local_retry_threshold:,
|
114
137
|
cancellation:,
|
115
138
|
cancellation_type:,
|
116
|
-
activity_id
|
139
|
+
activity_id:,
|
140
|
+
arg_hints:,
|
141
|
+
result_hint:
|
117
142
|
)
|
118
|
-
activity
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
143
|
+
activity, defn_arg_hints, defn_result_hint =
|
144
|
+
case activity
|
145
|
+
when Class
|
146
|
+
defn = Activity::Definition::Info.from_activity(activity)
|
147
|
+
[defn.name&.to_s, defn.arg_hints, defn.result_hint]
|
148
|
+
when Symbol, String
|
149
|
+
[activity.to_s, nil, nil]
|
150
|
+
else
|
151
|
+
raise ArgumentError, 'Activity must be a definition class, or a symbol/string'
|
152
|
+
end
|
126
153
|
raise 'Cannot invoke dynamic activities' unless activity
|
127
154
|
|
128
155
|
@outbound.execute_local_activity(
|
@@ -137,6 +164,8 @@ module Temporalio
|
|
137
164
|
cancellation:,
|
138
165
|
cancellation_type:,
|
139
166
|
activity_id:,
|
167
|
+
arg_hints: arg_hints || defn_arg_hints,
|
168
|
+
result_hint: result_hint || defn_result_hint,
|
140
169
|
headers: {}
|
141
170
|
)
|
142
171
|
)
|
@@ -245,11 +274,16 @@ module Temporalio
|
|
245
274
|
retry_policy:,
|
246
275
|
cron_schedule:,
|
247
276
|
memo:,
|
248
|
-
search_attributes
|
277
|
+
search_attributes:,
|
278
|
+
priority:,
|
279
|
+
arg_hints:,
|
280
|
+
result_hint:
|
249
281
|
)
|
282
|
+
workflow, defn_arg_hints, defn_result_hint =
|
283
|
+
Workflow::Definition._workflow_type_and_hints_from_workflow_parameter(workflow)
|
250
284
|
@outbound.start_child_workflow(
|
251
285
|
Temporalio::Worker::Interceptor::Workflow::StartChildWorkflowInput.new(
|
252
|
-
workflow
|
286
|
+
workflow:,
|
253
287
|
args:,
|
254
288
|
id:,
|
255
289
|
task_queue:,
|
@@ -266,6 +300,9 @@ module Temporalio
|
|
266
300
|
cron_schedule:,
|
267
301
|
memo:,
|
268
302
|
search_attributes:,
|
303
|
+
priority:,
|
304
|
+
arg_hints: arg_hints || defn_arg_hints,
|
305
|
+
result_hint: result_hint || defn_result_hint,
|
269
306
|
headers: {}
|
270
307
|
)
|
271
308
|
)
|
@@ -352,26 +389,30 @@ module Temporalio
|
|
352
389
|
@outbound = outbound
|
353
390
|
end
|
354
391
|
|
355
|
-
def _signal_child_workflow(id:, signal:, args:, cancellation:)
|
392
|
+
def _signal_child_workflow(id:, signal:, args:, cancellation:, arg_hints:)
|
393
|
+
signal, defn_arg_hints = Workflow::Definition::Signal._name_and_hints_from_parameter(signal)
|
356
394
|
@outbound.signal_child_workflow(
|
357
395
|
Temporalio::Worker::Interceptor::Workflow::SignalChildWorkflowInput.new(
|
358
396
|
id:,
|
359
|
-
signal
|
397
|
+
signal:,
|
360
398
|
args:,
|
361
399
|
cancellation:,
|
400
|
+
arg_hints: arg_hints || defn_arg_hints,
|
362
401
|
headers: {}
|
363
402
|
)
|
364
403
|
)
|
365
404
|
end
|
366
405
|
|
367
|
-
def _signal_external_workflow(id:, run_id:, signal:, args:, cancellation:)
|
406
|
+
def _signal_external_workflow(id:, run_id:, signal:, args:, cancellation:, arg_hints:)
|
407
|
+
signal, defn_arg_hints = Workflow::Definition::Signal._name_and_hints_from_parameter(signal)
|
368
408
|
@outbound.signal_external_workflow(
|
369
409
|
Temporalio::Worker::Interceptor::Workflow::SignalExternalWorkflowInput.new(
|
370
410
|
id:,
|
371
411
|
run_id:,
|
372
|
-
signal
|
412
|
+
signal:,
|
373
413
|
args:,
|
374
414
|
cancellation:,
|
415
|
+
arg_hints: arg_hints || defn_arg_hints,
|
375
416
|
headers: {}
|
376
417
|
)
|
377
418
|
)
|
@@ -8,7 +8,8 @@ module Temporalio
|
|
8
8
|
class Details
|
9
9
|
attr_reader :namespace, :task_queue, :definition, :initial_activation, :logger, :metric_meter,
|
10
10
|
:payload_converter, :failure_converter, :interceptors, :disable_eager_activity_execution,
|
11
|
-
:illegal_calls, :workflow_failure_exception_types, :unsafe_workflow_io_enabled
|
11
|
+
:illegal_calls, :workflow_failure_exception_types, :unsafe_workflow_io_enabled,
|
12
|
+
:assert_valid_local_activity
|
12
13
|
|
13
14
|
def initialize(
|
14
15
|
namespace:,
|
@@ -23,7 +24,8 @@ module Temporalio
|
|
23
24
|
disable_eager_activity_execution:,
|
24
25
|
illegal_calls:,
|
25
26
|
workflow_failure_exception_types:,
|
26
|
-
unsafe_workflow_io_enabled
|
27
|
+
unsafe_workflow_io_enabled:,
|
28
|
+
assert_valid_local_activity:
|
27
29
|
)
|
28
30
|
@namespace = namespace
|
29
31
|
@task_queue = task_queue
|
@@ -38,6 +40,7 @@ module Temporalio
|
|
38
40
|
@illegal_calls = illegal_calls
|
39
41
|
@workflow_failure_exception_types = workflow_failure_exception_types
|
40
42
|
@unsafe_workflow_io_enabled = unsafe_workflow_io_enabled
|
43
|
+
@assert_valid_local_activity = assert_valid_local_activity
|
41
44
|
end
|
42
45
|
end
|
43
46
|
end
|
@@ -18,8 +18,8 @@ module Temporalio
|
|
18
18
|
@instance = instance
|
19
19
|
end
|
20
20
|
|
21
|
-
def signal(signal, *args, cancellation: Workflow.cancellation)
|
22
|
-
@instance.context._signal_external_workflow(id:, run_id:, signal:, args:, cancellation:)
|
21
|
+
def signal(signal, *args, cancellation: Workflow.cancellation, arg_hints: nil)
|
22
|
+
@instance.context._signal_external_workflow(id:, run_id:, signal:, args:, cancellation:, arg_hints:)
|
23
23
|
end
|
24
24
|
|
25
25
|
def cancel
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'temporalio/worker/illegal_workflow_call_validator'
|
3
4
|
require 'temporalio/workflow'
|
4
5
|
|
5
6
|
module Temporalio
|
@@ -12,16 +13,32 @@ module Temporalio
|
|
12
13
|
illegal_calls.to_h do |key, val|
|
13
14
|
raise TypeError, 'Invalid illegal call map, top-level key must be a String' unless key.is_a?(String)
|
14
15
|
|
15
|
-
# @type var fixed_val: :all | Hash[Symbol,
|
16
|
+
# @type var fixed_val: :all | Worker::IllegalWorkflowCallValidator | Hash[Symbol, TrueClass | Worker::IllegalWorkflowCallValidator] # rubocop:disable Layout/LineLength
|
16
17
|
fixed_val = case val
|
18
|
+
when Temporalio::Worker::IllegalWorkflowCallValidator
|
19
|
+
if sub_val.method_name
|
20
|
+
raise ArgumentError,
|
21
|
+
'Top level IllegalWorkflowCallValidator instances cannot have method name'
|
22
|
+
end
|
23
|
+
val
|
17
24
|
when Array
|
18
25
|
val.to_h do |sub_val|
|
19
|
-
|
26
|
+
case sub_val
|
27
|
+
when Symbol
|
28
|
+
[sub_val, true]
|
29
|
+
when Temporalio::Worker::IllegalWorkflowCallValidator
|
30
|
+
unless sub_val.method_name
|
31
|
+
raise ArgumentError,
|
32
|
+
'IllegalWorkflowCallValidator instances in array for ' \
|
33
|
+
"#{key} must have a method name"
|
34
|
+
end
|
35
|
+
|
36
|
+
[sub_val.method_name, sub_val]
|
37
|
+
else
|
20
38
|
raise TypeError,
|
21
|
-
'Invalid illegal call
|
39
|
+
'Invalid illegal call array entry for ' \
|
40
|
+
"#{key}, each value must be a Symbol or an IllegalWorkflowCallValidator"
|
22
41
|
end
|
23
|
-
|
24
|
-
[sub_val, true]
|
25
42
|
end.freeze
|
26
43
|
when :all
|
27
44
|
:all
|
@@ -47,25 +64,54 @@ module Temporalio
|
|
47
64
|
# class of things like `Date` does not have `attached_object` so you have to fall back in these rare cases
|
48
65
|
# to parsing the string output. Reaching the string parsing component is rare, so this should not have
|
49
66
|
# significant performance impact.
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
67
|
+
class_name = if cls.singleton_class?
|
68
|
+
if cls.respond_to?(:attached_object)
|
69
|
+
cls = cls.attached_object # steep:ignore
|
70
|
+
next unless cls.is_a?(Module)
|
54
71
|
|
55
|
-
|
72
|
+
cls.name.to_s
|
73
|
+
else
|
74
|
+
cls.to_s.delete_prefix('#<Class:').delete_suffix('>')
|
75
|
+
end
|
56
76
|
else
|
57
|
-
cls.to_s
|
77
|
+
cls.name.to_s
|
58
78
|
end
|
59
|
-
else
|
60
|
-
cls.name.to_s
|
61
|
-
end
|
62
79
|
|
63
80
|
# Check if the call is considered illegal
|
64
|
-
vals = illegal_calls[
|
65
|
-
|
81
|
+
vals = illegal_calls[class_name]
|
82
|
+
invalid_suffix =
|
83
|
+
case vals
|
84
|
+
when :all
|
85
|
+
''
|
86
|
+
when Temporalio::Worker::IllegalWorkflowCallValidator
|
87
|
+
disable do
|
88
|
+
vals.block.call(Temporalio::Worker::IllegalWorkflowCallValidator::CallInfo.new(
|
89
|
+
class_name:, method_name: tp.callee_id, trace_point: tp
|
90
|
+
))
|
91
|
+
nil
|
92
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
93
|
+
", reason: #{e}"
|
94
|
+
end
|
95
|
+
else
|
96
|
+
per_method = vals&.[](tp.callee_id)
|
97
|
+
case per_method
|
98
|
+
when true
|
99
|
+
''
|
100
|
+
when Temporalio::Worker::IllegalWorkflowCallValidator
|
101
|
+
disable do
|
102
|
+
per_method.block.call(Temporalio::Worker::IllegalWorkflowCallValidator::CallInfo.new(
|
103
|
+
class_name:, method_name: tp.callee_id, trace_point: tp
|
104
|
+
))
|
105
|
+
nil
|
106
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
107
|
+
", reason: #{e}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
if invalid_suffix
|
66
112
|
raise Workflow::NondeterminismError,
|
67
|
-
"Cannot access #{
|
68
|
-
|
113
|
+
"Cannot access #{class_name} #{tp.callee_id} from inside a " \
|
114
|
+
"workflow#{invalid_suffix}. If this is known to be safe, the code can be run in " \
|
69
115
|
'a Temporalio::Workflow::Unsafe.illegal_call_tracing_disabled block.'
|
70
116
|
end
|
71
117
|
end
|