amit-temporalio 0.3.0-aarch64-linux-musl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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/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/temporalio_bridge.so +0 -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 +236 -0
@@ -0,0 +1,189 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'temporalio/internal/bridge/worker'
|
4
|
+
|
5
|
+
module Temporalio
|
6
|
+
class Worker
|
7
|
+
# Worker tuner that allows for dynamic customization of some aspects of worker configuration.
|
8
|
+
class Tuner
|
9
|
+
# Slot supplier used for reserving slots for execution. Currently the only implementations allowed are {Fixed} and
|
10
|
+
# {ResourceBased}.
|
11
|
+
class SlotSupplier
|
12
|
+
# A fixed-size slot supplier that will never issue more than a fixed number of slots.
|
13
|
+
class Fixed < SlotSupplier
|
14
|
+
# @return [Integer] The maximum number of slots that can be issued.
|
15
|
+
attr_reader :slots
|
16
|
+
|
17
|
+
# Create fixed-size slot supplier.
|
18
|
+
#
|
19
|
+
# @param slots [Integer] The maximum number of slots that can be issued.
|
20
|
+
def initialize(slots) # rubocop:disable Lint/MissingSuper
|
21
|
+
@slots = slots
|
22
|
+
end
|
23
|
+
|
24
|
+
# @!visibility private
|
25
|
+
def _to_bridge_options
|
26
|
+
Internal::Bridge::Worker::TunerSlotSupplierOptions.new(
|
27
|
+
fixed_size: slots,
|
28
|
+
resource_based: nil
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# A slot supplier that will dynamically adjust the number of slots based on resource usage.
|
34
|
+
#
|
35
|
+
# @note WARNING: This API is experimental.
|
36
|
+
class ResourceBased < SlotSupplier
|
37
|
+
attr_reader :tuner_options, :slot_options
|
38
|
+
|
39
|
+
# Create a reosurce-based slot supplier.
|
40
|
+
#
|
41
|
+
# @param tuner_options [ResourceBasedTunerOptions] General tuner options.
|
42
|
+
# @param slot_options [ResourceBasedSlotOptions] Slot-supplier-specific tuner options.
|
43
|
+
def initialize(tuner_options:, slot_options:) # rubocop:disable Lint/MissingSuper
|
44
|
+
@tuner_options = tuner_options
|
45
|
+
@slot_options = slot_options
|
46
|
+
end
|
47
|
+
|
48
|
+
# @!visibility private
|
49
|
+
def _to_bridge_options
|
50
|
+
Internal::Bridge::Worker::TunerSlotSupplierOptions.new(
|
51
|
+
fixed_size: nil,
|
52
|
+
resource_based: Internal::Bridge::Worker::TunerResourceBasedSlotSupplierOptions.new(
|
53
|
+
target_mem_usage: tuner_options.target_memory_usage,
|
54
|
+
target_cpu_usage: tuner_options.target_cpu_usage,
|
55
|
+
min_slots: slot_options.min_slots,
|
56
|
+
max_slots: slot_options.max_slots,
|
57
|
+
ramp_throttle: slot_options.ramp_throttle
|
58
|
+
)
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @!visibility private
|
64
|
+
def _to_bridge_options
|
65
|
+
raise ArgumentError, 'Tuner slot suppliers must be instances of Fixed or ResourceBased'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Options for {create_resource_based} or {SlotSupplier::ResourceBased}.
|
70
|
+
#
|
71
|
+
# @!attribute target_memory_usage
|
72
|
+
# @return [Float] A value between 0 and 1 that represents the target (system) memory usage. It's not recommended
|
73
|
+
# to set this higher than 0.8, since how much memory a workflow may use is not predictable, and you don't want
|
74
|
+
# to encounter OOM errors.
|
75
|
+
# @!attribute target_cpu_usage
|
76
|
+
# @return [Float] A value between 0 and 1 that represents the target (system) CPU usage. This can be set to 1.0
|
77
|
+
# if desired, but it's recommended to leave some headroom for other processes.
|
78
|
+
ResourceBasedTunerOptions = Struct.new(
|
79
|
+
:target_memory_usage,
|
80
|
+
:target_cpu_usage,
|
81
|
+
keyword_init: true
|
82
|
+
)
|
83
|
+
|
84
|
+
# Options for a specific slot type being used with {SlotSupplier::ResourceBased}.
|
85
|
+
#
|
86
|
+
# @!attribute min_slots
|
87
|
+
# @return [Integer, nil] Amount of slots that will be issued regardless of any other checks. Defaults to 5 for
|
88
|
+
# workflows and 1 for activities.
|
89
|
+
# @!attribute max_slots
|
90
|
+
# @return [Integer, nil] Maximum amount of slots permitted. Defaults to 500.
|
91
|
+
# @!attribute ramp_throttle
|
92
|
+
# @return [Float, nil] Minimum time we will wait (after passing the minimum slots number) between handing out
|
93
|
+
# new slots in seconds. Defaults to 0 for workflows and 0.05 for activities.
|
94
|
+
#
|
95
|
+
# This value matters because how many resources a task will use cannot be determined ahead of time, and thus
|
96
|
+
# the system should wait to see how much resources are used before issuing more slots.
|
97
|
+
ResourceBasedSlotOptions = Struct.new(
|
98
|
+
:min_slots,
|
99
|
+
:max_slots,
|
100
|
+
:ramp_throttle,
|
101
|
+
keyword_init: true
|
102
|
+
)
|
103
|
+
|
104
|
+
# Create a fixed-size tuner with the provided number of slots.
|
105
|
+
#
|
106
|
+
# @param workflow_slots [Integer] Maximum number of workflow task slots.
|
107
|
+
# @param activity_slots [Integer] Maximum number of activity slots.
|
108
|
+
# @param local_activity_slots [Integer] Maximum number of local activity slots.
|
109
|
+
# @return [Tuner] Created tuner.
|
110
|
+
def self.create_fixed(
|
111
|
+
workflow_slots: 100,
|
112
|
+
activity_slots: 100,
|
113
|
+
local_activity_slots: 100
|
114
|
+
)
|
115
|
+
new(
|
116
|
+
workflow_slot_supplier: SlotSupplier::Fixed.new(workflow_slots),
|
117
|
+
activity_slot_supplier: SlotSupplier::Fixed.new(activity_slots),
|
118
|
+
local_activity_slot_supplier: SlotSupplier::Fixed.new(local_activity_slots)
|
119
|
+
)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Create a resource-based tuner with the provided options.
|
123
|
+
#
|
124
|
+
# @param target_memory_usage [Float] A value between 0 and 1 that represents the target (system) memory usage.
|
125
|
+
# It's not recommended to set this higher than 0.8, since how much memory a workflow may use is not predictable,
|
126
|
+
# and you don't want to encounter OOM errors.
|
127
|
+
# @param target_cpu_usage [Float] A value between 0 and 1 that represents the target (system) CPU usage. This can
|
128
|
+
# be set to 1.0 if desired, but it's recommended to leave some headroom for other processes.
|
129
|
+
# @param workflow_options [ResourceBasedSlotOptions] Resource-based options for workflow slot supplier.
|
130
|
+
# @param activity_options [ResourceBasedSlotOptions] Resource-based options for activity slot supplier.
|
131
|
+
# @param local_activity_options [ResourceBasedSlotOptions] Resource-based options for local activity slot
|
132
|
+
# supplier.
|
133
|
+
# @return [Tuner] Created tuner.
|
134
|
+
def self.create_resource_based(
|
135
|
+
target_memory_usage:,
|
136
|
+
target_cpu_usage:,
|
137
|
+
workflow_options: ResourceBasedSlotOptions.new(min_slots: 5, max_slots: 500, ramp_throttle: 0.0),
|
138
|
+
activity_options: ResourceBasedSlotOptions.new(min_slots: 1, max_slots: 500, ramp_throttle: 0.05),
|
139
|
+
local_activity_options: ResourceBasedSlotOptions.new(min_slots: 1, max_slots: 500, ramp_throttle: 0.05)
|
140
|
+
)
|
141
|
+
tuner_options = ResourceBasedTunerOptions.new(target_memory_usage:, target_cpu_usage:)
|
142
|
+
new(
|
143
|
+
workflow_slot_supplier: SlotSupplier::ResourceBased.new(
|
144
|
+
tuner_options:, slot_options: workflow_options
|
145
|
+
),
|
146
|
+
activity_slot_supplier: SlotSupplier::ResourceBased.new(
|
147
|
+
tuner_options:, slot_options: activity_options
|
148
|
+
),
|
149
|
+
local_activity_slot_supplier: SlotSupplier::ResourceBased.new(
|
150
|
+
tuner_options:, slot_options: local_activity_options
|
151
|
+
)
|
152
|
+
)
|
153
|
+
end
|
154
|
+
|
155
|
+
# @return [SlotSupplier] Slot supplier for workflows.
|
156
|
+
attr_reader :workflow_slot_supplier
|
157
|
+
|
158
|
+
# @return [SlotSupplier] Slot supplier for activities.
|
159
|
+
attr_reader :activity_slot_supplier
|
160
|
+
|
161
|
+
# @return [SlotSupplier] Slot supplier for local activities.
|
162
|
+
attr_reader :local_activity_slot_supplier
|
163
|
+
|
164
|
+
# Create a tuner from 3 slot suppliers.
|
165
|
+
#
|
166
|
+
# @param workflow_slot_supplier [SlotSupplier] Slot supplier for workflows.
|
167
|
+
# @param activity_slot_supplier [SlotSupplier] Slot supplier for activities.
|
168
|
+
# @param local_activity_slot_supplier [SlotSupplier] Slot supplier for local activities.
|
169
|
+
def initialize(
|
170
|
+
workflow_slot_supplier:,
|
171
|
+
activity_slot_supplier:,
|
172
|
+
local_activity_slot_supplier:
|
173
|
+
)
|
174
|
+
@workflow_slot_supplier = workflow_slot_supplier
|
175
|
+
@activity_slot_supplier = activity_slot_supplier
|
176
|
+
@local_activity_slot_supplier = local_activity_slot_supplier
|
177
|
+
end
|
178
|
+
|
179
|
+
# @!visibility private
|
180
|
+
def _to_bridge_options
|
181
|
+
Internal::Bridge::Worker::TunerOptions.new(
|
182
|
+
workflow_slot_supplier: workflow_slot_supplier._to_bridge_options,
|
183
|
+
activity_slot_supplier: activity_slot_supplier._to_bridge_options,
|
184
|
+
local_activity_slot_supplier: local_activity_slot_supplier._to_bridge_options
|
185
|
+
)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'etc'
|
4
|
+
require 'temporalio/internal/bridge/api'
|
5
|
+
require 'temporalio/internal/proto_utils'
|
6
|
+
require 'temporalio/internal/worker/workflow_instance'
|
7
|
+
require 'temporalio/scoped_logger'
|
8
|
+
require 'temporalio/worker/thread_pool'
|
9
|
+
require 'temporalio/worker/workflow_executor'
|
10
|
+
require 'temporalio/workflow'
|
11
|
+
require 'temporalio/workflow/definition'
|
12
|
+
require 'timeout'
|
13
|
+
|
14
|
+
module Temporalio
|
15
|
+
class Worker
|
16
|
+
class WorkflowExecutor
|
17
|
+
# Thread pool implementation of {WorkflowExecutor}.
|
18
|
+
#
|
19
|
+
# Users should use {default} unless they have specific needs to change the thread pool or max threads.
|
20
|
+
class ThreadPool < WorkflowExecutor
|
21
|
+
# @return [ThreadPool] Default executor that lazily constructs an instance with default values.
|
22
|
+
def self.default
|
23
|
+
@default ||= ThreadPool.new
|
24
|
+
end
|
25
|
+
|
26
|
+
# Create a thread pool executor. Most users may prefer {default}.
|
27
|
+
#
|
28
|
+
# @param max_threads [Integer] Maximum number of threads to use concurrently.
|
29
|
+
# @param thread_pool [Worker::ThreadPool] Thread pool to use.
|
30
|
+
def initialize(max_threads: [4, Etc.nprocessors].max, thread_pool: Temporalio::Worker::ThreadPool.default) # rubocop:disable Lint/MissingSuper
|
31
|
+
@max_threads = max_threads
|
32
|
+
@thread_pool = thread_pool
|
33
|
+
@workers_mutex = Mutex.new
|
34
|
+
@workers = []
|
35
|
+
@workers_by_worker_state_and_run_id = {}
|
36
|
+
end
|
37
|
+
|
38
|
+
# @!visibility private
|
39
|
+
def _validate_worker(workflow_worker, worker_state)
|
40
|
+
# Do nothing
|
41
|
+
end
|
42
|
+
|
43
|
+
# @!visibility private
|
44
|
+
def _activate(activation, worker_state, &)
|
45
|
+
# Get applicable worker
|
46
|
+
worker = @workers_mutex.synchronize do
|
47
|
+
run_key = [worker_state, activation.run_id]
|
48
|
+
@workers_by_worker_state_and_run_id.fetch(run_key) do
|
49
|
+
# If not found, get a new one either by creating if not enough or find the one with the fewest.
|
50
|
+
new_worker = if @workers.size < @max_threads
|
51
|
+
created_worker = Worker.new(self)
|
52
|
+
@workers << Worker.new(self)
|
53
|
+
created_worker
|
54
|
+
else
|
55
|
+
@workers.min_by(&:workflow_count)
|
56
|
+
end
|
57
|
+
@workers_by_worker_state_and_run_id[run_key] = new_worker
|
58
|
+
new_worker.workflow_count += 1
|
59
|
+
new_worker
|
60
|
+
end
|
61
|
+
end
|
62
|
+
raise "No worker for run ID #{activation.run_id}" unless worker
|
63
|
+
|
64
|
+
# Enqueue activation
|
65
|
+
worker.enqueue_activation(activation, worker_state, &)
|
66
|
+
end
|
67
|
+
|
68
|
+
# @!visibility private
|
69
|
+
def _thread_pool
|
70
|
+
@thread_pool
|
71
|
+
end
|
72
|
+
|
73
|
+
# @!visibility private
|
74
|
+
def _remove_workflow(worker_state, run_id)
|
75
|
+
@workers_mutex.synchronize do
|
76
|
+
worker = @workers_by_worker_state_and_run_id.delete([worker_state, run_id])
|
77
|
+
if worker
|
78
|
+
worker.workflow_count -= 1
|
79
|
+
# Remove worker from array if done. The array should be small enough that the delete being O(N) is not
|
80
|
+
# worth using a set or a map.
|
81
|
+
if worker.workflow_count.zero?
|
82
|
+
@workers.delete(worker)
|
83
|
+
worker.shutdown
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# @!visibility private
|
90
|
+
class Worker
|
91
|
+
LOG_ACTIVATIONS = false
|
92
|
+
|
93
|
+
attr_accessor :workflow_count
|
94
|
+
|
95
|
+
def initialize(executor)
|
96
|
+
@executor = executor
|
97
|
+
@workflow_count = 0
|
98
|
+
@queue = Queue.new
|
99
|
+
executor._thread_pool.execute { run }
|
100
|
+
end
|
101
|
+
|
102
|
+
# @!visibility private
|
103
|
+
def enqueue_activation(activation, worker_state, &completion_block)
|
104
|
+
@queue << [:activate, activation, worker_state, completion_block]
|
105
|
+
end
|
106
|
+
|
107
|
+
# @!visibility private
|
108
|
+
def shutdown
|
109
|
+
@queue << [:shutdown]
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def run
|
115
|
+
loop do
|
116
|
+
work = @queue.pop
|
117
|
+
if work.is_a?(Exception)
|
118
|
+
Warning.warn("Failed activation: #{work}")
|
119
|
+
elsif work.is_a?(Array)
|
120
|
+
case work.first
|
121
|
+
when :shutdown
|
122
|
+
return
|
123
|
+
when :activate
|
124
|
+
activate(work[1], work[2], &work[3])
|
125
|
+
end
|
126
|
+
end
|
127
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
128
|
+
Warning.warn("Unexpected failure during run: #{e.full_message}")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def activate(activation, worker_state, &)
|
133
|
+
worker_state.logger.debug("Received workflow activation: #{activation}") if LOG_ACTIVATIONS
|
134
|
+
|
135
|
+
# Check whether it has eviction
|
136
|
+
cache_remove_job = activation.jobs.find { |j| !j.remove_from_cache.nil? }&.remove_from_cache
|
137
|
+
|
138
|
+
# If it's eviction only, just evict inline and do nothing else
|
139
|
+
if cache_remove_job && activation.jobs.size == 1
|
140
|
+
evict(worker_state, activation.run_id, cache_remove_job)
|
141
|
+
worker_state.logger.debug('Sending empty workflow completion') if LOG_ACTIVATIONS
|
142
|
+
yield Internal::Bridge::Api::WorkflowCompletion::WorkflowActivationCompletion.new(
|
143
|
+
run_id: activation.run_id,
|
144
|
+
successful: Internal::Bridge::Api::WorkflowCompletion::Success.new
|
145
|
+
)
|
146
|
+
return
|
147
|
+
end
|
148
|
+
|
149
|
+
completion = Timeout.timeout(
|
150
|
+
worker_state.deadlock_timeout,
|
151
|
+
DeadlockError,
|
152
|
+
# TODO(cretz): Document that this affects all running workflows on this worker
|
153
|
+
# and maybe test to see how that is mitigated
|
154
|
+
"[TMPRL1101] Potential deadlock detected: workflow didn't yield " \
|
155
|
+
"within #{worker_state.deadlock_timeout} second(s)."
|
156
|
+
) do
|
157
|
+
# Get or create workflow
|
158
|
+
instance = worker_state.get_or_create_running_workflow(activation.run_id) do
|
159
|
+
create_instance(activation, worker_state)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Activate. We expect most errors in here to have been captured inside.
|
163
|
+
instance.activate(activation)
|
164
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
165
|
+
worker_state.logger.error("Failed activation on workflow run ID: #{activation.run_id}")
|
166
|
+
worker_state.logger.error(e)
|
167
|
+
Internal::Worker::WorkflowInstance.new_completion_with_failure(
|
168
|
+
run_id: activation.run_id,
|
169
|
+
error: e,
|
170
|
+
failure_converter: worker_state.data_converter.failure_converter,
|
171
|
+
payload_converter: worker_state.data_converter.payload_converter
|
172
|
+
)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Go ahead and evict if there is an eviction job
|
176
|
+
evict(worker_state, activation.run_id, cache_remove_job) if cache_remove_job
|
177
|
+
|
178
|
+
# Complete the activation
|
179
|
+
worker_state.logger.debug("Sending workflow completion: #{completion}") if LOG_ACTIVATIONS
|
180
|
+
yield completion
|
181
|
+
end
|
182
|
+
|
183
|
+
def create_instance(initial_activation, worker_state)
|
184
|
+
# Extract start job
|
185
|
+
init_job = initial_activation.jobs.find { |j| !j.initialize_workflow.nil? }&.initialize_workflow
|
186
|
+
raise 'Missing initialize job in initial activation' unless init_job
|
187
|
+
|
188
|
+
# Obtain definition
|
189
|
+
definition = worker_state.workflow_definitions[init_job.workflow_type] ||
|
190
|
+
worker_state.workflow_definitions[nil]
|
191
|
+
unless definition
|
192
|
+
raise Error::ApplicationError.new(
|
193
|
+
"Workflow type #{init_job.workflow_type} is not registered on this worker, available workflows: " +
|
194
|
+
worker_state.workflow_definitions.keys.compact.sort.join(', '),
|
195
|
+
type: 'NotFoundError'
|
196
|
+
)
|
197
|
+
end
|
198
|
+
|
199
|
+
Internal::Worker::WorkflowInstance.new(
|
200
|
+
Internal::Worker::WorkflowInstance::Details.new(
|
201
|
+
namespace: worker_state.namespace,
|
202
|
+
task_queue: worker_state.task_queue,
|
203
|
+
definition:,
|
204
|
+
initial_activation:,
|
205
|
+
logger: worker_state.logger,
|
206
|
+
metric_meter: worker_state.metric_meter,
|
207
|
+
payload_converter: worker_state.data_converter.payload_converter,
|
208
|
+
failure_converter: worker_state.data_converter.failure_converter,
|
209
|
+
interceptors: worker_state.workflow_interceptors,
|
210
|
+
disable_eager_activity_execution: worker_state.disable_eager_activity_execution,
|
211
|
+
illegal_calls: worker_state.illegal_calls,
|
212
|
+
workflow_failure_exception_types: worker_state.workflow_failure_exception_types
|
213
|
+
)
|
214
|
+
)
|
215
|
+
end
|
216
|
+
|
217
|
+
def evict(worker_state, run_id, cache_remove_job)
|
218
|
+
worker_state.evict_running_workflow(run_id, cache_remove_job)
|
219
|
+
@executor._remove_workflow(worker_state, run_id)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
private_constant :Worker
|
224
|
+
|
225
|
+
# Error raised when a processing a workflow task takes more than the expected amount of time.
|
226
|
+
class DeadlockError < Exception; end # rubocop:disable Lint/InheritException
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'temporalio/worker/workflow_executor/thread_pool'
|
4
|
+
|
5
|
+
module Temporalio
|
6
|
+
class Worker
|
7
|
+
# Workflow executor that executes workflow tasks. Unlike {ActivityExecutor}, this class is not meant for user
|
8
|
+
# implementation. The only implementation that is currently accepted is {WorkflowExecutor::ThreadPool}.
|
9
|
+
class WorkflowExecutor
|
10
|
+
# @!visibility private
|
11
|
+
def initialize
|
12
|
+
raise 'Cannot create custom executors'
|
13
|
+
end
|
14
|
+
|
15
|
+
# @!visibility private
|
16
|
+
def _validate_worker(workflow_worker, worker_state)
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
|
20
|
+
# @!visibility private
|
21
|
+
def _activate(activation, worker_state, &)
|
22
|
+
raise NotImplementedError
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|