amit-temporalio 0.3.0-x86_64-darwin

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +2 -0
  3. data/Gemfile +23 -0
  4. data/Rakefile +101 -0
  5. data/lib/temporalio/activity/complete_async_error.rb +11 -0
  6. data/lib/temporalio/activity/context.rb +116 -0
  7. data/lib/temporalio/activity/definition.rb +189 -0
  8. data/lib/temporalio/activity/info.rb +64 -0
  9. data/lib/temporalio/activity.rb +12 -0
  10. data/lib/temporalio/api/activity/v1/message.rb +25 -0
  11. data/lib/temporalio/api/batch/v1/message.rb +31 -0
  12. data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
  13. data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +126 -0
  14. data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +25 -0
  15. data/lib/temporalio/api/cloud/cloudservice.rb +3 -0
  16. data/lib/temporalio/api/cloud/identity/v1/message.rb +41 -0
  17. data/lib/temporalio/api/cloud/namespace/v1/message.rb +42 -0
  18. data/lib/temporalio/api/cloud/nexus/v1/message.rb +31 -0
  19. data/lib/temporalio/api/cloud/operation/v1/message.rb +28 -0
  20. data/lib/temporalio/api/cloud/region/v1/message.rb +24 -0
  21. data/lib/temporalio/api/cloud/resource/v1/message.rb +23 -0
  22. data/lib/temporalio/api/cloud/sink/v1/message.rb +24 -0
  23. data/lib/temporalio/api/cloud/usage/v1/message.rb +31 -0
  24. data/lib/temporalio/api/command/v1/message.rb +46 -0
  25. data/lib/temporalio/api/common/v1/grpc_status.rb +23 -0
  26. data/lib/temporalio/api/common/v1/message.rb +47 -0
  27. data/lib/temporalio/api/enums/v1/batch_operation.rb +22 -0
  28. data/lib/temporalio/api/enums/v1/command_type.rb +21 -0
  29. data/lib/temporalio/api/enums/v1/common.rb +26 -0
  30. data/lib/temporalio/api/enums/v1/event_type.rb +21 -0
  31. data/lib/temporalio/api/enums/v1/failed_cause.rb +26 -0
  32. data/lib/temporalio/api/enums/v1/namespace.rb +23 -0
  33. data/lib/temporalio/api/enums/v1/query.rb +22 -0
  34. data/lib/temporalio/api/enums/v1/reset.rb +23 -0
  35. data/lib/temporalio/api/enums/v1/schedule.rb +21 -0
  36. data/lib/temporalio/api/enums/v1/task_queue.rb +25 -0
  37. data/lib/temporalio/api/enums/v1/update.rb +22 -0
  38. data/lib/temporalio/api/enums/v1/workflow.rb +30 -0
  39. data/lib/temporalio/api/errordetails/v1/message.rb +42 -0
  40. data/lib/temporalio/api/export/v1/message.rb +24 -0
  41. data/lib/temporalio/api/failure/v1/message.rb +35 -0
  42. data/lib/temporalio/api/filter/v1/message.rb +27 -0
  43. data/lib/temporalio/api/history/v1/message.rb +90 -0
  44. data/lib/temporalio/api/namespace/v1/message.rb +31 -0
  45. data/lib/temporalio/api/nexus/v1/message.rb +40 -0
  46. data/lib/temporalio/api/operatorservice/v1/request_response.rb +49 -0
  47. data/lib/temporalio/api/operatorservice/v1/service.rb +23 -0
  48. data/lib/temporalio/api/operatorservice.rb +3 -0
  49. data/lib/temporalio/api/payload_visitor.rb +1513 -0
  50. data/lib/temporalio/api/protocol/v1/message.rb +23 -0
  51. data/lib/temporalio/api/query/v1/message.rb +27 -0
  52. data/lib/temporalio/api/replication/v1/message.rb +26 -0
  53. data/lib/temporalio/api/schedule/v1/message.rb +43 -0
  54. data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +25 -0
  55. data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +21 -0
  56. data/lib/temporalio/api/sdk/v1/user_metadata.rb +23 -0
  57. data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +23 -0
  58. data/lib/temporalio/api/taskqueue/v1/message.rb +45 -0
  59. data/lib/temporalio/api/testservice/v1/request_response.rb +31 -0
  60. data/lib/temporalio/api/testservice/v1/service.rb +23 -0
  61. data/lib/temporalio/api/update/v1/message.rb +33 -0
  62. data/lib/temporalio/api/version/v1/message.rb +26 -0
  63. data/lib/temporalio/api/workflow/v1/message.rb +43 -0
  64. data/lib/temporalio/api/workflowservice/v1/request_response.rb +204 -0
  65. data/lib/temporalio/api/workflowservice/v1/service.rb +23 -0
  66. data/lib/temporalio/api/workflowservice.rb +3 -0
  67. data/lib/temporalio/api.rb +14 -0
  68. data/lib/temporalio/cancellation.rb +170 -0
  69. data/lib/temporalio/client/activity_id_reference.rb +32 -0
  70. data/lib/temporalio/client/async_activity_handle.rb +85 -0
  71. data/lib/temporalio/client/connection/cloud_service.rb +726 -0
  72. data/lib/temporalio/client/connection/operator_service.rb +201 -0
  73. data/lib/temporalio/client/connection/service.rb +42 -0
  74. data/lib/temporalio/client/connection/test_service.rb +111 -0
  75. data/lib/temporalio/client/connection/workflow_service.rb +1041 -0
  76. data/lib/temporalio/client/connection.rb +316 -0
  77. data/lib/temporalio/client/interceptor.rb +416 -0
  78. data/lib/temporalio/client/schedule.rb +967 -0
  79. data/lib/temporalio/client/schedule_handle.rb +126 -0
  80. data/lib/temporalio/client/workflow_execution.rb +100 -0
  81. data/lib/temporalio/client/workflow_execution_count.rb +36 -0
  82. data/lib/temporalio/client/workflow_execution_status.rb +18 -0
  83. data/lib/temporalio/client/workflow_handle.rb +389 -0
  84. data/lib/temporalio/client/workflow_query_reject_condition.rb +14 -0
  85. data/lib/temporalio/client/workflow_update_handle.rb +65 -0
  86. data/lib/temporalio/client/workflow_update_wait_stage.rb +17 -0
  87. data/lib/temporalio/client.rb +484 -0
  88. data/lib/temporalio/common_enums.rb +41 -0
  89. data/lib/temporalio/converters/data_converter.rb +99 -0
  90. data/lib/temporalio/converters/failure_converter.rb +202 -0
  91. data/lib/temporalio/converters/payload_codec.rb +26 -0
  92. data/lib/temporalio/converters/payload_converter/binary_null.rb +34 -0
  93. data/lib/temporalio/converters/payload_converter/binary_plain.rb +35 -0
  94. data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +42 -0
  95. data/lib/temporalio/converters/payload_converter/composite.rb +66 -0
  96. data/lib/temporalio/converters/payload_converter/encoding.rb +35 -0
  97. data/lib/temporalio/converters/payload_converter/json_plain.rb +44 -0
  98. data/lib/temporalio/converters/payload_converter/json_protobuf.rb +41 -0
  99. data/lib/temporalio/converters/payload_converter.rb +71 -0
  100. data/lib/temporalio/converters/raw_value.rb +20 -0
  101. data/lib/temporalio/converters.rb +9 -0
  102. data/lib/temporalio/error/failure.rb +219 -0
  103. data/lib/temporalio/error.rb +155 -0
  104. data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +34 -0
  105. data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +31 -0
  106. data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +33 -0
  107. data/lib/temporalio/internal/bridge/api/common/common.rb +26 -0
  108. data/lib/temporalio/internal/bridge/api/core_interface.rb +40 -0
  109. data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +27 -0
  110. data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +33 -0
  111. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +56 -0
  112. data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +57 -0
  113. data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +30 -0
  114. data/lib/temporalio/internal/bridge/api.rb +3 -0
  115. data/lib/temporalio/internal/bridge/client.rb +95 -0
  116. data/lib/temporalio/internal/bridge/runtime.rb +53 -0
  117. data/lib/temporalio/internal/bridge/temporalio_bridge.bundle +0 -0
  118. data/lib/temporalio/internal/bridge/testing.rb +66 -0
  119. data/lib/temporalio/internal/bridge/worker.rb +85 -0
  120. data/lib/temporalio/internal/bridge.rb +36 -0
  121. data/lib/temporalio/internal/client/implementation.rb +700 -0
  122. data/lib/temporalio/internal/metric.rb +122 -0
  123. data/lib/temporalio/internal/proto_utils.rb +133 -0
  124. data/lib/temporalio/internal/worker/activity_worker.rb +376 -0
  125. data/lib/temporalio/internal/worker/multi_runner.rb +213 -0
  126. data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
  127. data/lib/temporalio/internal/worker/workflow_instance/context.rb +333 -0
  128. data/lib/temporalio/internal/worker/workflow_instance/details.rb +44 -0
  129. data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +32 -0
  130. data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +22 -0
  131. data/lib/temporalio/internal/worker/workflow_instance/handler_execution.rb +25 -0
  132. data/lib/temporalio/internal/worker/workflow_instance/handler_hash.rb +41 -0
  133. data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +97 -0
  134. data/lib/temporalio/internal/worker/workflow_instance/inbound_implementation.rb +62 -0
  135. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +415 -0
  136. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +37 -0
  137. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb +40 -0
  138. data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +163 -0
  139. data/lib/temporalio/internal/worker/workflow_instance.rb +730 -0
  140. data/lib/temporalio/internal/worker/workflow_worker.rb +236 -0
  141. data/lib/temporalio/internal.rb +7 -0
  142. data/lib/temporalio/metric.rb +109 -0
  143. data/lib/temporalio/retry_policy.rb +74 -0
  144. data/lib/temporalio/runtime.rb +314 -0
  145. data/lib/temporalio/scoped_logger.rb +96 -0
  146. data/lib/temporalio/search_attributes.rb +343 -0
  147. data/lib/temporalio/testing/activity_environment.rb +136 -0
  148. data/lib/temporalio/testing/workflow_environment.rb +383 -0
  149. data/lib/temporalio/testing.rb +10 -0
  150. data/lib/temporalio/version.rb +5 -0
  151. data/lib/temporalio/worker/activity_executor/fiber.rb +49 -0
  152. data/lib/temporalio/worker/activity_executor/thread_pool.rb +46 -0
  153. data/lib/temporalio/worker/activity_executor.rb +55 -0
  154. data/lib/temporalio/worker/interceptor.rb +362 -0
  155. data/lib/temporalio/worker/thread_pool.rb +237 -0
  156. data/lib/temporalio/worker/tuner.rb +189 -0
  157. data/lib/temporalio/worker/workflow_executor/thread_pool.rb +230 -0
  158. data/lib/temporalio/worker/workflow_executor.rb +26 -0
  159. data/lib/temporalio/worker/workflow_replayer.rb +343 -0
  160. data/lib/temporalio/worker.rb +569 -0
  161. data/lib/temporalio/workflow/activity_cancellation_type.rb +20 -0
  162. data/lib/temporalio/workflow/child_workflow_cancellation_type.rb +21 -0
  163. data/lib/temporalio/workflow/child_workflow_handle.rb +43 -0
  164. data/lib/temporalio/workflow/definition.rb +566 -0
  165. data/lib/temporalio/workflow/external_workflow_handle.rb +41 -0
  166. data/lib/temporalio/workflow/future.rb +151 -0
  167. data/lib/temporalio/workflow/handler_unfinished_policy.rb +13 -0
  168. data/lib/temporalio/workflow/info.rb +82 -0
  169. data/lib/temporalio/workflow/parent_close_policy.rb +19 -0
  170. data/lib/temporalio/workflow/update_info.rb +20 -0
  171. data/lib/temporalio/workflow.rb +529 -0
  172. data/lib/temporalio/workflow_history.rb +47 -0
  173. data/lib/temporalio.rb +11 -0
  174. data/temporalio.gemspec +28 -0
  175. 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