amit-temporalio 0.3.1

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 (179) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +2 -0
  3. data/Cargo.lock +4325 -0
  4. data/Cargo.toml +25 -0
  5. data/Gemfile +23 -0
  6. data/LICENSE +21 -0
  7. data/README.md +1148 -0
  8. data/Rakefile +101 -0
  9. data/ext/Cargo.toml +27 -0
  10. data/lib/temporalio/activity/complete_async_error.rb +11 -0
  11. data/lib/temporalio/activity/context.rb +116 -0
  12. data/lib/temporalio/activity/definition.rb +189 -0
  13. data/lib/temporalio/activity/info.rb +64 -0
  14. data/lib/temporalio/activity.rb +12 -0
  15. data/lib/temporalio/api/activity/v1/message.rb +25 -0
  16. data/lib/temporalio/api/batch/v1/message.rb +31 -0
  17. data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
  18. data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +126 -0
  19. data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +25 -0
  20. data/lib/temporalio/api/cloud/cloudservice.rb +3 -0
  21. data/lib/temporalio/api/cloud/identity/v1/message.rb +41 -0
  22. data/lib/temporalio/api/cloud/namespace/v1/message.rb +42 -0
  23. data/lib/temporalio/api/cloud/nexus/v1/message.rb +31 -0
  24. data/lib/temporalio/api/cloud/operation/v1/message.rb +28 -0
  25. data/lib/temporalio/api/cloud/region/v1/message.rb +24 -0
  26. data/lib/temporalio/api/cloud/resource/v1/message.rb +23 -0
  27. data/lib/temporalio/api/cloud/sink/v1/message.rb +24 -0
  28. data/lib/temporalio/api/cloud/usage/v1/message.rb +31 -0
  29. data/lib/temporalio/api/command/v1/message.rb +46 -0
  30. data/lib/temporalio/api/common/v1/grpc_status.rb +23 -0
  31. data/lib/temporalio/api/common/v1/message.rb +47 -0
  32. data/lib/temporalio/api/enums/v1/batch_operation.rb +22 -0
  33. data/lib/temporalio/api/enums/v1/command_type.rb +21 -0
  34. data/lib/temporalio/api/enums/v1/common.rb +26 -0
  35. data/lib/temporalio/api/enums/v1/event_type.rb +21 -0
  36. data/lib/temporalio/api/enums/v1/failed_cause.rb +26 -0
  37. data/lib/temporalio/api/enums/v1/namespace.rb +23 -0
  38. data/lib/temporalio/api/enums/v1/query.rb +22 -0
  39. data/lib/temporalio/api/enums/v1/reset.rb +23 -0
  40. data/lib/temporalio/api/enums/v1/schedule.rb +21 -0
  41. data/lib/temporalio/api/enums/v1/task_queue.rb +25 -0
  42. data/lib/temporalio/api/enums/v1/update.rb +22 -0
  43. data/lib/temporalio/api/enums/v1/workflow.rb +30 -0
  44. data/lib/temporalio/api/errordetails/v1/message.rb +42 -0
  45. data/lib/temporalio/api/export/v1/message.rb +24 -0
  46. data/lib/temporalio/api/failure/v1/message.rb +35 -0
  47. data/lib/temporalio/api/filter/v1/message.rb +27 -0
  48. data/lib/temporalio/api/history/v1/message.rb +90 -0
  49. data/lib/temporalio/api/namespace/v1/message.rb +31 -0
  50. data/lib/temporalio/api/nexus/v1/message.rb +40 -0
  51. data/lib/temporalio/api/operatorservice/v1/request_response.rb +49 -0
  52. data/lib/temporalio/api/operatorservice/v1/service.rb +23 -0
  53. data/lib/temporalio/api/operatorservice.rb +3 -0
  54. data/lib/temporalio/api/payload_visitor.rb +1513 -0
  55. data/lib/temporalio/api/protocol/v1/message.rb +23 -0
  56. data/lib/temporalio/api/query/v1/message.rb +27 -0
  57. data/lib/temporalio/api/replication/v1/message.rb +26 -0
  58. data/lib/temporalio/api/schedule/v1/message.rb +43 -0
  59. data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +25 -0
  60. data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +21 -0
  61. data/lib/temporalio/api/sdk/v1/user_metadata.rb +23 -0
  62. data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +23 -0
  63. data/lib/temporalio/api/taskqueue/v1/message.rb +45 -0
  64. data/lib/temporalio/api/testservice/v1/request_response.rb +31 -0
  65. data/lib/temporalio/api/testservice/v1/service.rb +23 -0
  66. data/lib/temporalio/api/update/v1/message.rb +33 -0
  67. data/lib/temporalio/api/version/v1/message.rb +26 -0
  68. data/lib/temporalio/api/workflow/v1/message.rb +43 -0
  69. data/lib/temporalio/api/workflowservice/v1/request_response.rb +204 -0
  70. data/lib/temporalio/api/workflowservice/v1/service.rb +23 -0
  71. data/lib/temporalio/api/workflowservice.rb +3 -0
  72. data/lib/temporalio/api.rb +14 -0
  73. data/lib/temporalio/cancellation.rb +170 -0
  74. data/lib/temporalio/client/activity_id_reference.rb +32 -0
  75. data/lib/temporalio/client/async_activity_handle.rb +85 -0
  76. data/lib/temporalio/client/connection/cloud_service.rb +726 -0
  77. data/lib/temporalio/client/connection/operator_service.rb +201 -0
  78. data/lib/temporalio/client/connection/service.rb +42 -0
  79. data/lib/temporalio/client/connection/test_service.rb +111 -0
  80. data/lib/temporalio/client/connection/workflow_service.rb +1041 -0
  81. data/lib/temporalio/client/connection.rb +316 -0
  82. data/lib/temporalio/client/interceptor.rb +416 -0
  83. data/lib/temporalio/client/schedule.rb +967 -0
  84. data/lib/temporalio/client/schedule_handle.rb +126 -0
  85. data/lib/temporalio/client/workflow_execution.rb +100 -0
  86. data/lib/temporalio/client/workflow_execution_count.rb +36 -0
  87. data/lib/temporalio/client/workflow_execution_status.rb +18 -0
  88. data/lib/temporalio/client/workflow_handle.rb +389 -0
  89. data/lib/temporalio/client/workflow_query_reject_condition.rb +14 -0
  90. data/lib/temporalio/client/workflow_update_handle.rb +65 -0
  91. data/lib/temporalio/client/workflow_update_wait_stage.rb +17 -0
  92. data/lib/temporalio/client.rb +484 -0
  93. data/lib/temporalio/common_enums.rb +41 -0
  94. data/lib/temporalio/converters/data_converter.rb +99 -0
  95. data/lib/temporalio/converters/failure_converter.rb +202 -0
  96. data/lib/temporalio/converters/payload_codec.rb +26 -0
  97. data/lib/temporalio/converters/payload_converter/binary_null.rb +34 -0
  98. data/lib/temporalio/converters/payload_converter/binary_plain.rb +35 -0
  99. data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +42 -0
  100. data/lib/temporalio/converters/payload_converter/composite.rb +66 -0
  101. data/lib/temporalio/converters/payload_converter/encoding.rb +35 -0
  102. data/lib/temporalio/converters/payload_converter/json_plain.rb +44 -0
  103. data/lib/temporalio/converters/payload_converter/json_protobuf.rb +41 -0
  104. data/lib/temporalio/converters/payload_converter.rb +71 -0
  105. data/lib/temporalio/converters/raw_value.rb +20 -0
  106. data/lib/temporalio/converters.rb +9 -0
  107. data/lib/temporalio/error/failure.rb +219 -0
  108. data/lib/temporalio/error.rb +155 -0
  109. data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +34 -0
  110. data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +31 -0
  111. data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +33 -0
  112. data/lib/temporalio/internal/bridge/api/common/common.rb +26 -0
  113. data/lib/temporalio/internal/bridge/api/core_interface.rb +40 -0
  114. data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +27 -0
  115. data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +33 -0
  116. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +56 -0
  117. data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +57 -0
  118. data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +30 -0
  119. data/lib/temporalio/internal/bridge/api.rb +3 -0
  120. data/lib/temporalio/internal/bridge/client.rb +95 -0
  121. data/lib/temporalio/internal/bridge/runtime.rb +53 -0
  122. data/lib/temporalio/internal/bridge/testing.rb +66 -0
  123. data/lib/temporalio/internal/bridge/worker.rb +85 -0
  124. data/lib/temporalio/internal/bridge.rb +36 -0
  125. data/lib/temporalio/internal/client/implementation.rb +700 -0
  126. data/lib/temporalio/internal/metric.rb +122 -0
  127. data/lib/temporalio/internal/proto_utils.rb +133 -0
  128. data/lib/temporalio/internal/worker/activity_worker.rb +376 -0
  129. data/lib/temporalio/internal/worker/multi_runner.rb +213 -0
  130. data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
  131. data/lib/temporalio/internal/worker/workflow_instance/context.rb +333 -0
  132. data/lib/temporalio/internal/worker/workflow_instance/details.rb +44 -0
  133. data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +32 -0
  134. data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +22 -0
  135. data/lib/temporalio/internal/worker/workflow_instance/handler_execution.rb +25 -0
  136. data/lib/temporalio/internal/worker/workflow_instance/handler_hash.rb +41 -0
  137. data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +97 -0
  138. data/lib/temporalio/internal/worker/workflow_instance/inbound_implementation.rb +62 -0
  139. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +415 -0
  140. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +37 -0
  141. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb +40 -0
  142. data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +163 -0
  143. data/lib/temporalio/internal/worker/workflow_instance.rb +730 -0
  144. data/lib/temporalio/internal/worker/workflow_worker.rb +236 -0
  145. data/lib/temporalio/internal.rb +7 -0
  146. data/lib/temporalio/metric.rb +109 -0
  147. data/lib/temporalio/retry_policy.rb +74 -0
  148. data/lib/temporalio/runtime.rb +314 -0
  149. data/lib/temporalio/scoped_logger.rb +96 -0
  150. data/lib/temporalio/search_attributes.rb +343 -0
  151. data/lib/temporalio/testing/activity_environment.rb +136 -0
  152. data/lib/temporalio/testing/workflow_environment.rb +383 -0
  153. data/lib/temporalio/testing.rb +10 -0
  154. data/lib/temporalio/version.rb +5 -0
  155. data/lib/temporalio/worker/activity_executor/fiber.rb +49 -0
  156. data/lib/temporalio/worker/activity_executor/thread_pool.rb +46 -0
  157. data/lib/temporalio/worker/activity_executor.rb +55 -0
  158. data/lib/temporalio/worker/interceptor.rb +362 -0
  159. data/lib/temporalio/worker/thread_pool.rb +237 -0
  160. data/lib/temporalio/worker/tuner.rb +189 -0
  161. data/lib/temporalio/worker/workflow_executor/thread_pool.rb +230 -0
  162. data/lib/temporalio/worker/workflow_executor.rb +26 -0
  163. data/lib/temporalio/worker/workflow_replayer.rb +343 -0
  164. data/lib/temporalio/worker.rb +569 -0
  165. data/lib/temporalio/workflow/activity_cancellation_type.rb +20 -0
  166. data/lib/temporalio/workflow/child_workflow_cancellation_type.rb +21 -0
  167. data/lib/temporalio/workflow/child_workflow_handle.rb +43 -0
  168. data/lib/temporalio/workflow/definition.rb +566 -0
  169. data/lib/temporalio/workflow/external_workflow_handle.rb +41 -0
  170. data/lib/temporalio/workflow/future.rb +151 -0
  171. data/lib/temporalio/workflow/handler_unfinished_policy.rb +13 -0
  172. data/lib/temporalio/workflow/info.rb +82 -0
  173. data/lib/temporalio/workflow/parent_close_policy.rb +19 -0
  174. data/lib/temporalio/workflow/update_info.rb +20 -0
  175. data/lib/temporalio/workflow.rb +529 -0
  176. data/lib/temporalio/workflow_history.rb +47 -0
  177. data/lib/temporalio.rb +11 -0
  178. data/temporalio.gemspec +28 -0
  179. metadata +234 -0
