amit-temporalio 0.3.1-x86_64-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 +7 -0
- data/.yardopts +2 -0
- data/Gemfile +23 -0
- data/Rakefile +101 -0
- data/lib/temporalio/activity/complete_async_error.rb +11 -0
- data/lib/temporalio/activity/context.rb +116 -0
- data/lib/temporalio/activity/definition.rb +189 -0
- data/lib/temporalio/activity/info.rb +64 -0
- data/lib/temporalio/activity.rb +12 -0
- data/lib/temporalio/api/activity/v1/message.rb +25 -0
- data/lib/temporalio/api/batch/v1/message.rb +31 -0
- data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
- data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +126 -0
- data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +25 -0
- data/lib/temporalio/api/cloud/cloudservice.rb +3 -0
- data/lib/temporalio/api/cloud/identity/v1/message.rb +41 -0
- data/lib/temporalio/api/cloud/namespace/v1/message.rb +42 -0
- data/lib/temporalio/api/cloud/nexus/v1/message.rb +31 -0
- data/lib/temporalio/api/cloud/operation/v1/message.rb +28 -0
- data/lib/temporalio/api/cloud/region/v1/message.rb +24 -0
- 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/command/v1/message.rb +46 -0
- data/lib/temporalio/api/common/v1/grpc_status.rb +23 -0
- data/lib/temporalio/api/common/v1/message.rb +47 -0
- data/lib/temporalio/api/enums/v1/batch_operation.rb +22 -0
- data/lib/temporalio/api/enums/v1/command_type.rb +21 -0
- data/lib/temporalio/api/enums/v1/common.rb +26 -0
- data/lib/temporalio/api/enums/v1/event_type.rb +21 -0
- data/lib/temporalio/api/enums/v1/failed_cause.rb +26 -0
- data/lib/temporalio/api/enums/v1/namespace.rb +23 -0
- data/lib/temporalio/api/enums/v1/query.rb +22 -0
- data/lib/temporalio/api/enums/v1/reset.rb +23 -0
- data/lib/temporalio/api/enums/v1/schedule.rb +21 -0
- data/lib/temporalio/api/enums/v1/task_queue.rb +25 -0
- data/lib/temporalio/api/enums/v1/update.rb +22 -0
- data/lib/temporalio/api/enums/v1/workflow.rb +30 -0
- data/lib/temporalio/api/errordetails/v1/message.rb +42 -0
- data/lib/temporalio/api/export/v1/message.rb +24 -0
- data/lib/temporalio/api/failure/v1/message.rb +35 -0
- data/lib/temporalio/api/filter/v1/message.rb +27 -0
- data/lib/temporalio/api/history/v1/message.rb +90 -0
- data/lib/temporalio/api/namespace/v1/message.rb +31 -0
- data/lib/temporalio/api/nexus/v1/message.rb +40 -0
- data/lib/temporalio/api/operatorservice/v1/request_response.rb +49 -0
- data/lib/temporalio/api/operatorservice/v1/service.rb +23 -0
- data/lib/temporalio/api/operatorservice.rb +3 -0
- data/lib/temporalio/api/payload_visitor.rb +1513 -0
- data/lib/temporalio/api/protocol/v1/message.rb +23 -0
- data/lib/temporalio/api/query/v1/message.rb +27 -0
- data/lib/temporalio/api/replication/v1/message.rb +26 -0
- data/lib/temporalio/api/schedule/v1/message.rb +43 -0
- data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +25 -0
- data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +21 -0
- data/lib/temporalio/api/sdk/v1/user_metadata.rb +23 -0
- data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +23 -0
- data/lib/temporalio/api/taskqueue/v1/message.rb +45 -0
- 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/update/v1/message.rb +33 -0
- data/lib/temporalio/api/version/v1/message.rb +26 -0
- data/lib/temporalio/api/workflow/v1/message.rb +43 -0
- data/lib/temporalio/api/workflowservice/v1/request_response.rb +204 -0
- data/lib/temporalio/api/workflowservice/v1/service.rb +23 -0
- data/lib/temporalio/api/workflowservice.rb +3 -0
- data/lib/temporalio/api.rb +14 -0
- data/lib/temporalio/cancellation.rb +170 -0
- data/lib/temporalio/client/activity_id_reference.rb +32 -0
- data/lib/temporalio/client/async_activity_handle.rb +85 -0
- data/lib/temporalio/client/connection/cloud_service.rb +726 -0
- data/lib/temporalio/client/connection/operator_service.rb +201 -0
- data/lib/temporalio/client/connection/service.rb +42 -0
- data/lib/temporalio/client/connection/test_service.rb +111 -0
- data/lib/temporalio/client/connection/workflow_service.rb +1041 -0
- data/lib/temporalio/client/connection.rb +316 -0
- data/lib/temporalio/client/interceptor.rb +416 -0
- data/lib/temporalio/client/schedule.rb +967 -0
- data/lib/temporalio/client/schedule_handle.rb +126 -0
- data/lib/temporalio/client/workflow_execution.rb +100 -0
- data/lib/temporalio/client/workflow_execution_count.rb +36 -0
- data/lib/temporalio/client/workflow_execution_status.rb +18 -0
- data/lib/temporalio/client/workflow_handle.rb +389 -0
- data/lib/temporalio/client/workflow_query_reject_condition.rb +14 -0
- data/lib/temporalio/client/workflow_update_handle.rb +65 -0
- data/lib/temporalio/client/workflow_update_wait_stage.rb +17 -0
- data/lib/temporalio/client.rb +484 -0
- data/lib/temporalio/common_enums.rb +41 -0
- data/lib/temporalio/converters/data_converter.rb +99 -0
- data/lib/temporalio/converters/failure_converter.rb +202 -0
- data/lib/temporalio/converters/payload_codec.rb +26 -0
- data/lib/temporalio/converters/payload_converter/binary_null.rb +34 -0
- data/lib/temporalio/converters/payload_converter/binary_plain.rb +35 -0
- data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +42 -0
- data/lib/temporalio/converters/payload_converter/composite.rb +66 -0
- data/lib/temporalio/converters/payload_converter/encoding.rb +35 -0
- data/lib/temporalio/converters/payload_converter/json_plain.rb +44 -0
- data/lib/temporalio/converters/payload_converter/json_protobuf.rb +41 -0
- data/lib/temporalio/converters/payload_converter.rb +71 -0
- data/lib/temporalio/converters/raw_value.rb +20 -0
- data/lib/temporalio/converters.rb +9 -0
- data/lib/temporalio/error/failure.rb +219 -0
- data/lib/temporalio/error.rb +155 -0
- data/lib/temporalio/internal/bridge/3.2/temporalio_bridge.so +0 -0
- 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/api/activity_result/activity_result.rb +34 -0
- data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +31 -0
- data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +33 -0
- data/lib/temporalio/internal/bridge/api/common/common.rb +26 -0
- data/lib/temporalio/internal/bridge/api/core_interface.rb +40 -0
- data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +27 -0
- data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +33 -0
- data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +56 -0
- data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +57 -0
- data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +30 -0
- data/lib/temporalio/internal/bridge/api.rb +3 -0
- data/lib/temporalio/internal/bridge/client.rb +95 -0
- data/lib/temporalio/internal/bridge/runtime.rb +53 -0
- data/lib/temporalio/internal/bridge/testing.rb +66 -0
- data/lib/temporalio/internal/bridge/worker.rb +85 -0
- data/lib/temporalio/internal/bridge.rb +36 -0
- data/lib/temporalio/internal/client/implementation.rb +700 -0
- data/lib/temporalio/internal/metric.rb +122 -0
- data/lib/temporalio/internal/proto_utils.rb +133 -0
- data/lib/temporalio/internal/worker/activity_worker.rb +376 -0
- data/lib/temporalio/internal/worker/multi_runner.rb +213 -0
- data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
- data/lib/temporalio/internal/worker/workflow_instance/context.rb +333 -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 +236 -0
- data/lib/temporalio/internal.rb +7 -0
- data/lib/temporalio/metric.rb +109 -0
- data/lib/temporalio/retry_policy.rb +74 -0
- data/lib/temporalio/runtime.rb +314 -0
- data/lib/temporalio/scoped_logger.rb +96 -0
- data/lib/temporalio/search_attributes.rb +343 -0
- data/lib/temporalio/testing/activity_environment.rb +136 -0
- data/lib/temporalio/testing/workflow_environment.rb +383 -0
- data/lib/temporalio/testing.rb +10 -0
- data/lib/temporalio/version.rb +5 -0
- data/lib/temporalio/worker/activity_executor/fiber.rb +49 -0
- data/lib/temporalio/worker/activity_executor/thread_pool.rb +46 -0
- data/lib/temporalio/worker/activity_executor.rb +55 -0
- data/lib/temporalio/worker/interceptor.rb +362 -0
- data/lib/temporalio/worker/thread_pool.rb +237 -0
- data/lib/temporalio/worker/tuner.rb +189 -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/workflow_replayer.rb +343 -0
- data/lib/temporalio/worker.rb +569 -0
- 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 +529 -0
- data/lib/temporalio/workflow_history.rb +47 -0
- data/lib/temporalio.rb +11 -0
- data/temporalio.gemspec +28 -0
- metadata +238 -0
@@ -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
|
@@ -0,0 +1,415 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'temporalio/activity/definition'
|
4
|
+
require 'temporalio/cancellation'
|
5
|
+
require 'temporalio/error'
|
6
|
+
require 'temporalio/internal/bridge/api'
|
7
|
+
require 'temporalio/internal/proto_utils'
|
8
|
+
require 'temporalio/internal/worker/workflow_instance'
|
9
|
+
require 'temporalio/worker/interceptor'
|
10
|
+
require 'temporalio/workflow'
|
11
|
+
require 'temporalio/workflow/child_workflow_handle'
|
12
|
+
|
13
|
+
module Temporalio
|
14
|
+
module Internal
|
15
|
+
module Worker
|
16
|
+
class WorkflowInstance
|
17
|
+
# Root implementation of the outbound interceptor.
|
18
|
+
class OutboundImplementation < Temporalio::Worker::Interceptor::Workflow::Outbound
|
19
|
+
def initialize(instance)
|
20
|
+
super(nil) # steep:ignore
|
21
|
+
@instance = instance
|
22
|
+
@activity_counter = 0
|
23
|
+
@timer_counter = 0
|
24
|
+
@child_counter = 0
|
25
|
+
@external_signal_counter = 0
|
26
|
+
@external_cancel_counter = 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def cancel_external_workflow(input)
|
30
|
+
# Add command
|
31
|
+
seq = (@external_cancel_counter += 1)
|
32
|
+
cmd = Bridge::Api::WorkflowCommands::RequestCancelExternalWorkflowExecution.new(
|
33
|
+
seq:,
|
34
|
+
workflow_execution: Bridge::Api::Common::NamespacedWorkflowExecution.new(
|
35
|
+
namespace: @instance.info.namespace,
|
36
|
+
workflow_id: input.id,
|
37
|
+
run_id: input.run_id
|
38
|
+
)
|
39
|
+
)
|
40
|
+
@instance.add_command(
|
41
|
+
Bridge::Api::WorkflowCommands::WorkflowCommand.new(request_cancel_external_workflow_execution: cmd)
|
42
|
+
)
|
43
|
+
@instance.pending_external_cancels[seq] = Fiber.current
|
44
|
+
|
45
|
+
# Wait
|
46
|
+
resolution = Fiber.yield
|
47
|
+
|
48
|
+
# Raise if resolution has failure
|
49
|
+
return unless resolution.failure
|
50
|
+
|
51
|
+
raise @instance.failure_converter.from_failure(resolution.failure, @instance.payload_converter)
|
52
|
+
end
|
53
|
+
|
54
|
+
def execute_activity(input)
|
55
|
+
if input.schedule_to_close_timeout.nil? && input.start_to_close_timeout.nil?
|
56
|
+
raise ArgumentError, 'Activity must have schedule_to_close_timeout or start_to_close_timeout'
|
57
|
+
end
|
58
|
+
|
59
|
+
activity_type = case input.activity
|
60
|
+
when Class
|
61
|
+
Activity::Definition::Info.from_activity(input.activity).name
|
62
|
+
when Symbol, String
|
63
|
+
input.activity.to_s
|
64
|
+
else
|
65
|
+
raise ArgumentError, 'Activity must be a definition class, or a symbol/string'
|
66
|
+
end
|
67
|
+
raise 'Cannot invoke dynamic activities' unless activity_type
|
68
|
+
|
69
|
+
execute_activity_with_local_backoffs(local: false, cancellation: input.cancellation) do
|
70
|
+
seq = (@activity_counter += 1)
|
71
|
+
@instance.add_command(
|
72
|
+
Bridge::Api::WorkflowCommands::WorkflowCommand.new(
|
73
|
+
schedule_activity: Bridge::Api::WorkflowCommands::ScheduleActivity.new(
|
74
|
+
seq:,
|
75
|
+
activity_id: input.activity_id || seq.to_s,
|
76
|
+
activity_type:,
|
77
|
+
task_queue: input.task_queue,
|
78
|
+
headers: ProtoUtils.headers_to_proto_hash(input.headers, @instance.payload_converter),
|
79
|
+
arguments: ProtoUtils.convert_to_payload_array(@instance.payload_converter, input.args),
|
80
|
+
schedule_to_close_timeout: ProtoUtils.seconds_to_duration(input.schedule_to_close_timeout),
|
81
|
+
schedule_to_start_timeout: ProtoUtils.seconds_to_duration(input.schedule_to_start_timeout),
|
82
|
+
start_to_close_timeout: ProtoUtils.seconds_to_duration(input.start_to_close_timeout),
|
83
|
+
heartbeat_timeout: ProtoUtils.seconds_to_duration(input.heartbeat_timeout),
|
84
|
+
retry_policy: input.retry_policy&._to_proto,
|
85
|
+
cancellation_type: input.cancellation_type,
|
86
|
+
do_not_eagerly_execute: input.disable_eager_execution
|
87
|
+
)
|
88
|
+
)
|
89
|
+
)
|
90
|
+
seq
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def execute_local_activity(input)
|
95
|
+
if input.schedule_to_close_timeout.nil? && input.start_to_close_timeout.nil?
|
96
|
+
raise ArgumentError, 'Activity must have schedule_to_close_timeout or start_to_close_timeout'
|
97
|
+
end
|
98
|
+
|
99
|
+
activity_type = case input.activity
|
100
|
+
when Class
|
101
|
+
Activity::Definition::Info.from_activity(input.activity).name
|
102
|
+
when Symbol, String
|
103
|
+
input.activity.to_s
|
104
|
+
else
|
105
|
+
raise ArgumentError, 'Activity must be a definition class, or a symbol/string'
|
106
|
+
end
|
107
|
+
raise 'Cannot invoke dynamic activities' unless activity_type
|
108
|
+
|
109
|
+
execute_activity_with_local_backoffs(local: true, cancellation: input.cancellation) do |do_backoff|
|
110
|
+
seq = (@activity_counter += 1)
|
111
|
+
@instance.add_command(
|
112
|
+
Bridge::Api::WorkflowCommands::WorkflowCommand.new(
|
113
|
+
schedule_local_activity: Bridge::Api::WorkflowCommands::ScheduleLocalActivity.new(
|
114
|
+
seq:,
|
115
|
+
activity_id: input.activity_id || seq.to_s,
|
116
|
+
activity_type:,
|
117
|
+
headers: ProtoUtils.headers_to_proto_hash(input.headers, @instance.payload_converter),
|
118
|
+
arguments: ProtoUtils.convert_to_payload_array(@instance.payload_converter, input.args),
|
119
|
+
schedule_to_close_timeout: ProtoUtils.seconds_to_duration(input.schedule_to_close_timeout),
|
120
|
+
schedule_to_start_timeout: ProtoUtils.seconds_to_duration(input.schedule_to_start_timeout),
|
121
|
+
start_to_close_timeout: ProtoUtils.seconds_to_duration(input.start_to_close_timeout),
|
122
|
+
retry_policy: input.retry_policy&._to_proto,
|
123
|
+
cancellation_type: input.cancellation_type,
|
124
|
+
local_retry_threshold: ProtoUtils.seconds_to_duration(input.local_retry_threshold),
|
125
|
+
attempt: do_backoff&.attempt || 0,
|
126
|
+
original_schedule_time: do_backoff&.original_schedule_time
|
127
|
+
)
|
128
|
+
)
|
129
|
+
)
|
130
|
+
seq
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def execute_activity_with_local_backoffs(local:, cancellation:, &)
|
135
|
+
# We do not even want to schedule if the cancellation is already cancelled. We choose to use canceled
|
136
|
+
# failure instead of wrapping in activity failure which is similar to what other SDKs do, with the accepted
|
137
|
+
# tradeoff that it makes rescue more difficult (hence the presence of Error.canceled? helper).
|
138
|
+
raise Error::CanceledError, 'Activity canceled before scheduled' if cancellation.canceled?
|
139
|
+
|
140
|
+
# This has to be done in a loop for local activity backoff
|
141
|
+
last_local_backoff = nil
|
142
|
+
loop do
|
143
|
+
result = execute_activity_once(local:, cancellation:, last_local_backoff:, &)
|
144
|
+
return result unless result.is_a?(Bridge::Api::ActivityResult::DoBackoff)
|
145
|
+
|
146
|
+
# @type var result: untyped
|
147
|
+
last_local_backoff = result
|
148
|
+
# Have to sleep the amount of the backoff, which can be canceled with the same cancellation
|
149
|
+
# TODO(cretz): What should this cancellation raise?
|
150
|
+
Workflow.sleep(ProtoUtils.duration_to_seconds(result.backoff_duration), cancellation:)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# If this doesn't raise, it returns success | DoBackoff
|
155
|
+
def execute_activity_once(local:, cancellation:, last_local_backoff:, &)
|
156
|
+
# Add to pending activities (removed by the resolver)
|
157
|
+
seq = yield last_local_backoff
|
158
|
+
@instance.pending_activities[seq] = Fiber.current
|
159
|
+
|
160
|
+
# Add cancellation hook
|
161
|
+
cancel_callback_key = cancellation.add_cancel_callback do
|
162
|
+
# Only if the activity is present still
|
163
|
+
if @instance.pending_activities.include?(seq)
|
164
|
+
if local
|
165
|
+
@instance.add_command(
|
166
|
+
Bridge::Api::WorkflowCommands::WorkflowCommand.new(
|
167
|
+
request_cancel_local_activity: Bridge::Api::WorkflowCommands::RequestCancelLocalActivity.new(seq:)
|
168
|
+
)
|
169
|
+
)
|
170
|
+
else
|
171
|
+
@instance.add_command(
|
172
|
+
Bridge::Api::WorkflowCommands::WorkflowCommand.new(
|
173
|
+
request_cancel_activity: Bridge::Api::WorkflowCommands::RequestCancelActivity.new(seq:)
|
174
|
+
)
|
175
|
+
)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Wait
|
181
|
+
resolution = Fiber.yield
|
182
|
+
|
183
|
+
# Remove cancellation callback
|
184
|
+
cancellation.remove_cancel_callback(cancel_callback_key)
|
185
|
+
|
186
|
+
case resolution.status
|
187
|
+
when :completed
|
188
|
+
@instance.payload_converter.from_payload(resolution.completed.result)
|
189
|
+
when :failed
|
190
|
+
raise @instance.failure_converter.from_failure(resolution.failed.failure, @instance.payload_converter)
|
191
|
+
when :cancelled
|
192
|
+
raise @instance.failure_converter.from_failure(resolution.cancelled.failure, @instance.payload_converter)
|
193
|
+
when :backoff
|
194
|
+
resolution.backoff
|
195
|
+
else
|
196
|
+
raise "Unrecognized resolution status: #{resolution.status}"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def initialize_continue_as_new_error(input)
|
201
|
+
# Do nothing
|
202
|
+
end
|
203
|
+
|
204
|
+
def signal_child_workflow(input)
|
205
|
+
_signal_external_workflow(
|
206
|
+
id: input.id,
|
207
|
+
run_id: nil,
|
208
|
+
child: true,
|
209
|
+
signal: input.signal,
|
210
|
+
args: input.args,
|
211
|
+
cancellation: input.cancellation,
|
212
|
+
headers: input.headers
|
213
|
+
)
|
214
|
+
end
|
215
|
+
|
216
|
+
def signal_external_workflow(input)
|
217
|
+
_signal_external_workflow(
|
218
|
+
id: input.id,
|
219
|
+
run_id: input.run_id,
|
220
|
+
child: false,
|
221
|
+
signal: input.signal,
|
222
|
+
args: input.args,
|
223
|
+
cancellation: input.cancellation,
|
224
|
+
headers: input.headers
|
225
|
+
)
|
226
|
+
end
|
227
|
+
|
228
|
+
def _signal_external_workflow(id:, run_id:, child:, signal:, args:, cancellation:, headers:)
|
229
|
+
raise Error::CanceledError, 'Signal canceled before scheduled' if cancellation.canceled?
|
230
|
+
|
231
|
+
# Add command
|
232
|
+
seq = (@external_signal_counter += 1)
|
233
|
+
cmd = Bridge::Api::WorkflowCommands::SignalExternalWorkflowExecution.new(
|
234
|
+
seq:,
|
235
|
+
signal_name: Workflow::Definition::Signal._name_from_parameter(signal),
|
236
|
+
args: ProtoUtils.convert_to_payload_array(@instance.payload_converter, args),
|
237
|
+
headers: ProtoUtils.headers_to_proto_hash(headers, @instance.payload_converter)
|
238
|
+
)
|
239
|
+
if child
|
240
|
+
cmd.child_workflow_id = id
|
241
|
+
else
|
242
|
+
cmd.workflow_execution = Bridge::Api::Common::NamespacedWorkflowExecution.new(
|
243
|
+
namespace: @instance.info.namespace,
|
244
|
+
workflow_id: id,
|
245
|
+
run_id:
|
246
|
+
)
|
247
|
+
end
|
248
|
+
@instance.add_command(
|
249
|
+
Bridge::Api::WorkflowCommands::WorkflowCommand.new(signal_external_workflow_execution: cmd)
|
250
|
+
)
|
251
|
+
@instance.pending_external_signals[seq] = Fiber.current
|
252
|
+
|
253
|
+
# Add a cancellation callback
|
254
|
+
cancel_callback_key = cancellation.add_cancel_callback do
|
255
|
+
# Add the command but do not raise, we will let resolution do that
|
256
|
+
@instance.add_command(
|
257
|
+
Bridge::Api::WorkflowCommands::WorkflowCommand.new(
|
258
|
+
cancel_signal_workflow: Bridge::Api::WorkflowCommands::CancelSignalWorkflow.new(seq:)
|
259
|
+
)
|
260
|
+
)
|
261
|
+
end
|
262
|
+
|
263
|
+
# Wait
|
264
|
+
resolution = Fiber.yield
|
265
|
+
|
266
|
+
# Remove cancellation callback
|
267
|
+
cancellation.remove_cancel_callback(cancel_callback_key)
|
268
|
+
|
269
|
+
# Raise if resolution has failure
|
270
|
+
return unless resolution.failure
|
271
|
+
|
272
|
+
raise @instance.failure_converter.from_failure(resolution.failure, @instance.payload_converter)
|
273
|
+
end
|
274
|
+
|
275
|
+
def sleep(input)
|
276
|
+
# If already cancelled, raise as such
|
277
|
+
if input.cancellation.canceled?
|
278
|
+
raise Error::CanceledError,
|
279
|
+
input.cancellation.canceled_reason || 'Timer canceled before started'
|
280
|
+
end
|
281
|
+
|
282
|
+
# Disallow negative durations
|
283
|
+
raise ArgumentError, 'Sleep duration cannot be less than 0' if input.duration&.negative?
|
284
|
+
|
285
|
+
# If the duration is infinite, just wait for cancellation
|
286
|
+
if input.duration.nil?
|
287
|
+
input.cancellation.wait
|
288
|
+
raise Error::CanceledError, input.cancellation.canceled_reason || 'Timer canceled'
|
289
|
+
end
|
290
|
+
|
291
|
+
# If duration is zero, we make it one millisecond. It was decided a 0 duration still makes a timer to ensure
|
292
|
+
# determinism if a timer's duration is altered from non-zero to zero or vice versa.
|
293
|
+
duration = input.duration
|
294
|
+
duration = 0.001 if duration.zero?
|
295
|
+
|
296
|
+
# Add command
|
297
|
+
seq = (@timer_counter += 1)
|
298
|
+
@instance.add_command(
|
299
|
+
Bridge::Api::WorkflowCommands::WorkflowCommand.new(
|
300
|
+
start_timer: Bridge::Api::WorkflowCommands::StartTimer.new(
|
301
|
+
seq:,
|
302
|
+
start_to_fire_timeout: ProtoUtils.seconds_to_duration(duration)
|
303
|
+
)
|
304
|
+
)
|
305
|
+
)
|
306
|
+
@instance.pending_timers[seq] = Fiber.current
|
307
|
+
|
308
|
+
# Add a cancellation callback
|
309
|
+
cancel_callback_key = input.cancellation.add_cancel_callback do
|
310
|
+
# Only if the timer is still present
|
311
|
+
fiber = @instance.pending_timers.delete(seq)
|
312
|
+
if fiber
|
313
|
+
# Add the command for cancel then raise
|
314
|
+
@instance.add_command(
|
315
|
+
Bridge::Api::WorkflowCommands::WorkflowCommand.new(
|
316
|
+
cancel_timer: Bridge::Api::WorkflowCommands::CancelTimer.new(seq:)
|
317
|
+
)
|
318
|
+
)
|
319
|
+
if fiber.alive?
|
320
|
+
fiber.raise(Error::CanceledError.new(input.cancellation.canceled_reason || 'Timer canceled'))
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# Wait
|
326
|
+
Fiber.yield
|
327
|
+
|
328
|
+
# Remove cancellation callback (only needed on success)
|
329
|
+
input.cancellation.remove_cancel_callback(cancel_callback_key)
|
330
|
+
end
|
331
|
+
|
332
|
+
def start_child_workflow(input)
|
333
|
+
raise Error::CanceledError, 'Child canceled before scheduled' if input.cancellation.canceled?
|
334
|
+
|
335
|
+
# Add the command
|
336
|
+
seq = (@child_counter += 1)
|
337
|
+
@instance.add_command(
|
338
|
+
Bridge::Api::WorkflowCommands::WorkflowCommand.new(
|
339
|
+
start_child_workflow_execution: Bridge::Api::WorkflowCommands::StartChildWorkflowExecution.new(
|
340
|
+
seq:,
|
341
|
+
namespace: @instance.info.namespace,
|
342
|
+
workflow_id: input.id,
|
343
|
+
workflow_type: Workflow::Definition._workflow_type_from_workflow_parameter(input.workflow),
|
344
|
+
task_queue: input.task_queue,
|
345
|
+
input: ProtoUtils.convert_to_payload_array(@instance.payload_converter, input.args),
|
346
|
+
workflow_execution_timeout: ProtoUtils.seconds_to_duration(input.execution_timeout),
|
347
|
+
workflow_run_timeout: ProtoUtils.seconds_to_duration(input.run_timeout),
|
348
|
+
workflow_task_timeout: ProtoUtils.seconds_to_duration(input.task_timeout),
|
349
|
+
parent_close_policy: input.parent_close_policy,
|
350
|
+
workflow_id_reuse_policy: input.id_reuse_policy,
|
351
|
+
retry_policy: input.retry_policy&._to_proto,
|
352
|
+
cron_schedule: input.cron_schedule,
|
353
|
+
headers: ProtoUtils.headers_to_proto_hash(input.headers, @instance.payload_converter),
|
354
|
+
memo: ProtoUtils.memo_to_proto_hash(input.memo, @instance.payload_converter),
|
355
|
+
search_attributes: input.search_attributes&._to_proto_hash,
|
356
|
+
cancellation_type: input.cancellation_type
|
357
|
+
)
|
358
|
+
)
|
359
|
+
)
|
360
|
+
|
361
|
+
# Set as pending start and register cancel callback
|
362
|
+
@instance.pending_child_workflow_starts[seq] = Fiber.current
|
363
|
+
cancel_callback_key = input.cancellation.add_cancel_callback do
|
364
|
+
# Send cancel if in start or pending
|
365
|
+
if @instance.pending_child_workflow_starts.include?(seq) ||
|
366
|
+
@instance.pending_child_workflows.include?(seq)
|
367
|
+
@instance.add_command(
|
368
|
+
Bridge::Api::WorkflowCommands::WorkflowCommand.new(
|
369
|
+
cancel_child_workflow_execution: Bridge::Api::WorkflowCommands::CancelChildWorkflowExecution.new(
|
370
|
+
child_workflow_seq: seq
|
371
|
+
)
|
372
|
+
)
|
373
|
+
)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
# Wait for start
|
378
|
+
resolution = Fiber.yield
|
379
|
+
|
380
|
+
case resolution.status
|
381
|
+
when :succeeded
|
382
|
+
# Create handle, passing along the cancel callback key, and set it as pending
|
383
|
+
handle = ChildWorkflowHandle.new(
|
384
|
+
id: input.id,
|
385
|
+
first_execution_run_id: resolution.succeeded.run_id,
|
386
|
+
instance: @instance,
|
387
|
+
cancellation: input.cancellation,
|
388
|
+
cancel_callback_key:
|
389
|
+
)
|
390
|
+
@instance.pending_child_workflows[seq] = handle
|
391
|
+
handle
|
392
|
+
when :failed
|
393
|
+
# Remove cancel callback and handle failure
|
394
|
+
input.cancellation.remove_cancel_callback(cancel_callback_key)
|
395
|
+
if resolution.failed.cause == :START_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_WORKFLOW_ALREADY_EXISTS
|
396
|
+
raise Error::WorkflowAlreadyStartedError.new(
|
397
|
+
workflow_id: resolution.failed.workflow_id,
|
398
|
+
workflow_type: resolution.failed.workflow_type,
|
399
|
+
run_id: nil
|
400
|
+
)
|
401
|
+
end
|
402
|
+
raise "Unknown child start fail cause: #{resolution.failed.cause}"
|
403
|
+
when :cancelled
|
404
|
+
# Remove cancel callback and handle cancel
|
405
|
+
input.cancellation.remove_cancel_callback(cancel_callback_key)
|
406
|
+
raise @instance.failure_converter.from_failure(resolution.cancelled.failure, @instance.payload_converter)
|
407
|
+
else
|
408
|
+
raise "Unknown resolution status: #{resolution.status}"
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'temporalio/scoped_logger'
|
4
|
+
require 'temporalio/workflow'
|
5
|
+
|
6
|
+
module Temporalio
|
7
|
+
module Internal
|
8
|
+
module Worker
|
9
|
+
class WorkflowInstance
|
10
|
+
# Wrapper for a scoped logger that does not log on replay.
|
11
|
+
class ReplaySafeLogger < ScopedLogger
|
12
|
+
def initialize(logger:, instance:)
|
13
|
+
@instance = instance
|
14
|
+
@replay_safety_disabled = false
|
15
|
+
super(logger)
|
16
|
+
end
|
17
|
+
|
18
|
+
def replay_safety_disabled(&)
|
19
|
+
@replay_safety_disabled = true
|
20
|
+
yield
|
21
|
+
ensure
|
22
|
+
@replay_safety_disabled = false
|
23
|
+
end
|
24
|
+
|
25
|
+
def add(...)
|
26
|
+
if !@replay_safety_disabled && Temporalio::Workflow.in_workflow? && Temporalio::Workflow::Unsafe.replaying?
|
27
|
+
return true
|
28
|
+
end
|
29
|
+
|
30
|
+
# Disable illegal call tracing for the log call
|
31
|
+
@instance.illegal_call_tracing_disabled { super }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|