clara-temporalio 0.4.3

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 (192) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +2 -0
  3. data/Cargo.lock +4429 -0
  4. data/Cargo.toml +31 -0
  5. data/Gemfile +27 -0
  6. data/LICENSE +21 -0
  7. data/README.md +1311 -0
  8. data/Rakefile +101 -0
  9. data/ext/Cargo.toml +27 -0
  10. data/lib/temporalio/activity/cancellation_details.rb +58 -0
  11. data/lib/temporalio/activity/complete_async_error.rb +11 -0
  12. data/lib/temporalio/activity/context.rb +131 -0
  13. data/lib/temporalio/activity/definition.rb +197 -0
  14. data/lib/temporalio/activity/info.rb +70 -0
  15. data/lib/temporalio/activity.rb +14 -0
  16. data/lib/temporalio/api/activity/v1/message.rb +25 -0
  17. data/lib/temporalio/api/batch/v1/message.rb +38 -0
  18. data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
  19. data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +135 -0
  20. data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +25 -0
  21. data/lib/temporalio/api/cloud/cloudservice.rb +3 -0
  22. data/lib/temporalio/api/cloud/identity/v1/message.rb +46 -0
  23. data/lib/temporalio/api/cloud/namespace/v1/message.rb +46 -0
  24. data/lib/temporalio/api/cloud/nexus/v1/message.rb +32 -0
  25. data/lib/temporalio/api/cloud/operation/v1/message.rb +28 -0
  26. data/lib/temporalio/api/cloud/region/v1/message.rb +24 -0
  27. data/lib/temporalio/api/cloud/resource/v1/message.rb +23 -0
  28. data/lib/temporalio/api/cloud/sink/v1/message.rb +24 -0
  29. data/lib/temporalio/api/cloud/usage/v1/message.rb +31 -0
  30. data/lib/temporalio/api/command/v1/message.rb +46 -0
  31. data/lib/temporalio/api/common/v1/grpc_status.rb +23 -0
  32. data/lib/temporalio/api/common/v1/message.rb +49 -0
  33. data/lib/temporalio/api/deployment/v1/message.rb +39 -0
  34. data/lib/temporalio/api/enums/v1/batch_operation.rb +22 -0
  35. data/lib/temporalio/api/enums/v1/command_type.rb +21 -0
  36. data/lib/temporalio/api/enums/v1/common.rb +28 -0
  37. data/lib/temporalio/api/enums/v1/deployment.rb +23 -0
  38. data/lib/temporalio/api/enums/v1/event_type.rb +21 -0
  39. data/lib/temporalio/api/enums/v1/failed_cause.rb +26 -0
  40. data/lib/temporalio/api/enums/v1/namespace.rb +23 -0
  41. data/lib/temporalio/api/enums/v1/nexus.rb +21 -0
  42. data/lib/temporalio/api/enums/v1/query.rb +22 -0
  43. data/lib/temporalio/api/enums/v1/reset.rb +23 -0
  44. data/lib/temporalio/api/enums/v1/schedule.rb +21 -0
  45. data/lib/temporalio/api/enums/v1/task_queue.rb +25 -0
  46. data/lib/temporalio/api/enums/v1/update.rb +22 -0
  47. data/lib/temporalio/api/enums/v1/workflow.rb +31 -0
  48. data/lib/temporalio/api/errordetails/v1/message.rb +44 -0
  49. data/lib/temporalio/api/export/v1/message.rb +24 -0
  50. data/lib/temporalio/api/failure/v1/message.rb +38 -0
  51. data/lib/temporalio/api/filter/v1/message.rb +27 -0
  52. data/lib/temporalio/api/history/v1/message.rb +94 -0
  53. data/lib/temporalio/api/namespace/v1/message.rb +31 -0
  54. data/lib/temporalio/api/nexus/v1/message.rb +41 -0
  55. data/lib/temporalio/api/operatorservice/v1/request_response.rb +49 -0
  56. data/lib/temporalio/api/operatorservice/v1/service.rb +23 -0
  57. data/lib/temporalio/api/operatorservice.rb +3 -0
  58. data/lib/temporalio/api/payload_visitor.rb +1668 -0
  59. data/lib/temporalio/api/protocol/v1/message.rb +23 -0
  60. data/lib/temporalio/api/query/v1/message.rb +28 -0
  61. data/lib/temporalio/api/replication/v1/message.rb +26 -0
  62. data/lib/temporalio/api/rules/v1/message.rb +27 -0
  63. data/lib/temporalio/api/schedule/v1/message.rb +43 -0
  64. data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +25 -0
  65. data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +21 -0
  66. data/lib/temporalio/api/sdk/v1/user_metadata.rb +23 -0
  67. data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +23 -0
  68. data/lib/temporalio/api/taskqueue/v1/message.rb +48 -0
  69. data/lib/temporalio/api/testservice/v1/request_response.rb +31 -0
  70. data/lib/temporalio/api/testservice/v1/service.rb +23 -0
  71. data/lib/temporalio/api/update/v1/message.rb +33 -0
  72. data/lib/temporalio/api/version/v1/message.rb +26 -0
  73. data/lib/temporalio/api/workflow/v1/message.rb +63 -0
  74. data/lib/temporalio/api/workflowservice/v1/request_response.rb +244 -0
  75. data/lib/temporalio/api/workflowservice/v1/service.rb +23 -0
  76. data/lib/temporalio/api/workflowservice.rb +3 -0
  77. data/lib/temporalio/api.rb +15 -0
  78. data/lib/temporalio/cancellation.rb +170 -0
  79. data/lib/temporalio/client/activity_id_reference.rb +32 -0
  80. data/lib/temporalio/client/async_activity_handle.rb +85 -0
  81. data/lib/temporalio/client/connection/cloud_service.rb +786 -0
  82. data/lib/temporalio/client/connection/operator_service.rb +201 -0
  83. data/lib/temporalio/client/connection/service.rb +42 -0
  84. data/lib/temporalio/client/connection/test_service.rb +111 -0
  85. data/lib/temporalio/client/connection/workflow_service.rb +1326 -0
  86. data/lib/temporalio/client/connection.rb +316 -0
  87. data/lib/temporalio/client/interceptor.rb +457 -0
  88. data/lib/temporalio/client/schedule.rb +991 -0
  89. data/lib/temporalio/client/schedule_handle.rb +126 -0
  90. data/lib/temporalio/client/with_start_workflow_operation.rb +115 -0
  91. data/lib/temporalio/client/workflow_execution.rb +119 -0
  92. data/lib/temporalio/client/workflow_execution_count.rb +36 -0
  93. data/lib/temporalio/client/workflow_execution_status.rb +18 -0
  94. data/lib/temporalio/client/workflow_handle.rb +389 -0
  95. data/lib/temporalio/client/workflow_query_reject_condition.rb +14 -0
  96. data/lib/temporalio/client/workflow_update_handle.rb +65 -0
  97. data/lib/temporalio/client/workflow_update_wait_stage.rb +17 -0
  98. data/lib/temporalio/client.rb +625 -0
  99. data/lib/temporalio/common_enums.rb +55 -0
  100. data/lib/temporalio/contrib/open_telemetry.rb +469 -0
  101. data/lib/temporalio/converters/data_converter.rb +99 -0
  102. data/lib/temporalio/converters/failure_converter.rb +205 -0
  103. data/lib/temporalio/converters/payload_codec.rb +26 -0
  104. data/lib/temporalio/converters/payload_converter/binary_null.rb +34 -0
  105. data/lib/temporalio/converters/payload_converter/binary_plain.rb +35 -0
  106. data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +42 -0
  107. data/lib/temporalio/converters/payload_converter/composite.rb +66 -0
  108. data/lib/temporalio/converters/payload_converter/encoding.rb +35 -0
  109. data/lib/temporalio/converters/payload_converter/json_plain.rb +44 -0
  110. data/lib/temporalio/converters/payload_converter/json_protobuf.rb +41 -0
  111. data/lib/temporalio/converters/payload_converter.rb +71 -0
  112. data/lib/temporalio/converters/raw_value.rb +20 -0
  113. data/lib/temporalio/converters.rb +9 -0
  114. data/lib/temporalio/error/failure.rb +237 -0
  115. data/lib/temporalio/error.rb +156 -0
  116. data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +34 -0
  117. data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +32 -0
  118. data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +33 -0
  119. data/lib/temporalio/internal/bridge/api/common/common.rb +27 -0
  120. data/lib/temporalio/internal/bridge/api/core_interface.rb +40 -0
  121. data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +27 -0
  122. data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +34 -0
  123. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +56 -0
  124. data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +58 -0
  125. data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +31 -0
  126. data/lib/temporalio/internal/bridge/api.rb +3 -0
  127. data/lib/temporalio/internal/bridge/client.rb +95 -0
  128. data/lib/temporalio/internal/bridge/runtime.rb +56 -0
  129. data/lib/temporalio/internal/bridge/testing.rb +69 -0
  130. data/lib/temporalio/internal/bridge/worker.rb +109 -0
  131. data/lib/temporalio/internal/bridge.rb +36 -0
  132. data/lib/temporalio/internal/client/implementation.rb +926 -0
  133. data/lib/temporalio/internal/metric.rb +122 -0
  134. data/lib/temporalio/internal/proto_utils.rb +165 -0
  135. data/lib/temporalio/internal/worker/activity_worker.rb +448 -0
  136. data/lib/temporalio/internal/worker/multi_runner.rb +213 -0
  137. data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
  138. data/lib/temporalio/internal/worker/workflow_instance/context.rb +391 -0
  139. data/lib/temporalio/internal/worker/workflow_instance/details.rb +49 -0
  140. data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +32 -0
  141. data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +22 -0
  142. data/lib/temporalio/internal/worker/workflow_instance/handler_execution.rb +25 -0
  143. data/lib/temporalio/internal/worker/workflow_instance/handler_hash.rb +41 -0
  144. data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +97 -0
  145. data/lib/temporalio/internal/worker/workflow_instance/inbound_implementation.rb +62 -0
  146. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +404 -0
  147. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +37 -0
  148. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb +40 -0
  149. data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +183 -0
  150. data/lib/temporalio/internal/worker/workflow_instance.rb +800 -0
  151. data/lib/temporalio/internal/worker/workflow_worker.rb +249 -0
  152. data/lib/temporalio/internal.rb +7 -0
  153. data/lib/temporalio/metric.rb +109 -0
  154. data/lib/temporalio/priority.rb +59 -0
  155. data/lib/temporalio/retry_policy.rb +74 -0
  156. data/lib/temporalio/runtime/metric_buffer.rb +94 -0
  157. data/lib/temporalio/runtime.rb +352 -0
  158. data/lib/temporalio/scoped_logger.rb +96 -0
  159. data/lib/temporalio/search_attributes.rb +356 -0
  160. data/lib/temporalio/testing/activity_environment.rb +175 -0
  161. data/lib/temporalio/testing/workflow_environment.rb +406 -0
  162. data/lib/temporalio/testing.rb +10 -0
  163. data/lib/temporalio/version.rb +5 -0
  164. data/lib/temporalio/versioning_override.rb +55 -0
  165. data/lib/temporalio/worker/activity_executor/fiber.rb +49 -0
  166. data/lib/temporalio/worker/activity_executor/thread_pool.rb +46 -0
  167. data/lib/temporalio/worker/activity_executor.rb +55 -0
  168. data/lib/temporalio/worker/deployment_options.rb +45 -0
  169. data/lib/temporalio/worker/interceptor.rb +367 -0
  170. data/lib/temporalio/worker/poller_behavior.rb +61 -0
  171. data/lib/temporalio/worker/thread_pool.rb +237 -0
  172. data/lib/temporalio/worker/tuner.rb +189 -0
  173. data/lib/temporalio/worker/workflow_executor/thread_pool.rb +236 -0
  174. data/lib/temporalio/worker/workflow_executor.rb +26 -0
  175. data/lib/temporalio/worker/workflow_replayer.rb +349 -0
  176. data/lib/temporalio/worker.rb +633 -0
  177. data/lib/temporalio/worker_deployment_version.rb +67 -0
  178. data/lib/temporalio/workflow/activity_cancellation_type.rb +20 -0
  179. data/lib/temporalio/workflow/child_workflow_cancellation_type.rb +21 -0
  180. data/lib/temporalio/workflow/child_workflow_handle.rb +43 -0
  181. data/lib/temporalio/workflow/definition.rb +680 -0
  182. data/lib/temporalio/workflow/external_workflow_handle.rb +41 -0
  183. data/lib/temporalio/workflow/future.rb +151 -0
  184. data/lib/temporalio/workflow/handler_unfinished_policy.rb +13 -0
  185. data/lib/temporalio/workflow/info.rb +107 -0
  186. data/lib/temporalio/workflow/parent_close_policy.rb +19 -0
  187. data/lib/temporalio/workflow/update_info.rb +20 -0
  188. data/lib/temporalio/workflow.rb +594 -0
  189. data/lib/temporalio/workflow_history.rb +47 -0
  190. data/lib/temporalio.rb +12 -0
  191. data/temporalio.gemspec +31 -0
  192. metadata +263 -0