@@ -0,0 +1,362 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Temporalio
4
+ class Worker
5
+ module Interceptor
6
+ # Mixin for intercepting activity worker work. Clases that `include` may implement their own {intercept_activity}
7
+ # that returns their own instance of {Inbound}.
8
+ #
9
+ # @note Input classes herein may get new required fields added and therefore the constructors of the Input classes
10
+ # may change in backwards incompatible ways. Users should not try to construct Input classes themselves.
11
+ module Activity
12
+ # Method called when intercepting an activity. This is called when starting an activity attempt.
13
+ #
14
+ # @param next_interceptor [Inbound] Next interceptor in the chain that should be called. This is usually passed
15
+ # to {Inbound} constructor.
16
+ # @return [Inbound] Interceptor to be called for activity calls.
17
+ def intercept_activity(next_interceptor)
18
+ next_interceptor
19
+ end
20
+
21
+ # Input for {Inbound.execute}.
22
+ ExecuteInput = Data.define(
23
+ :proc,
24
+ :args,
25
+ :headers
26
+ )
27
+
28
+ # Inbound interceptor for intercepting inbound activity calls. This should be extended by users needing to
29
+ # intercept activities.
30
+ class Inbound
31
+ # @return [Inbound] Next interceptor in the chain.
32
+ attr_reader :next_interceptor
33
+
34
+ # Initialize inbound with the next interceptor in the chain.
35
+ #
36
+ # @param next_interceptor [Inbound] Next interceptor in the chain.
37
+ def initialize(next_interceptor)
38
+ @next_interceptor = next_interceptor
39
+ end
40
+
41
+ # Initialize the outbound interceptor. This should be extended by users to return their own {Outbound}
42
+ # implementation that wraps the parameter here.
43
+ #
44
+ # @param outbound [Outbound] Next outbound interceptor in the chain.
45
+ # @return [Outbound] Outbound activity interceptor.
46
+ def init(outbound)
47
+ @next_interceptor.init(outbound)
48
+ end
49
+
50
+ # Execute an activity and return result or raise exception. Next interceptor in chain (i.e. `super`) will
51
+ # perform the execution.
52
+ #
53
+ # @param input [ExecuteInput] Input information.
54
+ # @return [Object] Activity result.
55
+ def execute(input)
56
+ @next_interceptor.execute(input)
57
+ end
58
+ end
59
+
60
+ # Input for {Outbound.heartbeat}.
61
+ HeartbeatInput = Data.define(
62
+ :details
63
+ )
64
+
65
+ # Outbound interceptor for intercepting outbound activity calls. This should be extended by users needing to
66
+ # intercept activity calls.
67
+ class Outbound
68
+ # @return [Outbound] Next interceptor in the chain.
69
+ attr_reader :next_interceptor
70
+
71
+ # Initialize outbound with the next interceptor in the chain.
72
+ #
73
+ # @param next_interceptor [Outbound] Next interceptor in the chain.
74
+ def initialize(next_interceptor)
75
+ @next_interceptor = next_interceptor
76
+ end
77
+
78
+ # Issue a heartbeat.
79
+ #
80
+ # @param input [HeartbeatInput] Input information.
81
+ def heartbeat(input)
82
+ @next_interceptor.heartbeat(input)
83
+ end
84
+ end
85
+ end
86
+
87
+ # Mixin for intercepting workflow worker work. Classes that `include` may implement their own {intercept_workflow}
88
+ # that returns their own instance of {Inbound}.
89
+ #
90
+ # @note Input classes herein may get new required fields added and therefore the constructors of the Input classes
91
+ # may change in backwards incompatible ways. Users should not try to construct Input classes themselves.
92
+ module Workflow
93
+ # Method called when intercepting a workflow. This is called when creating a workflow instance.
94
+ #
95
+ # @param next_interceptor [Inbound] Next interceptor in the chain that should be called. This is usually passed
96
+ # to {Inbound} constructor.
97
+ # @return [Inbound] Interceptor to be called for workflow calls.
98
+ def intercept_workflow(next_interceptor)
99
+ next_interceptor
100
+ end
101
+
102
+ # Input for {Inbound.execute}.
103
+ ExecuteInput = Data.define(
104
+ :args,
105
+ :headers
106
+ )
107
+
108
+ # Input for {Inbound.handle_signal}.
109
+ HandleSignalInput = Data.define(
110
+ :signal,
111
+ :args,
112
+ :definition,
113
+ :headers
114
+ )
115
+
116
+ # Input for {Inbound.handle_query}.
117
+ HandleQueryInput = Data.define(
118
+ :id,
119
+ :query,
120
+ :args,
121
+ :definition,
122
+ :headers
123
+ )
124
+
125
+ # Input for {Inbound.validate_update} and {Inbound.handle_update}.
126
+ HandleUpdateInput = Data.define(
127
+ :id,
128
+ :update,
129
+ :args,
130
+ :definition,
131
+ :headers
132
+ )
133
+
134
+ # Inbound interceptor for intercepting inbound workflow calls. This should be extended by users needing to
135
+ # intercept workflows.
136
+ class Inbound
137
+ # @return [Inbound] Next interceptor in the chain.
138
+ attr_reader :next_interceptor
139
+
140
+ # Initialize inbound with the next interceptor in the chain.
141
+ #
142
+ # @param next_interceptor [Inbound] Next interceptor in the chain.
143
+ def initialize(next_interceptor)
144
+ @next_interceptor = next_interceptor
145
+ end
146
+
147
+ # Initialize the outbound interceptor. This should be extended by users to return their own {Outbound}
148
+ # implementation that wraps the parameter here.
149
+ #
150
+ # @param outbound [Outbound] Next outbound interceptor in the chain.
151
+ # @return [Outbound] Outbound workflow interceptor.
152
+ def init(outbound)
153
+ @next_interceptor.init(outbound)
154
+ end
155
+
156
+ # Execute a workflow and return result or raise exception. Next interceptor in chain (i.e. `super`) will
157
+ # perform the execution.
158
+ #
159
+ # @param input [ExecuteInput] Input information.
160
+ # @return [Object] Workflow result.
161
+ def execute(input)
162
+ @next_interceptor.execute(input)
163
+ end
164
+
165
+ # Handle a workflow signal. Next interceptor in chain (i.e. `super`) will perform the handling.
166
+ #
167
+ # @param input [HandleSignalInput] Input information.
168
+ def handle_signal(input)
169
+ @next_interceptor.handle_signal(input)
170
+ end
171
+
172
+ # Handle a workflow query and return result or raise exception. Next interceptor in chain (i.e. `super`) will
173
+ # perform the handling.
174
+ #
175
+ # @param input [HandleQueryInput] Input information.
176
+ # @return [Object] Query result.
177
+ def handle_query(input)
178
+ @next_interceptor.handle_query(input)
179
+ end
180
+
181
+ # Validate a workflow update. Next interceptor in chain (i.e. `super`) will perform the validation.
182
+ #
183
+ # @param input [HandleUpdateInput] Input information.
184
+ def validate_update(input)
185
+ @next_interceptor.validate_update(input)
186
+ end
187
+
188
+ # Handle a workflow update and return result or raise exception. Next interceptor in chain (i.e. `super`) will
189
+ # perform the handling.
190
+ #
191
+ # @param input [HandleUpdateInput] Input information.
192
+ # @return [Object] Update result.
193
+ def handle_update(input)
194
+ @next_interceptor.handle_update(input)
195
+ end
196
+ end
197
+
198
+ # Input for {Outbound.cancel_external_workflow}.
199
+ CancelExternalWorkflowInput = Data.define(
200
+ :id,
201
+ :run_id
202
+ )
203
+
204
+ # Input for {Outbound.execute_activity}.
205
+ ExecuteActivityInput = Data.define(
206
+ :activity,
207
+ :args,
208
+ :task_queue,
209
+ :schedule_to_close_timeout,
210
+ :schedule_to_start_timeout,
211
+ :start_to_close_timeout,
212
+ :heartbeat_timeout,
213
+ :retry_policy,
214
+ :cancellation,
215
+ :cancellation_type,
216
+ :activity_id,
217
+ :disable_eager_execution,
218
+ :headers
219
+ )
220
+
221
+ # Input for {Outbound.execute_local_activity}.
222
+ ExecuteLocalActivityInput = Data.define(
223
+ :activity,
224
+ :args,
225
+ :schedule_to_close_timeout,
226
+ :schedule_to_start_timeout,
227
+ :start_to_close_timeout,
228
+ :retry_policy,
229
+ :local_retry_threshold,
230
+ :cancellation,
231
+ :cancellation_type,
232
+ :activity_id,
233
+ :headers
234
+ )
235
+
236
+ # Input for {Outbound.initialize_continue_as_new_error}.
237
+ InitializeContinueAsNewErrorInput = Data.define(
238
+ :error
239
+ )
240
+
241
+ # Input for {Outbound.signal_child_workflow}.
242
+ SignalChildWorkflowInput = Data.define(
243
+ :id,
244
+ :signal,
245
+ :args,
246
+ :cancellation,
247
+ :headers
248
+ )
249
+
250
+ # Input for {Outbound.signal_external_workflow}.
251
+ SignalExternalWorkflowInput = Data.define(
252
+ :id,
253
+ :run_id,
254
+ :signal,
255
+ :args,
256
+ :cancellation,
257
+ :headers
258
+ )
259
+
260
+ # Input for {Outbound.sleep}.
261
+ SleepInput = Data.define(
262
+ :duration,
263
+ :summary,
264
+ :cancellation
265
+ )
266
+
267
+ # Input for {Outbound.start_child_workflow}.
268
+ StartChildWorkflowInput = Data.define(
269
+ :workflow,
270
+ :args,
271
+ :id,
272
+ :task_queue,
273
+ :cancellation,
274
+ :cancellation_type,
275
+ :parent_close_policy,
276
+ :execution_timeout,
277
+ :run_timeout,
278
+ :task_timeout,
279
+ :id_reuse_policy,
280
+ :retry_policy,
281
+ :cron_schedule,
282
+ :memo,
283
+ :search_attributes,
284
+ :headers
285
+ )
286
+
287
+ # Outbound interceptor for intercepting outbound workflow calls. This should be extended by users needing to
288
+ # intercept workflow calls.
289
+ class Outbound
290
+ # @return [Outbound] Next interceptor in the chain.
291
+ attr_reader :next_interceptor
292
+
293
+ # Initialize outbound with the next interceptor in the chain.
294
+ #
295
+ # @param next_interceptor [Outbound] Next interceptor in the chain.
296
+ def initialize(next_interceptor)
297
+ @next_interceptor = next_interceptor
298
+ end
299
+
300
+ # Cancel external workflow.
301
+ #
302
+ # @param input [CancelExternalWorkflowInput] Input.
303
+ def cancel_external_workflow(input)
304
+ @next_interceptor.cancel_external_workflow(input)
305
+ end
306
+
307
+ # Execute activity.
308
+ #
309
+ # @param input [ExecuteActivityInput] Input.
310
+ # @return [Object] Activity result.
311
+ def execute_activity(input)
312
+ @next_interceptor.execute_activity(input)
313
+ end
314
+
315
+ # Execute local activity.
316
+ #
317
+ # @param input [ExecuteLocalActivityInput] Input.
318
+ # @return [Object] Activity result.
319
+ def execute_local_activity(input)
320
+ @next_interceptor.execute_local_activity(input)
321
+ end
322
+
323
+ # Initialize continue as new error.
324
+ #
325
+ # @param input [InitializeContinueAsNewErrorInput] Input.
326
+ def initialize_continue_as_new_error(input)
327
+ @next_interceptor.initialize_continue_as_new_error(input)
328
+ end
329
+
330
+ # Signal child workflow.
331
+ #
332
+ # @param input [SignalChildWorkflowInput] Input.
333
+ def signal_child_workflow(input)
334
+ @next_interceptor.signal_child_workflow(input)
335
+ end
336
+
337
+ # Signal external workflow.
338
+ #
339
+ # @param input [SignalExternalWorkflowInput] Input.
340
+ def signal_external_workflow(input)
341
+ @next_interceptor.signal_external_workflow(input)
342
+ end
343
+
344
+ # Sleep.
345
+ #
346
+ # @param input [SleepInput] Input.
347
+ def sleep(input)
348
+ @next_interceptor.sleep(input)
349
+ end
350
+
351
+ # Start child workflow.
352
+ #
353
+ # @param input [StartChildWorkflowInput] Input.
354
+ # @return [Workflow::ChildWorkflowHandle] Child workflow handle.
355
+ def start_child_workflow(input)
356
+ @next_interceptor.start_child_workflow(input)
357
+ end
358
+ end
359
+ end
360
+ end
361
+ end
362
+ end
@@ -0,0 +1,237 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Much of this logic taken from
4
+ # https://github.com/ruby-concurrency/concurrent-ruby/blob/044020f44b36930b863b930f3ee8fa1e9f750469/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb,
5
+ # see MIT license at
6
+ # https://github.com/ruby-concurrency/concurrent-ruby/blob/044020f44b36930b863b930f3ee8fa1e9f750469/LICENSE.txt
7
+
8
+ module Temporalio
9
+ class Worker
10
+ # Implementation of a thread pool. This implementation is a stripped down form of Concurrent Ruby's
11
+ # `CachedThreadPool`.
12
+ class ThreadPool
13
+ # @return [ThreadPool] Default/shared thread pool instance with unlimited max threads.
14
+ def self.default
15
+ @default ||= new
16
+ end
17
+
18
+ # @!visibility private
19
+ def self._monotonic_time
20
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
21
+ end
22
+
23
+ # Create a new thread pool that creates threads as needed.
24
+ #
25
+ # @param max_threads [Integer, nil] Maximum number of thread workers to create, or nil for unlimited max.
26
+ # @param idle_timeout [Float] Number of seconds before a thread worker with no work should be stopped. Note,
27
+ # the check of whether a thread worker is idle is only done on each new {execute} call.
28
+ def initialize(max_threads: nil, idle_timeout: 20)
29
+ @max_threads = max_threads
30
+ @idle_timeout = idle_timeout
31
+
32
+ @mutex = Mutex.new
33
+ @pool = []
34
+ @ready = []
35
+ @queue = []
36
+ @scheduled_task_count = 0
37
+ @completed_task_count = 0
38
+ @largest_length = 0
39
+ @workers_counter = 0
40
+ @prune_interval = @idle_timeout / 2
41
+ @next_prune_time = ThreadPool._monotonic_time + @prune_interval
42
+ end
43
+
44
+ # Execute the given block in a thread. The block should be built to never raise and need no arguments.
45
+ #
46
+ # @yield Block to execute.
47
+ def execute(&block)
48
+ @mutex.synchronize do
49
+ locked_assign_worker(&block) || locked_enqueue(&block)
50
+ @scheduled_task_count += 1
51
+ locked_prune_pool if @next_prune_time < ThreadPool._monotonic_time
52
+ end
53
+ end
54
+
55
+ # @return [Integer] The largest number of threads that have been created in the pool since construction.
56
+ def largest_length
57
+ @mutex.synchronize { @largest_length }
58
+ end
59
+
60
+ # @return [Integer] The number of tasks that have been scheduled for execution on the pool since construction.
61
+ def scheduled_task_count
62
+ @mutex.synchronize { @scheduled_task_count }
63
+ end
64
+
65
+ # @return [Integer] The number of tasks that have been completed by the pool since construction.
66
+ def completed_task_count
67
+ @mutex.synchronize { @completed_task_count }
68
+ end
69
+
70
+ # @return [Integer] The number of threads that are actively executing tasks.
71
+ def active_count
72
+ @mutex.synchronize { @pool.length - @ready.length }
73
+ end
74
+
75
+ # @return [Integer] The number of threads currently in the pool.
76
+ def length
77
+ @mutex.synchronize { @pool.length }
78
+ end
79
+
80
+ # @return [Integer] The number of tasks in the queue awaiting execution.
81
+ def queue_length
82
+ @mutex.synchronize { @queue.length }
83
+ end
84
+
85
+ # Gracefully shutdown each thread when it is done with its current task. This should not be called until all
86
+ # workers using this executor are complete. This does not need to be called at all on program exit (e.g. for the
87
+ # global default).
88
+ def shutdown
89
+ @mutex.synchronize do
90
+ # Stop all workers
91
+ @pool.each(&:stop)
92
+ end
93
+ end
94
+
95
+ # Kill each thread. This should not be called until all workers using this executor are complete. This does not
96
+ # need to be called at all on program exit (e.g. for the global default).
97
+ def kill
98
+ @mutex.synchronize do
99
+ # Kill all workers
100
+ @pool.each(&:kill)
101
+ @pool.clear
102
+ @ready.clear
103
+ end
104
+ end
105
+
106
+ # @!visibility private
107
+ def _remove_busy_worker(worker)
108
+ @mutex.synchronize { locked_remove_busy_worker(worker) }
109
+ end
110
+
111
+ # @!visibility private
112
+ def _ready_worker(worker, last_message)
113
+ @mutex.synchronize { locked_ready_worker(worker, last_message) }
114
+ end
115
+
116
+ # @!visibility private
117
+ def _worker_died(worker)
118
+ @mutex.synchronize { locked_worker_died(worker) }
119
+ end
120
+
121
+ # @!visibility private
122
+ def _worker_task_completed
123
+ @mutex.synchronize { @completed_task_count += 1 }
124
+ end
125
+
126
+ private
127
+
128
+ def locked_assign_worker(&block)
129
+ # keep growing if the pool is not at the minimum yet
130
+ worker, = @ready.pop || locked_add_busy_worker
131
+ if worker
132
+ worker << block
133
+ true
134
+ else
135
+ false
136
+ end
137
+ end
138
+
139
+ def locked_enqueue(&block)
140
+ @queue << block
141
+ end
142
+
143
+ def locked_add_busy_worker
144
+ return if @max_threads && @pool.size >= @max_threads
145
+
146
+ @workers_counter += 1
147
+ @pool << (worker = Worker.new(self, @workers_counter))
148
+ @largest_length = @pool.length if @pool.length > @largest_length
149
+ worker
150
+ end
151
+
152
+ def locked_prune_pool
153
+ now = ThreadPool._monotonic_time
154
+ stopped_workers = 0
155
+ while !@ready.empty? && (@pool.size - stopped_workers).positive?
156
+ worker, last_message = @ready.first
157
+ break unless now - last_message > @idle_timeout
158
+
159
+ stopped_workers += 1
160
+ @ready.shift
161
+ worker << :stop
162
+
163
+ end
164
+
165
+ @next_prune_time = ThreadPool._monotonic_time + @prune_interval
166
+ end
167
+
168
+ def locked_remove_busy_worker(worker)
169
+ @pool.delete(worker)
170
+ end
171
+
172
+ def locked_ready_worker(worker, last_message)
173
+ block = @queue.shift
174
+ if block
175
+ worker << block
176
+ else
177
+ @ready.push([worker, last_message])
178
+ end
179
+ end
180
+
181
+ def locked_worker_died(worker)
182
+ locked_remove_busy_worker(worker)
183
+ replacement_worker = locked_add_busy_worker
184
+ locked_ready_worker(replacement_worker, ThreadPool._monotonic_time) if replacement_worker
185
+ end
186
+
187
+ # @!visibility private
188
+ class Worker
189
+ def initialize(pool, id)
190
+ @queue = Queue.new
191
+ @thread = Thread.new(@queue, pool) do |my_queue, my_pool|
192
+ catch(:stop) do
193
+ loop do
194
+ case block = my_queue.pop
195
+ when :stop
196
+ pool._remove_busy_worker(self)
197
+ throw :stop
198
+ else
199
+ begin
200
+ block.call
201
+ my_pool._worker_task_completed
202
+ my_pool._ready_worker(self, ThreadPool._monotonic_time)
203
+ rescue StandardError => e
204
+ # Ignore
205
+ warn("Unexpected execute block error: #{e.full_message}")
206
+ rescue Exception => e # rubocop:disable Lint/RescueException
207
+ warn("Unexpected execute block exception: #{e.full_message}")
208
+ my_pool._worker_died(self)
209
+ throw :stop
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
215
+ @thread.name = "temporal-thread-#{id}"
216
+ end
217
+
218
+ # @!visibility private
219
+ def <<(block)
220
+ @queue << block
221
+ end
222
+
223
+ # @!visibility private
224
+ def stop
225
+ @queue << :stop
226
+ end
227
+
228
+ # @!visibility private
229
+ def kill
230
+ @thread.kill
231
+ end
232
+ end
233
+
234
+ private_constant :Worker
235
+ end
236
+ end
237
+ end