clara-temporalio 0.4.3-arm64-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 +7 -0
- data/.yardopts +2 -0
- data/Gemfile +27 -0
- data/Rakefile +101 -0
- data/lib/temporalio/activity/cancellation_details.rb +58 -0
- data/lib/temporalio/activity/complete_async_error.rb +11 -0
- data/lib/temporalio/activity/context.rb +131 -0
- data/lib/temporalio/activity/definition.rb +197 -0
- data/lib/temporalio/activity/info.rb +70 -0
- data/lib/temporalio/activity.rb +14 -0
- data/lib/temporalio/api/activity/v1/message.rb +25 -0
- data/lib/temporalio/api/batch/v1/message.rb +38 -0
- data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
- data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +135 -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 +46 -0
- data/lib/temporalio/api/cloud/namespace/v1/message.rb +46 -0
- data/lib/temporalio/api/cloud/nexus/v1/message.rb +32 -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 +49 -0
- data/lib/temporalio/api/deployment/v1/message.rb +39 -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 +28 -0
- data/lib/temporalio/api/enums/v1/deployment.rb +23 -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/nexus.rb +21 -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 +31 -0
- data/lib/temporalio/api/errordetails/v1/message.rb +44 -0
- data/lib/temporalio/api/export/v1/message.rb +24 -0
- data/lib/temporalio/api/failure/v1/message.rb +38 -0
- data/lib/temporalio/api/filter/v1/message.rb +27 -0
- data/lib/temporalio/api/history/v1/message.rb +94 -0
- data/lib/temporalio/api/namespace/v1/message.rb +31 -0
- data/lib/temporalio/api/nexus/v1/message.rb +41 -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 +1668 -0
- data/lib/temporalio/api/protocol/v1/message.rb +23 -0
- data/lib/temporalio/api/query/v1/message.rb +28 -0
- data/lib/temporalio/api/replication/v1/message.rb +26 -0
- data/lib/temporalio/api/rules/v1/message.rb +27 -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 +48 -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 +63 -0
- data/lib/temporalio/api/workflowservice/v1/request_response.rb +244 -0
- data/lib/temporalio/api/workflowservice/v1/service.rb +23 -0
- data/lib/temporalio/api/workflowservice.rb +3 -0
- data/lib/temporalio/api.rb +15 -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 +786 -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 +1326 -0
- data/lib/temporalio/client/connection.rb +316 -0
- data/lib/temporalio/client/interceptor.rb +457 -0
- data/lib/temporalio/client/schedule.rb +991 -0
- data/lib/temporalio/client/schedule_handle.rb +126 -0
- data/lib/temporalio/client/with_start_workflow_operation.rb +115 -0
- data/lib/temporalio/client/workflow_execution.rb +119 -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 +625 -0
- data/lib/temporalio/common_enums.rb +55 -0
- data/lib/temporalio/contrib/open_telemetry.rb +469 -0
- data/lib/temporalio/converters/data_converter.rb +99 -0
- data/lib/temporalio/converters/failure_converter.rb +205 -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 +237 -0
- data/lib/temporalio/error.rb +156 -0
- 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.4/temporalio_bridge.bundle +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 +32 -0
- data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +33 -0
- data/lib/temporalio/internal/bridge/api/common/common.rb +27 -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 +34 -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 +58 -0
- data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +31 -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 +56 -0
- data/lib/temporalio/internal/bridge/testing.rb +69 -0
- data/lib/temporalio/internal/bridge/worker.rb +109 -0
- data/lib/temporalio/internal/bridge.rb +36 -0
- data/lib/temporalio/internal/client/implementation.rb +926 -0
- data/lib/temporalio/internal/metric.rb +122 -0
- data/lib/temporalio/internal/proto_utils.rb +165 -0
- data/lib/temporalio/internal/worker/activity_worker.rb +448 -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 +391 -0
- data/lib/temporalio/internal/worker/workflow_instance/details.rb +49 -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 +404 -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 +183 -0
- data/lib/temporalio/internal/worker/workflow_instance.rb +800 -0
- data/lib/temporalio/internal/worker/workflow_worker.rb +249 -0
- data/lib/temporalio/internal.rb +7 -0
- data/lib/temporalio/metric.rb +109 -0
- data/lib/temporalio/priority.rb +59 -0
- data/lib/temporalio/retry_policy.rb +74 -0
- data/lib/temporalio/runtime/metric_buffer.rb +94 -0
- data/lib/temporalio/runtime.rb +352 -0
- data/lib/temporalio/scoped_logger.rb +96 -0
- data/lib/temporalio/search_attributes.rb +356 -0
- data/lib/temporalio/testing/activity_environment.rb +175 -0
- data/lib/temporalio/testing/workflow_environment.rb +406 -0
- data/lib/temporalio/testing.rb +10 -0
- data/lib/temporalio/version.rb +5 -0
- data/lib/temporalio/versioning_override.rb +55 -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/deployment_options.rb +45 -0
- data/lib/temporalio/worker/interceptor.rb +367 -0
- data/lib/temporalio/worker/poller_behavior.rb +61 -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 +236 -0
- data/lib/temporalio/worker/workflow_executor.rb +26 -0
- data/lib/temporalio/worker/workflow_replayer.rb +349 -0
- data/lib/temporalio/worker.rb +633 -0
- data/lib/temporalio/worker_deployment_version.rb +67 -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 +680 -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 +107 -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 +594 -0
- data/lib/temporalio/workflow_history.rb +47 -0
- data/lib/temporalio.rb +12 -0
- data/temporalio.gemspec +31 -0
- metadata +267 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'temporalio/activity'
|
|
4
|
+
require 'temporalio/activity/definition'
|
|
5
|
+
require 'temporalio/cancellation'
|
|
6
|
+
require 'temporalio/converters/raw_value'
|
|
7
|
+
require 'temporalio/internal/bridge/api'
|
|
8
|
+
require 'temporalio/internal/proto_utils'
|
|
9
|
+
require 'temporalio/scoped_logger'
|
|
10
|
+
require 'temporalio/worker/interceptor'
|
|
11
|
+
|
|
12
|
+
module Temporalio
|
|
13
|
+
module Internal
|
|
14
|
+
module Worker
|
|
15
|
+
# Worker for handling activity tasks. Upon overarching worker shutdown, {wait_all_complete} should be used to wait
|
|
16
|
+
# for the activities to complete.
|
|
17
|
+
class ActivityWorker
|
|
18
|
+
LOG_TASKS = false
|
|
19
|
+
|
|
20
|
+
attr_reader :worker, :bridge_worker
|
|
21
|
+
|
|
22
|
+
def initialize(worker:, bridge_worker:)
|
|
23
|
+
@worker = worker
|
|
24
|
+
@bridge_worker = bridge_worker
|
|
25
|
+
@runtime_metric_meter = worker.options.client.connection.options.runtime.metric_meter
|
|
26
|
+
|
|
27
|
+
# Create shared logger that gives scoped activity details
|
|
28
|
+
@scoped_logger = ScopedLogger.new(@worker.options.logger)
|
|
29
|
+
@scoped_logger.scoped_values_getter = proc {
|
|
30
|
+
Activity::Context.current_or_nil&._scoped_logger_info
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# Build up activity hash by name (can be nil for dynamic), failing if any fail validation
|
|
34
|
+
@activities = worker.options.activities.each_with_object({}) do |act, hash|
|
|
35
|
+
# Class means create each time, instance means just call, definition
|
|
36
|
+
# does nothing special
|
|
37
|
+
defn = Activity::Definition::Info.from_activity(act)
|
|
38
|
+
# Confirm name not in use
|
|
39
|
+
raise ArgumentError, 'Only one dynamic activity allowed' if !defn.name && hash.key?(defn.name)
|
|
40
|
+
raise ArgumentError, "Multiple activities named #{defn.name}" if hash.key?(defn.name)
|
|
41
|
+
|
|
42
|
+
# Confirm executor is a known executor and let it initialize
|
|
43
|
+
executor = worker.options.activity_executors[defn.executor]
|
|
44
|
+
raise ArgumentError, "Unknown executor '#{defn.executor}'" if executor.nil?
|
|
45
|
+
|
|
46
|
+
executor.initialize_activity(defn)
|
|
47
|
+
|
|
48
|
+
hash[defn.name] = defn
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Need mutex for the rest of these
|
|
52
|
+
@running_activities_mutex = Mutex.new
|
|
53
|
+
@running_activities = {}
|
|
54
|
+
@running_activities_empty_condvar = ConditionVariable.new
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def set_running_activity(task_token, activity)
|
|
58
|
+
@running_activities_mutex.synchronize do
|
|
59
|
+
@running_activities[task_token] = activity
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def get_running_activity(task_token)
|
|
64
|
+
@running_activities_mutex.synchronize do
|
|
65
|
+
@running_activities[task_token]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def remove_running_activity(task_token)
|
|
70
|
+
@running_activities_mutex.synchronize do
|
|
71
|
+
@running_activities.delete(task_token)
|
|
72
|
+
@running_activities_empty_condvar.broadcast if @running_activities.empty?
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def wait_all_complete
|
|
77
|
+
@running_activities_mutex.synchronize do
|
|
78
|
+
@running_activities_empty_condvar.wait(@running_activities_mutex) until @running_activities.empty?
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def handle_task(task)
|
|
83
|
+
@scoped_logger.debug("Received activity task: #{task}") if LOG_TASKS
|
|
84
|
+
if !task.start.nil?
|
|
85
|
+
handle_start_task(task.task_token, task.start)
|
|
86
|
+
elsif !task.cancel.nil?
|
|
87
|
+
handle_cancel_task(task.task_token, task.cancel)
|
|
88
|
+
else
|
|
89
|
+
raise "Unrecognized activity task: #{task}"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def handle_start_task(task_token, start)
|
|
94
|
+
set_running_activity(task_token, nil)
|
|
95
|
+
|
|
96
|
+
# Find activity definition, falling back to dynamic if not found and not reserved name
|
|
97
|
+
defn = @activities[start.activity_type]
|
|
98
|
+
defn = @activities[nil] if !defn && !Internal::ProtoUtils.reserved_name?(start.activity_type)
|
|
99
|
+
|
|
100
|
+
if defn.nil?
|
|
101
|
+
raise Error::ApplicationError.new(
|
|
102
|
+
"Activity #{start.activity_type} for workflow #{start.workflow_execution.workflow_id} " \
|
|
103
|
+
"is not registered on this worker, available activities: #{@activities.keys.sort.join(', ')}",
|
|
104
|
+
type: 'NotFoundError'
|
|
105
|
+
)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Run everything else in the excecutor
|
|
109
|
+
executor = @worker.options.activity_executors[defn.executor]
|
|
110
|
+
executor.execute_activity(defn) do
|
|
111
|
+
# Set current executor
|
|
112
|
+
Activity::Context._current_executor = executor
|
|
113
|
+
# Execute with error handling
|
|
114
|
+
execute_activity(task_token, defn, start)
|
|
115
|
+
ensure
|
|
116
|
+
# Unset at the end
|
|
117
|
+
Activity::Context._current_executor = nil
|
|
118
|
+
end
|
|
119
|
+
rescue Exception => e # rubocop:disable Lint/RescueException -- We are intending to catch everything here
|
|
120
|
+
remove_running_activity(task_token)
|
|
121
|
+
@scoped_logger.warn("Failed starting activity #{start.activity_type}")
|
|
122
|
+
@scoped_logger.warn(e)
|
|
123
|
+
|
|
124
|
+
# We need to complete the activity task as failed, but this is on the
|
|
125
|
+
# hot path for polling, so we want to complete it in the background
|
|
126
|
+
begin
|
|
127
|
+
@bridge_worker.complete_activity_task_in_background(
|
|
128
|
+
Bridge::Api::CoreInterface::ActivityTaskCompletion.new(
|
|
129
|
+
task_token:,
|
|
130
|
+
result: Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
|
131
|
+
failed: Bridge::Api::ActivityResult::Failure.new(
|
|
132
|
+
# TODO(cretz): If failure conversion does slow failure
|
|
133
|
+
# encoding, it can gum up the system
|
|
134
|
+
failure: @worker.options.client.data_converter.to_failure(e)
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
rescue StandardError => e_inner
|
|
140
|
+
@scoped_logger.error("Failed building start failure to return for #{start.activity_type}")
|
|
141
|
+
@scoped_logger.error(e_inner)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def handle_cancel_task(task_token, cancel)
|
|
146
|
+
activity = get_running_activity(task_token)
|
|
147
|
+
if activity.nil?
|
|
148
|
+
@scoped_logger.warn("Cannot find activity to cancel for token #{task_token}")
|
|
149
|
+
return
|
|
150
|
+
end
|
|
151
|
+
begin
|
|
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
|
+
)
|
|
163
|
+
rescue StandardError => e
|
|
164
|
+
@scoped_logger.warn("Failed cancelling activity #{activity.info.activity_type} \
|
|
165
|
+
with ID #{activity.info.activity_id}")
|
|
166
|
+
@scoped_logger.warn(e)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def execute_activity(task_token, defn, start)
|
|
171
|
+
# Build info
|
|
172
|
+
info = Activity::Info.new(
|
|
173
|
+
activity_id: start.activity_id,
|
|
174
|
+
activity_type: start.activity_type,
|
|
175
|
+
attempt: start.attempt,
|
|
176
|
+
current_attempt_scheduled_time: Internal::ProtoUtils.timestamp_to_time(
|
|
177
|
+
start.current_attempt_scheduled_time
|
|
178
|
+
) || raise, # Never nil
|
|
179
|
+
heartbeat_details: ProtoUtils.convert_from_payload_array(
|
|
180
|
+
@worker.options.client.data_converter,
|
|
181
|
+
start.heartbeat_details.to_ary
|
|
182
|
+
),
|
|
183
|
+
heartbeat_timeout: Internal::ProtoUtils.duration_to_seconds(start.heartbeat_timeout),
|
|
184
|
+
local?: start.is_local,
|
|
185
|
+
priority: Priority._from_proto(start.priority),
|
|
186
|
+
schedule_to_close_timeout: Internal::ProtoUtils.duration_to_seconds(start.schedule_to_close_timeout),
|
|
187
|
+
scheduled_time: Internal::ProtoUtils.timestamp_to_time(start.scheduled_time) || raise, # Never nil
|
|
188
|
+
start_to_close_timeout: Internal::ProtoUtils.duration_to_seconds(start.start_to_close_timeout),
|
|
189
|
+
started_time: Internal::ProtoUtils.timestamp_to_time(start.started_time) || raise, # Never nil
|
|
190
|
+
task_queue: @worker.options.task_queue,
|
|
191
|
+
task_token:,
|
|
192
|
+
workflow_id: start.workflow_execution.workflow_id,
|
|
193
|
+
workflow_namespace: start.workflow_namespace,
|
|
194
|
+
workflow_run_id: start.workflow_execution.run_id,
|
|
195
|
+
workflow_type: start.workflow_type
|
|
196
|
+
).freeze
|
|
197
|
+
|
|
198
|
+
# Build input
|
|
199
|
+
input = Temporalio::Worker::Interceptor::Activity::ExecuteInput.new(
|
|
200
|
+
proc: defn.proc,
|
|
201
|
+
# If the activity wants raw_args, we only decode we don't convert
|
|
202
|
+
args: if defn.raw_args
|
|
203
|
+
payloads = start.input.to_ary
|
|
204
|
+
codec = @worker.options.client.data_converter.payload_codec
|
|
205
|
+
payloads = codec.decode(payloads) if codec
|
|
206
|
+
payloads.map { |p| Temporalio::Converters::RawValue.new(p) }
|
|
207
|
+
else
|
|
208
|
+
ProtoUtils.convert_from_payload_array(@worker.options.client.data_converter, start.input.to_ary)
|
|
209
|
+
end,
|
|
210
|
+
headers: ProtoUtils.headers_from_proto_map(start.header_fields, @worker.options.client.data_converter) || {}
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Run
|
|
214
|
+
activity = RunningActivity.new(
|
|
215
|
+
worker: @worker,
|
|
216
|
+
info:,
|
|
217
|
+
cancellation: Cancellation.new,
|
|
218
|
+
worker_shutdown_cancellation: @worker._worker_shutdown_cancellation,
|
|
219
|
+
payload_converter: @worker.options.client.data_converter.payload_converter,
|
|
220
|
+
logger: @scoped_logger,
|
|
221
|
+
runtime_metric_meter: @runtime_metric_meter
|
|
222
|
+
)
|
|
223
|
+
Activity::Context._current_executor&.set_activity_context(defn, activity)
|
|
224
|
+
set_running_activity(task_token, activity)
|
|
225
|
+
run_activity(defn, activity, input)
|
|
226
|
+
rescue Exception => e # rubocop:disable Lint/RescueException -- We are intending to catch everything here
|
|
227
|
+
@scoped_logger.warn("Failed starting or sending completion for activity #{start.activity_type}")
|
|
228
|
+
@scoped_logger.warn(e)
|
|
229
|
+
# This means that the activity couldn't start or send completion (run
|
|
230
|
+
# handles its own errors).
|
|
231
|
+
begin
|
|
232
|
+
@bridge_worker.complete_activity_task(
|
|
233
|
+
Bridge::Api::CoreInterface::ActivityTaskCompletion.new(
|
|
234
|
+
task_token:,
|
|
235
|
+
result: Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
|
236
|
+
failed: Bridge::Api::ActivityResult::Failure.new(
|
|
237
|
+
failure: @worker.options.client.data_converter.to_failure(e)
|
|
238
|
+
)
|
|
239
|
+
)
|
|
240
|
+
)
|
|
241
|
+
)
|
|
242
|
+
rescue StandardError => e_inner
|
|
243
|
+
@scoped_logger.error("Failed sending failure for activity #{start.activity_type}")
|
|
244
|
+
@scoped_logger.error(e_inner)
|
|
245
|
+
end
|
|
246
|
+
ensure
|
|
247
|
+
Activity::Context._current_executor&.set_activity_context(defn, nil)
|
|
248
|
+
remove_running_activity(task_token)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def run_activity(defn, activity, input)
|
|
252
|
+
result = begin
|
|
253
|
+
# Create the instance. We choose to do this before interceptors so that it is available in the interceptor.
|
|
254
|
+
activity.instance = defn.instance.is_a?(Proc) ? defn.instance.call : defn.instance # steep:ignore
|
|
255
|
+
|
|
256
|
+
# Build impl with interceptors
|
|
257
|
+
# @type var impl: Temporalio::Worker::Interceptor::Activity::Inbound
|
|
258
|
+
impl = InboundImplementation.new(self)
|
|
259
|
+
impl = @worker._activity_interceptors.reverse_each.reduce(impl) do |acc, int|
|
|
260
|
+
int.intercept_activity(acc)
|
|
261
|
+
end
|
|
262
|
+
impl.init(OutboundImplementation.new(self, activity.info.task_token))
|
|
263
|
+
|
|
264
|
+
# Execute
|
|
265
|
+
result = impl.execute(input)
|
|
266
|
+
|
|
267
|
+
# Success
|
|
268
|
+
Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
|
269
|
+
completed: Bridge::Api::ActivityResult::Success.new(
|
|
270
|
+
result: @worker.options.client.data_converter.to_payload(result)
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
rescue Exception => e # rubocop:disable Lint/RescueException -- We are intending to catch everything here
|
|
274
|
+
if e.is_a?(Activity::CompleteAsyncError)
|
|
275
|
+
# Wanting to complete async
|
|
276
|
+
@scoped_logger.debug('Completing activity asynchronously')
|
|
277
|
+
Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
|
278
|
+
will_complete_async: Bridge::Api::ActivityResult::WillCompleteAsync.new
|
|
279
|
+
)
|
|
280
|
+
elsif e.is_a?(Error::CanceledError) && activity.cancellation_details&.paused?
|
|
281
|
+
# Server requested pause
|
|
282
|
+
@scoped_logger.debug('Completing activity as failed due to exception caused by pause')
|
|
283
|
+
Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
|
284
|
+
failed: Bridge::Api::ActivityResult::Failure.new(
|
|
285
|
+
failure: @worker.options.client.data_converter.to_failure(
|
|
286
|
+
Error._with_backtrace_and_cause(
|
|
287
|
+
Error::ApplicationError.new('Activity paused', type: 'ActivityPause'), backtrace: nil, cause: e
|
|
288
|
+
)
|
|
289
|
+
)
|
|
290
|
+
)
|
|
291
|
+
)
|
|
292
|
+
elsif e.is_a?(Error::CanceledError) && activity.cancellation_details&.reset?
|
|
293
|
+
# Server requested reset
|
|
294
|
+
@scoped_logger.debug('Completing activity as failed due to exception caused by reset')
|
|
295
|
+
Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
|
296
|
+
failed: Bridge::Api::ActivityResult::Failure.new(
|
|
297
|
+
failure: @worker.options.client.data_converter.to_failure(
|
|
298
|
+
Error._with_backtrace_and_cause(
|
|
299
|
+
Error::ApplicationError.new('Activity reset', type: 'ActivityReset'), backtrace: nil, cause: e
|
|
300
|
+
)
|
|
301
|
+
)
|
|
302
|
+
)
|
|
303
|
+
)
|
|
304
|
+
elsif e.is_a?(Error::CanceledError) && activity._server_requested_cancel
|
|
305
|
+
# Server requested cancel
|
|
306
|
+
@scoped_logger.debug('Completing activity as canceled')
|
|
307
|
+
Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
|
308
|
+
cancelled: Bridge::Api::ActivityResult::Cancellation.new(
|
|
309
|
+
failure: @worker.options.client.data_converter.to_failure(e)
|
|
310
|
+
)
|
|
311
|
+
)
|
|
312
|
+
else
|
|
313
|
+
# General failure
|
|
314
|
+
log_level = if e.is_a?(Error::ApplicationError) && e.category == Error::ApplicationError::Category::BENIGN
|
|
315
|
+
Logger::DEBUG
|
|
316
|
+
else
|
|
317
|
+
Logger::WARN
|
|
318
|
+
end
|
|
319
|
+
@scoped_logger.add(log_level, 'Completing activity as failed')
|
|
320
|
+
@scoped_logger.add(log_level, e)
|
|
321
|
+
Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
|
322
|
+
failed: Bridge::Api::ActivityResult::Failure.new(
|
|
323
|
+
failure: @worker.options.client.data_converter.to_failure(e)
|
|
324
|
+
)
|
|
325
|
+
)
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
@scoped_logger.debug("Sending activity completion: #{result}") if LOG_TASKS
|
|
330
|
+
@bridge_worker.complete_activity_task(
|
|
331
|
+
Bridge::Api::CoreInterface::ActivityTaskCompletion.new(
|
|
332
|
+
task_token: activity.info.task_token,
|
|
333
|
+
result:
|
|
334
|
+
)
|
|
335
|
+
)
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def assert_valid_activity(activity)
|
|
339
|
+
defn = @activities[activity]
|
|
340
|
+
defn = @activities[nil] if !defn && !Internal::ProtoUtils.reserved_name?(activity)
|
|
341
|
+
|
|
342
|
+
return unless defn.nil?
|
|
343
|
+
|
|
344
|
+
raise ArgumentError,
|
|
345
|
+
"Activity #{activity} " \
|
|
346
|
+
"is not registered on this worker, available activities: #{@activities.keys.sort.join(', ')}"
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
class RunningActivity < Activity::Context
|
|
350
|
+
attr_reader :info, :cancellation, :cancellation_details, :worker_shutdown_cancellation,
|
|
351
|
+
:payload_converter, :logger, :_server_requested_cancel
|
|
352
|
+
attr_accessor :instance, :_outbound_impl
|
|
353
|
+
|
|
354
|
+
def initialize( # rubocop:disable Lint/MissingSuper
|
|
355
|
+
worker:,
|
|
356
|
+
info:,
|
|
357
|
+
cancellation:,
|
|
358
|
+
worker_shutdown_cancellation:,
|
|
359
|
+
payload_converter:,
|
|
360
|
+
logger:,
|
|
361
|
+
runtime_metric_meter:
|
|
362
|
+
)
|
|
363
|
+
@worker = worker
|
|
364
|
+
@info = info
|
|
365
|
+
@cancellation = cancellation
|
|
366
|
+
@cancellation_details = nil
|
|
367
|
+
@worker_shutdown_cancellation = worker_shutdown_cancellation
|
|
368
|
+
@payload_converter = payload_converter
|
|
369
|
+
@logger = logger
|
|
370
|
+
@runtime_metric_meter = runtime_metric_meter
|
|
371
|
+
@_outbound_impl = nil
|
|
372
|
+
@_server_requested_cancel = false
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def heartbeat(*details)
|
|
376
|
+
raise 'Implementation not set yet' if _outbound_impl.nil?
|
|
377
|
+
|
|
378
|
+
# No-op if local
|
|
379
|
+
return if info.local?
|
|
380
|
+
|
|
381
|
+
_outbound_impl.heartbeat(Temporalio::Worker::Interceptor::Activity::HeartbeatInput.new(details:))
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def metric_meter
|
|
385
|
+
@metric_meter ||= @runtime_metric_meter.with_additional_attributes(
|
|
386
|
+
{
|
|
387
|
+
namespace: info.workflow_namespace,
|
|
388
|
+
task_queue: info.task_queue,
|
|
389
|
+
activity_type: info.activity_type
|
|
390
|
+
}
|
|
391
|
+
)
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def client
|
|
395
|
+
@worker.client
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def _cancel(reason:, details:)
|
|
399
|
+
# Do not issue cancel if already canceled
|
|
400
|
+
return if @cancellation_details
|
|
401
|
+
|
|
402
|
+
@_server_requested_cancel = true
|
|
403
|
+
# Set the cancellation details _before_ issuing the cancel itself
|
|
404
|
+
@cancellation_details = details
|
|
405
|
+
_, cancel_proc = cancellation
|
|
406
|
+
cancel_proc.call(reason:)
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
class InboundImplementation < Temporalio::Worker::Interceptor::Activity::Inbound
|
|
411
|
+
def initialize(worker)
|
|
412
|
+
super(nil) # steep:ignore
|
|
413
|
+
@worker = worker
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
def init(outbound)
|
|
417
|
+
context = Activity::Context.current
|
|
418
|
+
raise 'Unexpected context type' unless context.is_a?(RunningActivity)
|
|
419
|
+
|
|
420
|
+
context._outbound_impl = outbound
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def execute(input)
|
|
424
|
+
input.proc.call(*input.args)
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
class OutboundImplementation < Temporalio::Worker::Interceptor::Activity::Outbound
|
|
429
|
+
def initialize(worker, task_token)
|
|
430
|
+
super(nil) # steep:ignore
|
|
431
|
+
@worker = worker
|
|
432
|
+
@task_token = task_token
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def heartbeat(input)
|
|
436
|
+
@worker.bridge_worker.record_activity_heartbeat(
|
|
437
|
+
Bridge::Api::CoreInterface::ActivityHeartbeat.new(
|
|
438
|
+
task_token: @task_token,
|
|
439
|
+
details: ProtoUtils.convert_to_payload_array(@worker.worker.options.client.data_converter,
|
|
440
|
+
input.details)
|
|
441
|
+
).to_proto
|
|
442
|
+
)
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
end
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'singleton'
|
|
4
|
+
require 'temporalio/internal/bridge/worker'
|
|
5
|
+
|
|
6
|
+
module Temporalio
|
|
7
|
+
module Internal
|
|
8
|
+
module Worker
|
|
9
|
+
# Primary worker (re)actor-style event handler. This handles multiple workers, receiving events from the bridge,
|
|
10
|
+
# and handling a user block.
|
|
11
|
+
class MultiRunner
|
|
12
|
+
def initialize(workers:, shutdown_signals:)
|
|
13
|
+
@workers = workers
|
|
14
|
+
@queue = Queue.new
|
|
15
|
+
|
|
16
|
+
@shutdown_initiated_mutex = Mutex.new
|
|
17
|
+
@shutdown_initiated = false
|
|
18
|
+
|
|
19
|
+
# Trap signals to push to queue
|
|
20
|
+
shutdown_signals.each do |signal|
|
|
21
|
+
Signal.trap(signal) { @queue.push(Event::ShutdownSignalReceived.new) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Start pollers
|
|
25
|
+
Bridge::Worker.async_poll_all(workers.map(&:_bridge_worker), @queue)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def apply_thread_or_fiber_block(&)
|
|
29
|
+
return unless block_given?
|
|
30
|
+
|
|
31
|
+
@thread_or_fiber = if Fiber.current_scheduler
|
|
32
|
+
Fiber.schedule do
|
|
33
|
+
@queue.push(Event::BlockSuccess.new(result: yield))
|
|
34
|
+
rescue InjectEventForTesting => e
|
|
35
|
+
@queue.push(e.event)
|
|
36
|
+
@queue.push(Event::BlockSuccess.new(result: e))
|
|
37
|
+
rescue Exception => e # rubocop:disable Lint/RescueException -- Intentionally catch all
|
|
38
|
+
@queue.push(Event::BlockFailure.new(error: e))
|
|
39
|
+
end
|
|
40
|
+
else
|
|
41
|
+
Thread.new do
|
|
42
|
+
@queue.push(Event::BlockSuccess.new(result: yield))
|
|
43
|
+
rescue InjectEventForTesting => e
|
|
44
|
+
@queue.push(e.event)
|
|
45
|
+
@queue.push(Event::BlockSuccess.new(result: e))
|
|
46
|
+
rescue Exception => e # rubocop:disable Lint/RescueException -- Intentionally catch all
|
|
47
|
+
@queue.push(Event::BlockFailure.new(error: e))
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def apply_workflow_activation_decoded(workflow_worker:, activation:)
|
|
53
|
+
@queue.push(Event::WorkflowActivationDecoded.new(workflow_worker:, activation:))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def apply_workflow_activation_complete(workflow_worker:, activation_completion:, encoded:)
|
|
57
|
+
@queue.push(Event::WorkflowActivationComplete.new(
|
|
58
|
+
workflow_worker:, activation_completion:, encoded:, completion_complete_queue: @queue
|
|
59
|
+
))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def raise_in_thread_or_fiber_block(error)
|
|
63
|
+
@thread_or_fiber&.raise(error)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Clarify this is the only thread-safe function here
|
|
67
|
+
def initiate_shutdown
|
|
68
|
+
should_call = @shutdown_initiated_mutex.synchronize do
|
|
69
|
+
break false if @shutdown_initiated
|
|
70
|
+
|
|
71
|
+
@shutdown_initiated = true
|
|
72
|
+
end
|
|
73
|
+
return unless should_call
|
|
74
|
+
|
|
75
|
+
@workers.each(&:_initiate_shutdown)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def wait_complete_and_finalize_shutdown
|
|
79
|
+
# Wait for them all to complete
|
|
80
|
+
@workers.each(&:_wait_all_complete)
|
|
81
|
+
|
|
82
|
+
# Finalize them all
|
|
83
|
+
Bridge::Worker.finalize_shutdown_all(@workers.map(&:_bridge_worker))
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Intentionally not an enumerable/enumerator since stop semantics are
|
|
87
|
+
# caller determined
|
|
88
|
+
def next_event
|
|
89
|
+
# Queue value is one of the following:
|
|
90
|
+
# * Event - non-poller event
|
|
91
|
+
# * [worker index, :activity/:workflow, bytes] - poll success
|
|
92
|
+
# * [worker index, :activity/:workflow, error] - poll fail
|
|
93
|
+
# * [worker index, :activity/:workflow, nil] - worker shutdown
|
|
94
|
+
# * [nil, nil, nil] - all pollers done
|
|
95
|
+
# * [-1, run_id_string, error_or_nil] - workflow activation completion complete
|
|
96
|
+
result = @queue.pop
|
|
97
|
+
if result.is_a?(Event)
|
|
98
|
+
result
|
|
99
|
+
else
|
|
100
|
+
first, second, third = result
|
|
101
|
+
if first.nil? || second.nil?
|
|
102
|
+
Event::AllPollersShutDown.instance
|
|
103
|
+
elsif first == -1
|
|
104
|
+
Event::WorkflowActivationCompletionComplete.new(run_id: second, error: third)
|
|
105
|
+
else
|
|
106
|
+
worker = @workers[first]
|
|
107
|
+
case third
|
|
108
|
+
when nil
|
|
109
|
+
Event::PollerShutDown.new(worker:, worker_type: second)
|
|
110
|
+
when Exception
|
|
111
|
+
Event::PollFailure.new(worker:, worker_type: second, error: third)
|
|
112
|
+
else
|
|
113
|
+
Event::PollSuccess.new(worker:, worker_type: second, bytes: third)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
class Event
|
|
120
|
+
class PollSuccess < Event
|
|
121
|
+
attr_reader :worker, :worker_type, :bytes
|
|
122
|
+
|
|
123
|
+
def initialize(worker:, worker_type:, bytes:) # rubocop:disable Lint/MissingSuper
|
|
124
|
+
@worker = worker
|
|
125
|
+
@worker_type = worker_type
|
|
126
|
+
@bytes = bytes
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
class PollFailure < Event
|
|
131
|
+
attr_reader :worker, :worker_type, :error
|
|
132
|
+
|
|
133
|
+
def initialize(worker:, worker_type:, error:) # rubocop:disable Lint/MissingSuper
|
|
134
|
+
@worker = worker
|
|
135
|
+
@worker_type = worker_type
|
|
136
|
+
@error = error
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
class WorkflowActivationDecoded < Event
|
|
141
|
+
attr_reader :workflow_worker, :activation
|
|
142
|
+
|
|
143
|
+
def initialize(workflow_worker:, activation:) # rubocop:disable Lint/MissingSuper
|
|
144
|
+
@workflow_worker = workflow_worker
|
|
145
|
+
@activation = activation
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
class WorkflowActivationComplete < Event
|
|
150
|
+
attr_reader :workflow_worker, :activation_completion, :encoded, :completion_complete_queue
|
|
151
|
+
|
|
152
|
+
def initialize(workflow_worker:, activation_completion:, encoded:, completion_complete_queue:) # rubocop:disable Lint/MissingSuper
|
|
153
|
+
@workflow_worker = workflow_worker
|
|
154
|
+
@activation_completion = activation_completion
|
|
155
|
+
@encoded = encoded
|
|
156
|
+
@completion_complete_queue = completion_complete_queue
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
class WorkflowActivationCompletionComplete < Event
|
|
161
|
+
attr_reader :run_id, :error
|
|
162
|
+
|
|
163
|
+
def initialize(run_id:, error:) # rubocop:disable Lint/MissingSuper
|
|
164
|
+
@run_id = run_id
|
|
165
|
+
@error = error
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
class PollerShutDown < Event
|
|
170
|
+
attr_reader :worker, :worker_type
|
|
171
|
+
|
|
172
|
+
def initialize(worker:, worker_type:) # rubocop:disable Lint/MissingSuper
|
|
173
|
+
@worker = worker
|
|
174
|
+
@worker_type = worker_type
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
class AllPollersShutDown < Event
|
|
179
|
+
include Singleton
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
class BlockSuccess < Event
|
|
183
|
+
attr_reader :result
|
|
184
|
+
|
|
185
|
+
def initialize(result:) # rubocop:disable Lint/MissingSuper
|
|
186
|
+
@result = result
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
class BlockFailure < Event
|
|
191
|
+
attr_reader :error
|
|
192
|
+
|
|
193
|
+
def initialize(error:) # rubocop:disable Lint/MissingSuper
|
|
194
|
+
@error = error
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
class ShutdownSignalReceived < Event
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
class InjectEventForTesting < Temporalio::Error
|
|
203
|
+
attr_reader :event
|
|
204
|
+
|
|
205
|
+
def initialize(event)
|
|
206
|
+
super('Injecting event for testing')
|
|
207
|
+
@event = event
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|