@@ -0,0 +1,249 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/api/payload_visitor'
4
+ require 'temporalio/error'
5
+ require 'temporalio/internal/worker/workflow_instance'
6
+ require 'temporalio/scoped_logger'
7
+ require 'temporalio/workflow'
8
+ require 'temporalio/workflow/definition'
9
+ require 'timeout'
10
+
11
+ module Temporalio
12
+ module Internal
13
+ module Worker
14
+ # Worker for handling workflow activations. Most activation work is delegated to the workflow executor.
15
+ class WorkflowWorker
16
+ def self.workflow_definitions(workflows, should_enforce_versioning_behavior:)
17
+ workflows.each_with_object({}) do |workflow, hash|
18
+ # Load definition
19
+ defn = begin
20
+ if workflow.is_a?(Workflow::Definition::Info)
21
+ workflow
22
+ else
23
+ Workflow::Definition::Info.from_class(workflow)
24
+ end
25
+ rescue StandardError
26
+ raise ArgumentError, "Failed loading workflow #{workflow}"
27
+ end
28
+
29
+ # Confirm name not in use
30
+ raise ArgumentError, "Multiple workflows named #{defn.name || '<dynamic>'}" if hash.key?(defn.name)
31
+
32
+ # Enforce versioning behavior is set when versioning is on
33
+ if should_enforce_versioning_behavior &&
34
+ defn.versioning_behavior == VersioningBehavior::UNSPECIFIED && !defn.dynamic_options_method
35
+ raise ArgumentError, "Workflow #{defn.name} must specify a versioning behavior"
36
+ end
37
+
38
+ hash[defn.name] = defn
39
+ end
40
+ end
41
+
42
+ def self.bridge_workflow_failure_exception_type_options(
43
+ workflow_failure_exception_types:,
44
+ workflow_definitions:
45
+ )
46
+ as_fail = workflow_failure_exception_types.any? do |t|
47
+ t.is_a?(Class) && t >= Workflow::NondeterminismError
48
+ end
49
+ as_fail_for_types = workflow_definitions.values.map do |defn|
50
+ next unless defn.failure_exception_types.any? { |t| t.is_a?(Class) && t >= Workflow::NondeterminismError }
51
+
52
+ # If they tried to do this on a dynamic workflow and haven't already set worker-level option, warn
53
+ unless defn.name || as_fail
54
+ warn('Note, dynamic workflows cannot trap non-determinism errors, so worker-level ' \
55
+ 'workflow_failure_exception_types should be set to capture that if that is the intention')
56
+ end
57
+ defn.name
58
+ end.compact
59
+ [as_fail, as_fail_for_types]
60
+ end
61
+
62
+ def initialize(
63
+ bridge_worker:,
64
+ namespace:,
65
+ task_queue:,
66
+ workflow_definitions:,
67
+ workflow_executor:,
68
+ logger:,
69
+ data_converter:,
70
+ metric_meter:,
71
+ workflow_interceptors:,
72
+ disable_eager_activity_execution:,
73
+ illegal_workflow_calls:,
74
+ workflow_failure_exception_types:,
75
+ workflow_payload_codec_thread_pool:,
76
+ unsafe_workflow_io_enabled:,
77
+ debug_mode:,
78
+ assert_valid_local_activity:, on_eviction: nil
79
+ )
80
+ @executor = workflow_executor
81
+
82
+ payload_codec = data_converter.payload_codec
83
+ @workflow_payload_codec_thread_pool = workflow_payload_codec_thread_pool
84
+ if !Fiber.current_scheduler && payload_codec && !@workflow_payload_codec_thread_pool
85
+ raise ArgumentError, 'Must have workflow payload codec thread pool if providing codec and not using fibers'
86
+ end
87
+
88
+ # If there is a payload codec, we need to build encoding and decoding visitors
89
+ if payload_codec
90
+ @payload_encoding_visitor = Api::PayloadVisitor.new(skip_search_attributes: true) do |payload_or_payloads|
91
+ apply_codec_on_payload_visit(payload_or_payloads) { |payloads| payload_codec.encode(payloads) }
92
+ end
93
+ @payload_decoding_visitor = Api::PayloadVisitor.new(skip_search_attributes: true) do |payload_or_payloads|
94
+ apply_codec_on_payload_visit(payload_or_payloads) { |payloads| payload_codec.decode(payloads) }
95
+ end
96
+ end
97
+
98
+ @state = State.new(
99
+ workflow_definitions:,
100
+ bridge_worker:,
101
+ logger:,
102
+ metric_meter:,
103
+ data_converter:,
104
+ deadlock_timeout: debug_mode ? nil : 2.0,
105
+ # TODO(cretz): Make this more performant for the default set?
106
+ illegal_calls: WorkflowInstance::IllegalCallTracer.frozen_validated_illegal_calls(
107
+ illegal_workflow_calls || {}
108
+ ),
109
+ namespace:,
110
+ task_queue:,
111
+ disable_eager_activity_execution:,
112
+ workflow_interceptors:,
113
+ workflow_failure_exception_types: workflow_failure_exception_types.map do |t|
114
+ unless t.is_a?(Class) && t < Exception
115
+ raise ArgumentError, 'All failure types must classes inheriting Exception'
116
+ end
117
+
118
+ t
119
+ end.freeze,
120
+ unsafe_workflow_io_enabled:,
121
+ assert_valid_local_activity:
122
+ )
123
+ @state.on_eviction = on_eviction if on_eviction
124
+
125
+ # Validate worker
126
+ @executor._validate_worker(self, @state)
127
+ end
128
+
129
+ def handle_activation(runner:, activation:, decoded:)
130
+ # Encode in background if not encoded but it needs to be
131
+ if @payload_encoding_visitor && !decoded
132
+ if Fiber.current_scheduler
133
+ Fiber.schedule { decode_activation(runner, activation) }
134
+ else
135
+ @workflow_payload_codec_thread_pool.execute { decode_activation(runner, activation) }
136
+ end
137
+ else
138
+ @executor._activate(activation, @state) do |activation_completion|
139
+ runner.apply_workflow_activation_complete(workflow_worker: self, activation_completion:, encoded: false)
140
+ end
141
+ end
142
+ rescue Exception => e # rubocop:disable Lint/RescueException
143
+ # Should never happen, executors are expected to trap things
144
+ @state.logger.error("Failed issuing activation on workflow run ID: #{activation.run_id}")
145
+ @state.logger.error(e)
146
+ end
147
+
148
+ def handle_activation_complete(runner:, activation_completion:, encoded:, completion_complete_queue:)
149
+ if @payload_encoding_visitor && !encoded
150
+ if Fiber.current_scheduler
151
+ Fiber.schedule { encode_activation_completion(runner, activation_completion) }
152
+ else
153
+ @workflow_payload_codec_thread_pool.execute do
154
+ encode_activation_completion(runner, activation_completion)
155
+ end
156
+ end
157
+ else
158
+ @state.bridge_worker.async_complete_workflow_activation(
159
+ activation_completion.run_id, activation_completion.to_proto, completion_complete_queue
160
+ )
161
+ end
162
+ end
163
+
164
+ def on_shutdown_complete
165
+ @state.evict_all
166
+ end
167
+
168
+ private
169
+
170
+ def decode_activation(runner, activation)
171
+ @payload_decoding_visitor.run(activation)
172
+ runner.apply_workflow_activation_decoded(workflow_worker: self, activation:)
173
+ end
174
+
175
+ def encode_activation_completion(runner, activation_completion)
176
+ @payload_encoding_visitor.run(activation_completion)
177
+ runner.apply_workflow_activation_complete(workflow_worker: self, activation_completion:, encoded: true)
178
+ end
179
+
180
+ def apply_codec_on_payload_visit(payload_or_payloads, &)
181
+ case payload_or_payloads
182
+ when Temporalio::Api::Common::V1::Payload
183
+ new_payloads = yield [payload_or_payloads]
184
+ payload_or_payloads.metadata = new_payloads.first.metadata
185
+ payload_or_payloads.data = new_payloads.first.data
186
+ when Enumerable
187
+ payload_or_payloads.replace(yield payload_or_payloads) # steep:ignore
188
+ else
189
+ raise 'Unrecognized visitor type'
190
+ end
191
+ end
192
+
193
+ class State
194
+ attr_reader :workflow_definitions, :bridge_worker, :logger, :metric_meter, :data_converter, :deadlock_timeout,
195
+ :illegal_calls, :namespace, :task_queue, :disable_eager_activity_execution,
196
+ :workflow_interceptors, :workflow_failure_exception_types, :unsafe_workflow_io_enabled,
197
+ :assert_valid_local_activity
198
+
199
+ attr_writer :on_eviction
200
+
201
+ def initialize(
202
+ workflow_definitions:, bridge_worker:, logger:, metric_meter:, data_converter:, deadlock_timeout:,
203
+ illegal_calls:, namespace:, task_queue:, disable_eager_activity_execution:,
204
+ workflow_interceptors:, workflow_failure_exception_types:, unsafe_workflow_io_enabled:,
205
+ assert_valid_local_activity:
206
+ )
207
+ @workflow_definitions = workflow_definitions
208
+ @bridge_worker = bridge_worker
209
+ @logger = logger
210
+ @metric_meter = metric_meter
211
+ @data_converter = data_converter
212
+ @deadlock_timeout = deadlock_timeout
213
+ @illegal_calls = illegal_calls
214
+ @namespace = namespace
215
+ @task_queue = task_queue
216
+ @disable_eager_activity_execution = disable_eager_activity_execution
217
+ @workflow_interceptors = workflow_interceptors
218
+ @workflow_failure_exception_types = workflow_failure_exception_types
219
+ @unsafe_workflow_io_enabled = unsafe_workflow_io_enabled
220
+ @assert_valid_local_activity = assert_valid_local_activity
221
+
222
+ @running_workflows = {}
223
+ @running_workflows_mutex = Mutex.new
224
+ end
225
+
226
+ # This can never be called at the same time for the same run ID on the same state object
227
+ def get_or_create_running_workflow(run_id, &)
228
+ instance = @running_workflows_mutex.synchronize { @running_workflows[run_id] }
229
+ # If instance is not there, we create it out of lock then store it under lock
230
+ unless instance
231
+ instance = yield
232
+ @running_workflows_mutex.synchronize { @running_workflows[run_id] = instance }
233
+ end
234
+ instance
235
+ end
236
+
237
+ def evict_running_workflow(run_id, cache_remove_job)
238
+ @running_workflows_mutex.synchronize { @running_workflows.delete(run_id) }
239
+ @on_eviction&.call(run_id, cache_remove_job)
240
+ end
241
+
242
+ def evict_all
243
+ @running_workflows_mutex.synchronize { @running_workflows.clear }
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Temporalio
4
+ # @!visibility private
5
+ module Internal
6
+ end
7
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/internal/metric'
4
+
5
+ module Temporalio
6
+ # Metric that can be recorded from a metric meter. This is obtained via {Meter.create_metric}. The metric meter is
7
+ # obtained via workflow environment, activity context, or from the {Runtime} if in neither of those. This class is
8
+ # effectively abstract and will fail if `initialize` is attempted.
9
+ class Metric
10
+ # @!visibility private
11
+ def initialize
12
+ raise NotImplementedError, 'Metric is abstract, implementations override initialize'
13
+ end
14
+
15
+ # Record a value for the metric. For counters, this adds to any existing. For histograms, this records into proper
16
+ # buckets. For gauges, this sets the value. The value type must match the expectation.
17
+ #
18
+ # @param value [Numeric] Value to record. For counters and duration-based histograms, this value cannot be negative.
19
+ # @param additional_attributes [Hash{String, Symbol => String, Integer, Float, Boolean}, nil] Additional attributes
20
+ # on this specific record. For better performance for attribute reuse, users are encouraged to use
21
+ # {with_additional_attributes} to make a copy of this metric with those attributes.
22
+ def record(value, additional_attributes: nil)
23
+ raise NotImplementedError
24
+ end
25
+
26
+ # Create a copy of this metric but with the given additional attributes. This is more performant than providing
27
+ # attributes on each {record} call.
28
+ #
29
+ # @param additional_attributes [Hash{String, Symbol => String, Integer, Float, Boolean}] Attributes to set on the
30
+ # resulting metric.
31
+ # @return [Metric] Copy of this metric with the additional attributes.
32
+ def with_additional_attributes(additional_attributes)
33
+ raise NotImplementedError
34
+ end
35
+
36
+ # @return [:counter, :histogram, :gauge] Metric type.
37
+ def metric_type
38
+ raise NotImplementedError
39
+ end
40
+
41
+ # @return [String] Metric name.
42
+ def name
43
+ raise NotImplementedError
44
+ end
45
+
46
+ # @return [String, nil] Metric description.
47
+ def description
48
+ raise NotImplementedError
49
+ end
50
+
51
+ # @return [String, nil] Metric unit.
52
+ def unit
53
+ raise NotImplementedError
54
+ end
55
+
56
+ # @return [:integer, :float, :duration] Metric value type.
57
+ def value_type
58
+ raise NotImplementedError
59
+ end
60
+
61
+ # Meter for creating metrics to record values on. This is obtained via workflow environment, activity context, or
62
+ # from the {Runtime} if in neither of those. This class is effectively abstract and will fail if `initialize` is
63
+ # attempted.
64
+ class Meter
65
+ # @return [Meter] A no-op instance of {Meter}.
66
+ def self.null
67
+ Internal::Metric::NullMeter.instance
68
+ end
69
+
70
+ # @!visibility private
71
+ def initialize
72
+ raise NotImplementedError, 'Meter is abstract, implementations override initialize'
73
+ end
74
+
75
+ # Create a new metric. Only certain metric types are accepted and only value types can work with certain metric
76
+ # types.
77
+ #
78
+ # @param metric_type [:counter, :histogram, :gauge] Metric type. Counters can only have `:integer` value types,
79
+ # histograms can have `:integer`, `:float`, or :duration` value types, and gauges can have `:integer` or
80
+ # `:float` value types.
81
+ # @param name [String] Metric name.
82
+ # @param description [String, nil] Metric description.
83
+ # @param unit [String, nil] Metric unit.
84
+ # @param value_type [:integer, :float, :duration] Metric value type. `:integer` works for all metric types,
85
+ # `:float` works for `:histogram` and `:gauge` metric types, and `:duration` only works for `:histogram` metric
86
+ # types.
87
+ # @return [Metric] Created metric.
88
+ def create_metric(
89
+ metric_type,
90
+ name,
91
+ description: nil,
92
+ unit: nil,
93
+ value_type: :integer
94
+ )
95
+ raise NotImplementedError
96
+ end
97
+
98
+ # Create a copy of this meter but with the given additional attributes. This is more performant than providing
99
+ # attributes on each {record} call.
100
+ #
101
+ # @param additional_attributes [Hash{String, Symbol => String, Integer, Float, Boolean}] Attributes to set on the
102
+ # resulting meter.
103
+ # @return [Meter] Copy of this meter with the additional attributes.
104
+ def with_additional_attributes(additional_attributes)
105
+ raise NotImplementedError
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/api'
4
+
5
+ module Temporalio
6
+ Priority = Data.define(
7
+ :priority_key
8
+ )
9
+
10
+ # Priority contains metadata that controls relative ordering of task processing when tasks are
11
+ # backlogged in a queue. Initially, Priority will be used in activity and workflow task
12
+ # queues, which are typically where backlogs exist. Priority is (for now) attached to
13
+ # workflows and activities. Activities and child workflows inherit Priority from the workflow
14
+ # that created them, but may override fields when they are started or modified. For each field
15
+ # of a Priority on an activity/workflow, not present or equal to zero/empty string means to
16
+ # inherit the value from the calling workflow, or if there is no calling workflow, then use
17
+ # the default (documented on the field).
18
+ #
19
+ # The overall semantics of Priority are:
20
+ # 1. First, consider "priority_key": lower number goes first.
21
+ # (more will be added here later).
22
+ #
23
+ # @!attribute priority_key
24
+ # @return [Integer, nil] The priority key, which is a positive integer from 1 to n, where
25
+ # smaller integers correspond to higher priorities (tasks run sooner). In general, tasks in a
26
+ # queue should be processed in close to priority order, although small deviations are possible.
27
+ # The maximum priority value (minimum priority) is determined by server configuration, and
28
+ # defaults to 5.
29
+ #
30
+ # The default priority is (min+max)/2. With the default max of 5 and min of 1, that comes
31
+ # out to 3.
32
+ class Priority
33
+ # @!visibility private
34
+ def self._from_proto(priority)
35
+ return default if priority.nil?
36
+
37
+ new(priority_key: priority.priority_key.zero? ? nil : priority.priority_key)
38
+ end
39
+
40
+ # The default priority instance.
41
+ #
42
+ # @return [Priority] The default priority
43
+ def self.default
44
+ @default ||= new(priority_key: nil)
45
+ end
46
+
47
+ # @!visibility private
48
+ def _to_proto
49
+ return nil if priority_key.nil?
50
+
51
+ Temporalio::Api::Common::V1::Priority.new(priority_key: priority_key || 0)
52
+ end
53
+
54
+ # @return [Boolean] True if this priority is empty/default
55
+ def empty?
56
+ priority_key.nil?
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/internal/proto_utils'
4
+
5
+ module Temporalio
6
+ RetryPolicy = Data.define(
7
+ :initial_interval,
8
+ :backoff_coefficient,
9
+ :max_interval,
10
+ :max_attempts,
11
+ :non_retryable_error_types
12
+ )
13
+
14
+ # Options for retrying workflows and activities.
15
+ #
16
+ # @!attribute initial_interval
17
+ # @return [Float] Backoff interval in seconds for the first retry. Default 1.0.
18
+ # @!attribute backoff_coefficient
19
+ # @return [Float] Coefficient to multiply previous backoff interval by to get new interval. Default 2.0.
20
+ # @!attribute max_interval
21
+ # @return [Float, nil] Maximum backoff interval in seconds between retries. Default 100x `initial_interval`.
22
+ # @!attribute max_attempts
23
+ # @return [Integer] Maximum number of attempts. If `0`, the default, there is no maximum.
24
+ # @!attribute non_retryable_error_types
25
+ # @return [Array<String>, nil] List of error types that are not retryable.
26
+ class RetryPolicy
27
+ # @!visibility private
28
+ def self._from_proto(raw_policy)
29
+ RetryPolicy.new(
30
+ initial_interval: Internal::ProtoUtils.duration_to_seconds(raw_policy.initial_interval) || raise, # Never nil
31
+ backoff_coefficient: raw_policy.backoff_coefficient,
32
+ max_interval: Internal::ProtoUtils.duration_to_seconds(raw_policy.maximum_interval),
33
+ max_attempts: raw_policy.maximum_attempts,
34
+ non_retryable_error_types: raw_policy.non_retryable_error_types&.to_a
35
+ )
36
+ end
37
+
38
+ # Create retry policy.
39
+ #
40
+ # @param initial_interval [Float] Backoff interval in seconds for the first retry. Default 1.0.
41
+ # @param backoff_coefficient [Float] Coefficient to multiply previous backoff interval by to get new interval.
42
+ # Default 2.0.
43
+ # @param max_interval [Float, nil] Maximum backoff interval in seconds between retries. Default 100x
44
+ # `initial_interval`.
45
+ # @param max_attempts [Integer] Maximum number of attempts. If `0`, the default, there is no maximum.
46
+ # @param non_retryable_error_types [Array<String>, nil] List of error types that are not retryable.
47
+ def initialize(
48
+ initial_interval: 1.0,
49
+ backoff_coefficient: 2.0,
50
+ max_interval: nil,
51
+ max_attempts: 0,
52
+ non_retryable_error_types: nil
53
+ )
54
+ super
55
+ end
56
+
57
+ # @!visibility private
58
+ def _to_proto
59
+ raise 'Initial interval cannot be negative' if initial_interval.negative?
60
+ raise 'Backoff coefficient cannot be less than 1' if backoff_coefficient < 1
61
+ raise 'Max interval cannot be negative' if max_interval&.negative?
62
+ raise 'Max interval cannot be less than initial interval' if max_interval && max_interval < initial_interval
63
+ raise 'Max attempts cannot be negative' if max_attempts.negative?
64
+
65
+ Api::Common::V1::RetryPolicy.new(
66
+ initial_interval: Internal::ProtoUtils.seconds_to_duration(initial_interval),
67
+ backoff_coefficient:,
68
+ maximum_interval: Internal::ProtoUtils.seconds_to_duration(max_interval),
69
+ maximum_attempts: max_attempts,
70
+ non_retryable_error_types:
71
+ )
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Temporalio
4
+ class Runtime
5
+ # Metric buffer for use with a runtime to capture metrics. Only one metric buffer can be associated with a runtime
6
+ # and {retrieve_updates} cannot be called before the runtime is created. Once runtime created, users should
7
+ # regularly call {retrieve_updates} to drain the buffer.
8
+ #
9
+ # @note WARNING: It is important that the buffer size is set to a high number and that {retrieve_updates} is called
10
+ # regularly to drain the buffer. If the buffer is full, metric updates will be dropped and an error will be
11
+ # logged.
12
+ class MetricBuffer
13
+ # Enumerates for the duration format.
14
+ module DurationFormat
15
+ # Durations are millisecond integers.
16
+ MILLISECONDS = :milliseconds
17
+
18
+ # Durations are second floats.
19
+ SECONDS = :seconds
20
+ end
21
+
22
+ Update = Data.define(:metric, :value, :attributes)
23
+
24
+ # Metric buffer update.
25
+ #
26
+ # @note WARNING: The constructor of this class should not be invoked by users and may change in incompatible ways
27
+ # in the future.
28
+ #
29
+ # @!attribute metric
30
+ # @return [Metric] Metric for this update. For performance reasons, this is created lazily on first use and is
31
+ # the same object each time an update on this metric exists.
32
+ # @!attribute value
33
+ # @return [Integer, Float] Metric value for this update.
34
+ # @!attribute attributes
35
+ # @return [Hash{String => String, Integer, Float, Boolean}] Attributes for this value as a frozen hash.
36
+ # For performance reasons this is sometimes the same hash if the attribute set is reused at a metric level.
37
+ class Update # rubocop:disable Lint/EmptyClass
38
+ # DEV NOTE: This class is instantiated inside Rust, be careful changing it.
39
+ end
40
+
41
+ Metric = Data.define(:name, :description, :unit, :kind)
42
+
43
+ # Metric definition present on an update.
44
+ #
45
+ # @!attribute name
46
+ # @return [String] Name of the metric.
47
+ # @!attribute description
48
+ # @return [String, nil] Description of the metric if any.
49
+ # @!attribute unit
50
+ # @return [String, nil] Unit of the metric if any.
51
+ # @!attribute kind
52
+ # @return [:counter, :histogram, :gauge] Kind of the metric.
53
+ class Metric # rubocop:disable Lint/EmptyClass
54
+ # DEV NOTE: This class is instantiated inside Rust, be careful changing it.
55
+ end
56
+
57
+ # Create a metric buffer with the given size.
58
+ #
59
+ # @note WARNING: It is important that the buffer size is set to a high number and is drained regularly. See
60
+ # {MetricBuffer} warning.
61
+ #
62
+ # @param buffer_size [Integer] Maximum size of the buffer before metrics will be dropped.
63
+ # @param duration_format [DurationFormat] How durations are represented.
64
+ def initialize(buffer_size, duration_format: DurationFormat::MILLISECONDS)
65
+ @buffer_size = buffer_size
66
+ @duration_format = duration_format
67
+ @runtime = nil
68
+ end
69
+
70
+ # Drain the buffer and return all metric updates.
71
+ #
72
+ # @note WARNING: It is important that this is called regularly. See {MetricBuffer} warning.
73
+ #
74
+ # @return [Array<Update>] Updates since last time this was called.
75
+ def retrieve_updates
76
+ raise 'Attempting to retrieve updates before runtime created' unless @runtime
77
+
78
+ @runtime._core_runtime.retrieve_buffered_metrics(@duration_format == DurationFormat::SECONDS)
79
+ end
80
+
81
+ # @!visibility private
82
+ def _buffer_size
83
+ @buffer_size
84
+ end
85
+
86
+ # @!visibility private
87
+ def _set_runtime(runtime)
88
+ raise 'Metric buffer already attached to a runtime' if @runtime
89
+
90
+ @runtime = runtime
91
+ end
92
+ end
93
+ end
94
+ end