temporalio 0.4.0-aarch64-linux-musl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +2 -0
  3. data/Gemfile +27 -0
  4. data/Rakefile +101 -0
  5. data/lib/temporalio/activity/complete_async_error.rb +11 -0
  6. data/lib/temporalio/activity/context.rb +123 -0
  7. data/lib/temporalio/activity/definition.rb +192 -0
  8. data/lib/temporalio/activity/info.rb +67 -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 +36 -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 +48 -0
  27. data/lib/temporalio/api/deployment/v1/message.rb +38 -0
  28. data/lib/temporalio/api/enums/v1/batch_operation.rb +22 -0
  29. data/lib/temporalio/api/enums/v1/command_type.rb +21 -0
  30. data/lib/temporalio/api/enums/v1/common.rb +26 -0
  31. data/lib/temporalio/api/enums/v1/deployment.rb +23 -0
  32. data/lib/temporalio/api/enums/v1/event_type.rb +21 -0
  33. data/lib/temporalio/api/enums/v1/failed_cause.rb +26 -0
  34. data/lib/temporalio/api/enums/v1/namespace.rb +23 -0
  35. data/lib/temporalio/api/enums/v1/nexus.rb +21 -0
  36. data/lib/temporalio/api/enums/v1/query.rb +22 -0
  37. data/lib/temporalio/api/enums/v1/reset.rb +23 -0
  38. data/lib/temporalio/api/enums/v1/schedule.rb +21 -0
  39. data/lib/temporalio/api/enums/v1/task_queue.rb +25 -0
  40. data/lib/temporalio/api/enums/v1/update.rb +22 -0
  41. data/lib/temporalio/api/enums/v1/workflow.rb +31 -0
  42. data/lib/temporalio/api/errordetails/v1/message.rb +44 -0
  43. data/lib/temporalio/api/export/v1/message.rb +24 -0
  44. data/lib/temporalio/api/failure/v1/message.rb +37 -0
  45. data/lib/temporalio/api/filter/v1/message.rb +27 -0
  46. data/lib/temporalio/api/history/v1/message.rb +92 -0
  47. data/lib/temporalio/api/namespace/v1/message.rb +31 -0
  48. data/lib/temporalio/api/nexus/v1/message.rb +41 -0
  49. data/lib/temporalio/api/operatorservice/v1/request_response.rb +49 -0
  50. data/lib/temporalio/api/operatorservice/v1/service.rb +23 -0
  51. data/lib/temporalio/api/operatorservice.rb +3 -0
  52. data/lib/temporalio/api/payload_visitor.rb +1581 -0
  53. data/lib/temporalio/api/protocol/v1/message.rb +23 -0
  54. data/lib/temporalio/api/query/v1/message.rb +28 -0
  55. data/lib/temporalio/api/replication/v1/message.rb +26 -0
  56. data/lib/temporalio/api/schedule/v1/message.rb +43 -0
  57. data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +25 -0
  58. data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +21 -0
  59. data/lib/temporalio/api/sdk/v1/user_metadata.rb +23 -0
  60. data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +23 -0
  61. data/lib/temporalio/api/taskqueue/v1/message.rb +48 -0
  62. data/lib/temporalio/api/testservice/v1/request_response.rb +31 -0
  63. data/lib/temporalio/api/testservice/v1/service.rb +23 -0
  64. data/lib/temporalio/api/update/v1/message.rb +33 -0
  65. data/lib/temporalio/api/version/v1/message.rb +26 -0
  66. data/lib/temporalio/api/workflow/v1/message.rb +51 -0
  67. data/lib/temporalio/api/workflowservice/v1/request_response.rb +233 -0
  68. data/lib/temporalio/api/workflowservice/v1/service.rb +23 -0
  69. data/lib/temporalio/api/workflowservice.rb +3 -0
  70. data/lib/temporalio/api.rb +15 -0
  71. data/lib/temporalio/cancellation.rb +170 -0
  72. data/lib/temporalio/client/activity_id_reference.rb +32 -0
  73. data/lib/temporalio/client/async_activity_handle.rb +85 -0
  74. data/lib/temporalio/client/connection/cloud_service.rb +726 -0
  75. data/lib/temporalio/client/connection/operator_service.rb +201 -0
  76. data/lib/temporalio/client/connection/service.rb +42 -0
  77. data/lib/temporalio/client/connection/test_service.rb +111 -0
  78. data/lib/temporalio/client/connection/workflow_service.rb +1251 -0
  79. data/lib/temporalio/client/connection.rb +316 -0
  80. data/lib/temporalio/client/interceptor.rb +455 -0
  81. data/lib/temporalio/client/schedule.rb +991 -0
  82. data/lib/temporalio/client/schedule_handle.rb +126 -0
  83. data/lib/temporalio/client/with_start_workflow_operation.rb +115 -0
  84. data/lib/temporalio/client/workflow_execution.rb +119 -0
  85. data/lib/temporalio/client/workflow_execution_count.rb +36 -0
  86. data/lib/temporalio/client/workflow_execution_status.rb +18 -0
  87. data/lib/temporalio/client/workflow_handle.rb +389 -0
  88. data/lib/temporalio/client/workflow_query_reject_condition.rb +14 -0
  89. data/lib/temporalio/client/workflow_update_handle.rb +65 -0
  90. data/lib/temporalio/client/workflow_update_wait_stage.rb +17 -0
  91. data/lib/temporalio/client.rb +607 -0
  92. data/lib/temporalio/common_enums.rb +41 -0
  93. data/lib/temporalio/contrib/open_telemetry.rb +470 -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 +156 -0
  109. data/lib/temporalio/internal/bridge/3.2/temporalio_bridge.so +0 -0
  110. data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.so +0 -0
  111. data/lib/temporalio/internal/bridge/3.4/temporalio_bridge.so +0 -0
  112. data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +34 -0
  113. data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +31 -0
  114. data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +33 -0
  115. data/lib/temporalio/internal/bridge/api/common/common.rb +27 -0
  116. data/lib/temporalio/internal/bridge/api/core_interface.rb +40 -0
  117. data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +27 -0
  118. data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +33 -0
  119. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +56 -0
  120. data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +57 -0
  121. data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +31 -0
  122. data/lib/temporalio/internal/bridge/api.rb +3 -0
  123. data/lib/temporalio/internal/bridge/client.rb +95 -0
  124. data/lib/temporalio/internal/bridge/runtime.rb +56 -0
  125. data/lib/temporalio/internal/bridge/testing.rb +69 -0
  126. data/lib/temporalio/internal/bridge/worker.rb +85 -0
  127. data/lib/temporalio/internal/bridge.rb +36 -0
  128. data/lib/temporalio/internal/client/implementation.rb +922 -0
  129. data/lib/temporalio/internal/metric.rb +122 -0
  130. data/lib/temporalio/internal/proto_utils.rb +165 -0
  131. data/lib/temporalio/internal/worker/activity_worker.rb +385 -0
  132. data/lib/temporalio/internal/worker/multi_runner.rb +213 -0
  133. data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
  134. data/lib/temporalio/internal/worker/workflow_instance/context.rb +383 -0
  135. data/lib/temporalio/internal/worker/workflow_instance/details.rb +46 -0
  136. data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +32 -0
  137. data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +22 -0
  138. data/lib/temporalio/internal/worker/workflow_instance/handler_execution.rb +25 -0
  139. data/lib/temporalio/internal/worker/workflow_instance/handler_hash.rb +41 -0
  140. data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +97 -0
  141. data/lib/temporalio/internal/worker/workflow_instance/inbound_implementation.rb +62 -0
  142. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +400 -0
  143. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +37 -0
  144. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb +40 -0
  145. data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +183 -0
  146. data/lib/temporalio/internal/worker/workflow_instance.rb +774 -0
  147. data/lib/temporalio/internal/worker/workflow_worker.rb +239 -0
  148. data/lib/temporalio/internal.rb +7 -0
  149. data/lib/temporalio/metric.rb +109 -0
  150. data/lib/temporalio/retry_policy.rb +74 -0
  151. data/lib/temporalio/runtime/metric_buffer.rb +94 -0
  152. data/lib/temporalio/runtime.rb +352 -0
  153. data/lib/temporalio/scoped_logger.rb +96 -0
  154. data/lib/temporalio/search_attributes.rb +356 -0
  155. data/lib/temporalio/testing/activity_environment.rb +160 -0
  156. data/lib/temporalio/testing/workflow_environment.rb +406 -0
  157. data/lib/temporalio/testing.rb +10 -0
  158. data/lib/temporalio/version.rb +5 -0
  159. data/lib/temporalio/worker/activity_executor/fiber.rb +49 -0
  160. data/lib/temporalio/worker/activity_executor/thread_pool.rb +46 -0
  161. data/lib/temporalio/worker/activity_executor.rb +55 -0
  162. data/lib/temporalio/worker/interceptor.rb +365 -0
  163. data/lib/temporalio/worker/thread_pool.rb +237 -0
  164. data/lib/temporalio/worker/tuner.rb +189 -0
  165. data/lib/temporalio/worker/workflow_executor/thread_pool.rb +235 -0
  166. data/lib/temporalio/worker/workflow_executor.rb +26 -0
  167. data/lib/temporalio/worker/workflow_replayer.rb +350 -0
  168. data/lib/temporalio/worker.rb +603 -0
  169. data/lib/temporalio/workflow/activity_cancellation_type.rb +20 -0
  170. data/lib/temporalio/workflow/child_workflow_cancellation_type.rb +21 -0
  171. data/lib/temporalio/workflow/child_workflow_handle.rb +43 -0
  172. data/lib/temporalio/workflow/definition.rb +598 -0
  173. data/lib/temporalio/workflow/external_workflow_handle.rb +41 -0
  174. data/lib/temporalio/workflow/future.rb +151 -0
  175. data/lib/temporalio/workflow/handler_unfinished_policy.rb +13 -0
  176. data/lib/temporalio/workflow/info.rb +104 -0
  177. data/lib/temporalio/workflow/parent_close_policy.rb +19 -0
  178. data/lib/temporalio/workflow/update_info.rb +20 -0
  179. data/lib/temporalio/workflow.rb +575 -0
  180. data/lib/temporalio/workflow_history.rb +47 -0
  181. data/lib/temporalio.rb +11 -0
  182. data/temporalio.gemspec +29 -0
  183. metadata +258 -0
@@ -0,0 +1,470 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require 'opentelemetry' # This import will intentionally fail if the user does not have OTel gem available
5
+ require 'temporalio/client/interceptor'
6
+ require 'temporalio/converters/payload_converter'
7
+ require 'temporalio/worker/interceptor'
8
+
9
+ module Temporalio
10
+ module Contrib
11
+ module OpenTelemetry
12
+ # Tracing interceptor to add OpenTelemetry traces to clients, activities, and workflows.
13
+ class TracingInterceptor
14
+ include Client::Interceptor
15
+ include Worker::Interceptor::Activity
16
+ include Worker::Interceptor::Workflow
17
+
18
+ # @return [OpenTelemetry::Trace::Tracer] Tracer in use.
19
+ attr_reader :tracer
20
+
21
+ # Create interceptor.
22
+ #
23
+ # @param tracer [OpenTelemetry::Trace::Tracer] Tracer to use.
24
+ # @param header_key [String] Temporal header name to serialize spans to/from. Most users should not change this.
25
+ # @param propagator [Object] Propagator to use. Most users should not change this.
26
+ # @param always_create_workflow_spans [Boolean] When false, the default, spans are only created in workflows
27
+ # when an overarching span from the client is present. In cases of starting a workflow elsewhere, e.g. CLI or
28
+ # schedules, a client-created span is not present and workflow spans will not be created. Setting this to true
29
+ # will create spans in workflows no matter what, but there is a risk of them being orphans since they may not
30
+ # have a parent span after replaying.
31
+ def initialize(
32
+ tracer,
33
+ header_key: '_tracer-data',
34
+ propagator: ::OpenTelemetry::Context::Propagation::CompositeTextMapPropagator.compose_propagators(
35
+ [
36
+ ::OpenTelemetry::Trace::Propagation::TraceContext::TextMapPropagator.new,
37
+ ::OpenTelemetry::Baggage::Propagation::TextMapPropagator.new
38
+ ]
39
+ ),
40
+ always_create_workflow_spans: false
41
+ )
42
+ @tracer = tracer
43
+ @header_key = header_key
44
+ @propagator = propagator
45
+ @always_create_workflow_spans = always_create_workflow_spans
46
+ end
47
+
48
+ # @!visibility private
49
+ def intercept_client(next_interceptor)
50
+ ClientOutbound.new(self, next_interceptor)
51
+ end
52
+
53
+ # @!visibility private
54
+ def intercept_activity(next_interceptor)
55
+ ActivityInbound.new(self, next_interceptor)
56
+ end
57
+
58
+ # @!visibility private
59
+ def intercept_workflow(next_interceptor)
60
+ WorkflowInbound.new(self, next_interceptor)
61
+ end
62
+
63
+ # @!visibility private
64
+ def _apply_context_to_headers(headers, context: ::OpenTelemetry::Context.current)
65
+ carrier = {}
66
+ @propagator.inject(carrier, context:)
67
+ headers[@header_key] = carrier unless carrier.empty?
68
+ end
69
+
70
+ # @!visibility private
71
+ def _attach_context(headers)
72
+ context = _context_from_headers(headers)
73
+ ::OpenTelemetry::Context.attach(context) if context
74
+ end
75
+
76
+ # @!visibility private
77
+ def _context_from_headers(headers)
78
+ carrier = headers[@header_key]
79
+ @propagator.extract(carrier) if carrier.is_a?(Hash) && !carrier.empty?
80
+ end
81
+
82
+ # @!visibility private
83
+ def _with_started_span(
84
+ name:,
85
+ kind:,
86
+ attributes: nil,
87
+ outbound_input: nil
88
+ )
89
+ tracer.in_span(name, attributes:, kind:) do
90
+ _apply_context_to_headers(outbound_input.headers) if outbound_input
91
+ yield
92
+ end
93
+ end
94
+
95
+ # @!visibility private
96
+ def _always_create_workflow_spans
97
+ @always_create_workflow_spans
98
+ end
99
+
100
+ # @!visibility private
101
+ class ClientOutbound < Client::Interceptor::Outbound
102
+ def initialize(root, next_interceptor)
103
+ super(next_interceptor)
104
+ @root = root
105
+ end
106
+
107
+ # @!visibility private
108
+ def start_workflow(input)
109
+ @root._with_started_span(
110
+ name: "StartWorkflow:#{input.workflow}",
111
+ kind: :client,
112
+ attributes: { 'temporalWorkflowID' => input.workflow_id },
113
+ outbound_input: input
114
+ ) { super }
115
+ end
116
+
117
+ # @!visibility private
118
+ def start_update_with_start_workflow(input)
119
+ @root._with_started_span(
120
+ name: "UpdateWithStartWorkflow:#{input.update}",
121
+ kind: :client,
122
+ attributes: { 'temporalWorkflowID' => input.start_workflow_operation.options.id,
123
+ 'temporalUpdateID' => input.update_id },
124
+ outbound_input: input
125
+ ) do
126
+ # Also add to start headers
127
+ if input.headers[@header_key]
128
+ input.start_workflow_operation.options.headers[@header_key] = input.headers[@header_key]
129
+ end
130
+ super
131
+ end
132
+ end
133
+
134
+ # @!visibility private
135
+ def signal_with_start_workflow(input)
136
+ @root._with_started_span(
137
+ name: "SignalWithStartWorkflow:#{input.workflow}",
138
+ kind: :client,
139
+ attributes: { 'temporalWorkflowID' => input.start_workflow_operation.options.id },
140
+ outbound_input: input
141
+ ) do
142
+ # Also add to start headers
143
+ if input.headers[@header_key]
144
+ input.start_workflow_operation.options.headers[@header_key] = input.headers[@header_key]
145
+ end
146
+ super
147
+ end
148
+ end
149
+
150
+ # @!visibility private
151
+ def signal_workflow(input)
152
+ @root._with_started_span(
153
+ name: "SignalWorkflow:#{input.signal}",
154
+ kind: :client,
155
+ attributes: { 'temporalWorkflowID' => input.workflow_id },
156
+ outbound_input: input
157
+ ) { super }
158
+ end
159
+
160
+ # @!visibility private
161
+ def query_workflow(input)
162
+ @root._with_started_span(
163
+ name: "QueryWorkflow:#{input.query}",
164
+ kind: :client,
165
+ attributes: { 'temporalWorkflowID' => input.workflow_id },
166
+ outbound_input: input
167
+ ) { super }
168
+ end
169
+
170
+ # @!visibility private
171
+ def start_workflow_update(input)
172
+ @root._with_started_span(
173
+ name: "StartWorkflowUpdate:#{input.update}",
174
+ kind: :client,
175
+ attributes: { 'temporalWorkflowID' => input.workflow_id, 'temporalUpdateID' => input.update_id },
176
+ outbound_input: input
177
+ ) { super }
178
+ end
179
+ end
180
+
181
+ # @!visibility private
182
+ class ActivityInbound < Worker::Interceptor::Activity::Inbound
183
+ def initialize(root, next_interceptor)
184
+ super(next_interceptor)
185
+ @root = root
186
+ end
187
+
188
+ # @!visibility private
189
+ def execute(input)
190
+ @root._attach_context(input.headers)
191
+ info = Activity::Context.current.info
192
+ @root._with_started_span(
193
+ name: "RunActivity:#{info.activity_type}",
194
+ kind: :server,
195
+ attributes: {
196
+ 'temporalWorkflowID' => info.workflow_id,
197
+ 'temporalRunID' => info.workflow_run_id,
198
+ 'temporalActivityID' => info.activity_id
199
+ }
200
+ ) { super }
201
+ end
202
+ end
203
+
204
+ # @!visibility private
205
+ class WorkflowInbound < Worker::Interceptor::Workflow::Inbound
206
+ def initialize(root, next_interceptor)
207
+ super(next_interceptor)
208
+ @root = root
209
+ end
210
+
211
+ # @!visibility private
212
+ def init(outbound)
213
+ # Set root on storage
214
+ Temporalio::Workflow.storage[:__temporal_opentelemetry_tracing_interceptor] = @root
215
+ super(WorkflowOutbound.new(@root, outbound))
216
+ end
217
+
218
+ # @!visibility private
219
+ def execute(input)
220
+ @root._attach_context(Temporalio::Workflow.info.headers)
221
+ Workflow.with_completed_span("RunWorkflow:#{Temporalio::Workflow.info.workflow_type}", kind: :server) do
222
+ super
223
+ ensure
224
+ Workflow.completed_span(
225
+ "CompleteWorkflow:#{Temporalio::Workflow.info.workflow_type}",
226
+ kind: :internal,
227
+ exception: $ERROR_INFO # steep:ignore
228
+ )
229
+ end
230
+ end
231
+
232
+ # @!visibility private
233
+ def handle_signal(input)
234
+ @root._attach_context(Temporalio::Workflow.info.headers)
235
+ Workflow.with_completed_span(
236
+ "HandleSignal:#{input.signal}",
237
+ links: _links_from_headers(input.headers),
238
+ kind: :server
239
+ ) do
240
+ super
241
+ rescue Exception => e # rubocop:disable Lint/RescueException
242
+ Workflow.completed_span("FailHandleSignal:#{input.signal}", kind: :internal, exception: e)
243
+ raise
244
+ end
245
+ end
246
+
247
+ # @!visibility private
248
+ def handle_query(input)
249
+ @root._attach_context(Temporalio::Workflow.info.headers)
250
+ Workflow.with_completed_span(
251
+ "HandleQuery:#{input.query}",
252
+ links: _links_from_headers(input.headers),
253
+ kind: :server,
254
+ even_during_replay: true
255
+ ) do
256
+ super
257
+ rescue Exception => e # rubocop:disable Lint/RescueException
258
+ Workflow.completed_span(
259
+ "FailHandleQuery:#{input.query}",
260
+ kind: :internal,
261
+ exception: e,
262
+ even_during_replay: true
263
+ )
264
+ raise
265
+ end
266
+ end
267
+
268
+ # @!visibility private
269
+ def validate_update(input)
270
+ @root._attach_context(Temporalio::Workflow.info.headers)
271
+ Workflow.with_completed_span(
272
+ "ValidateUpdate:#{input.update}",
273
+ attributes: { 'temporalUpdateID' => input.id },
274
+ links: _links_from_headers(input.headers),
275
+ kind: :server,
276
+ even_during_replay: true
277
+ ) do
278
+ super
279
+ rescue Exception => e # rubocop:disable Lint/RescueException
280
+ Workflow.completed_span(
281
+ "FailValidateUpdate:#{input.update}",
282
+ attributes: { 'temporalUpdateID' => input.id },
283
+ kind: :internal,
284
+ exception: e,
285
+ even_during_replay: true
286
+ )
287
+ raise
288
+ end
289
+ end
290
+
291
+ # @!visibility private
292
+ def handle_update(input)
293
+ @root._attach_context(Temporalio::Workflow.info.headers)
294
+ Workflow.with_completed_span(
295
+ "HandleUpdate:#{input.update}",
296
+ attributes: { 'temporalUpdateID' => input.id },
297
+ links: _links_from_headers(input.headers),
298
+ kind: :server
299
+ ) do
300
+ super
301
+ rescue Exception => e # rubocop:disable Lint/RescueException
302
+ Workflow.completed_span(
303
+ "FailHandleUpdate:#{input.update}",
304
+ attributes: { 'temporalUpdateID' => input.id },
305
+ kind: :internal,
306
+ exception: e
307
+ )
308
+ raise
309
+ end
310
+ end
311
+
312
+ # @!visibility private
313
+ def _links_from_headers(headers)
314
+ context = @root._context_from_headers(headers)
315
+ span = ::OpenTelemetry::Trace.current_span(context) if context
316
+ if span && span != ::OpenTelemetry::Trace::Span::INVALID
317
+ [::OpenTelemetry::Trace::Link.new(span.context)]
318
+ else
319
+ []
320
+ end
321
+ end
322
+ end
323
+
324
+ # @!visibility private
325
+ class WorkflowOutbound < Worker::Interceptor::Workflow::Outbound
326
+ def initialize(root, next_interceptor)
327
+ super(next_interceptor)
328
+ @root = root
329
+ end
330
+
331
+ # @!visibility private
332
+ def execute_activity(input)
333
+ _apply_span_to_headers(input.headers,
334
+ Workflow.completed_span("StartActivity:#{input.activity}", kind: :client))
335
+ super
336
+ end
337
+
338
+ # @!visibility private
339
+ def execute_local_activity(input)
340
+ _apply_span_to_headers(input.headers,
341
+ Workflow.completed_span("StartActivity:#{input.activity}", kind: :client))
342
+ super
343
+ end
344
+
345
+ # @!visibility private
346
+ def initialize_continue_as_new_error(input)
347
+ # Just apply the current context to headers
348
+ @root._apply_context_to_headers(input.error.headers)
349
+ super
350
+ end
351
+
352
+ # @!visibility private
353
+ def signal_child_workflow(input)
354
+ _apply_span_to_headers(input.headers,
355
+ Workflow.completed_span("SignalChildWorkflow:#{input.signal}", kind: :client))
356
+ super
357
+ end
358
+
359
+ # @!visibility private
360
+ def signal_external_workflow(input)
361
+ _apply_span_to_headers(input.headers,
362
+ Workflow.completed_span("SignalExternalWorkflow:#{input.signal}", kind: :client))
363
+ super
364
+ end
365
+
366
+ # @!visibility private
367
+ def start_child_workflow(input)
368
+ _apply_span_to_headers(input.headers,
369
+ Workflow.completed_span("StartChildWorkflow:#{input.workflow}", kind: :client))
370
+ super
371
+ end
372
+
373
+ # @!visibility private
374
+ def _apply_span_to_headers(headers, span)
375
+ @root._apply_context_to_headers(headers, context: ::OpenTelemetry::Trace.context_with_span(span)) if span
376
+ end
377
+ end
378
+
379
+ private_constant :ClientOutbound
380
+ private_constant :ActivityInbound
381
+ private_constant :WorkflowInbound
382
+ private_constant :WorkflowOutbound
383
+ end
384
+
385
+ # Contains workflow methods that can be used for OpenTelemetry.
386
+ module Workflow
387
+ # Create a completed span and execute block with the span set on the context.
388
+ #
389
+ # @param name [String] Span name.
390
+ # @param attributes [Hash] Span attributes. These will have workflow and run ID automatically added.
391
+ # @param links [Array, nil] Span links.
392
+ # @param kind [Symbol, nil] Span kind.
393
+ # @param exception [Exception, nil] Exception to record on the span.
394
+ # @param even_during_replay [Boolean] Set to true to record this span even during replay. Most users should
395
+ # never set this.
396
+ # @yield Block to call. It is UNSAFE to expect any parameters in this block.
397
+ # @return [Object] Result of the block.
398
+ def self.with_completed_span(
399
+ name,
400
+ attributes: {},
401
+ links: nil,
402
+ kind: nil,
403
+ exception: nil,
404
+ even_during_replay: false
405
+ )
406
+ span = completed_span(name, attributes:, links:, kind:, exception:, even_during_replay:)
407
+ if span
408
+ ::OpenTelemetry::Trace.with_span(span) do # rubocop:disable Style/ExplicitBlockArgument
409
+ # Yield with no parameters
410
+ yield
411
+ end
412
+ else
413
+ yield
414
+ end
415
+ end
416
+
417
+ # Create a completed span only if not replaying (or `even_during_replay` is true).
418
+ #
419
+ # @note WARNING: It is UNSAFE to rely on the result of this method as it may be different/absent on replay.
420
+ #
421
+ # @param name [String] Span name.
422
+ # @param attributes [Hash] Span attributes. These will have workflow and run ID automatically added.
423
+ # @param links [Array, nil] Span links.
424
+ # @param kind [Symbol, nil] Span kind.
425
+ # @param exception [Exception, nil] Exception to record on the span.
426
+ # @param even_during_replay [Boolean] Set to true to record this span even during replay. Most users should
427
+ # never set this.
428
+ # @return [OpenTelemetry::Trace::Span, nil] Span if one was created. WARNING: It is UNSAFE to use this value.
429
+ def self.completed_span(
430
+ name,
431
+ attributes: {},
432
+ links: nil,
433
+ kind: nil,
434
+ exception: nil,
435
+ even_during_replay: false
436
+ )
437
+ # Get root interceptor, which also checks if in workflow
438
+ root = Temporalio::Workflow.storage[:__temporal_opentelemetry_tracing_interceptor] #: TracingInterceptor?
439
+ raise 'Tracing interceptor not configured' unless root
440
+
441
+ # Do nothing if replaying and not wanted during replay
442
+ return nil if !even_during_replay && Temporalio::Workflow::Unsafe.replaying?
443
+
444
+ # If there is no span on the context and the user hasn't opted in to always creating, do not create. This
445
+ # prevents orphans if there was no span originally created from the client start-workflow call.
446
+ if ::OpenTelemetry::Trace.current_span == ::OpenTelemetry::Trace::Span::INVALID &&
447
+ !root._always_create_workflow_spans
448
+ return nil
449
+ end
450
+
451
+ # Create attributes, adding user-defined ones
452
+ attributes = { 'temporalWorkflowID' => Temporalio::Workflow.info.workflow_id,
453
+ 'temporalRunID' => Temporalio::Workflow.info.run_id }.merge(attributes)
454
+
455
+ # Create span, which has to be done with illegal call disabling because OTel asks for full exception message
456
+ # which uses error highlighting and such which accesses File#path
457
+ Temporalio::Workflow::Unsafe.illegal_call_tracing_disabled do
458
+ time = Temporalio::Workflow.now
459
+ timestamp = (time.to_i * 1_000_000_000) + time.nsec
460
+ span = root.tracer.start_span(name, attributes:, links:, start_timestamp: timestamp, kind:) # steep:ignore
461
+ # Record exception if present
462
+ span.record_exception(exception) if exception
463
+ # Finish the span (returns self)
464
+ span.finish(end_timestamp: timestamp)
465
+ end
466
+ end
467
+ end
468
+ end
469
+ end
470
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/api'
4
+ require 'temporalio/converters/failure_converter'
5
+ require 'temporalio/converters/payload_converter'
6
+
7
+ module Temporalio
8
+ module Converters
9
+ # Data converter for converting/encoding payloads to/from Ruby values.
10
+ class DataConverter
11
+ # @return [PayloadConverter] Payload converter.
12
+ attr_reader :payload_converter
13
+
14
+ # @return [FailureConverter] Failure converter.
15
+ attr_reader :failure_converter
16
+
17
+ # @return [PayloadCodec, nil] Optional codec for encoding/decoding payload bytes such as for encryption.
18
+ attr_reader :payload_codec
19
+
20
+ # @return [DataConverter] Default data converter.
21
+ def self.default
22
+ @default ||= DataConverter.new
23
+ end
24
+
25
+ # Create data converter.
26
+ #
27
+ # @param payload_converter [PayloadConverter] Payload converter to use.
28
+ # @param failure_converter [FailureConverter] Failure converter to use.
29
+ # @param payload_codec [PayloadCodec, nil] Payload codec to use.
30
+ def initialize(
31
+ payload_converter: PayloadConverter.default,
32
+ failure_converter: FailureConverter.default,
33
+ payload_codec: nil
34
+ )
35
+ @payload_converter = payload_converter
36
+ @failure_converter = failure_converter
37
+ @payload_codec = payload_codec
38
+ end
39
+
40
+ # Convert a Ruby value to a payload and encode it.
41
+ #
42
+ # @param value [Object] Ruby value.
43
+ # @return [Api::Common::V1::Payload] Converted and encoded payload.
44
+ def to_payload(value)
45
+ payload = payload_converter.to_payload(value)
46
+ payload = payload_codec.encode([payload]).first if payload_codec
47
+ payload
48
+ end
49
+
50
+ # Convert multiple Ruby values to a payload set and encode it.
51
+ #
52
+ # @param values [Object] Ruby values, converted to array via {::Array}.
53
+ # @return [Api::Common::V1::Payloads] Converted and encoded payload set.
54
+ def to_payloads(values)
55
+ payloads = payload_converter.to_payloads(values)
56
+ payloads.payloads.replace(payload_codec.encode(payloads.payloads)) if payload_codec && !payloads.payloads.empty?
57
+ payloads
58
+ end
59
+
60
+ # Decode and convert a payload to a Ruby value.
61
+ #
62
+ # @param payload [Api::Common::V1::Payload] Encoded payload.
63
+ # @return [Object] Decoded and converted Ruby value.
64
+ def from_payload(payload)
65
+ payload = payload_codec.decode([payload]).first if payload_codec
66
+ payload_converter.from_payload(payload)
67
+ end
68
+
69
+ # Decode and convert a payload set to Ruby values.
70
+ #
71
+ # @param payloads [Api::Common::V1::Payloads, nil] Encoded payload set.
72
+ # @return [Array<Object>] Decoded and converted Ruby values.
73
+ def from_payloads(payloads)
74
+ return [] unless payloads && !payloads.payloads.empty?
75
+
76
+ if payload_codec && !payloads.payloads.empty?
77
+ payloads = Api::Common::V1::Payloads.new(payloads: payload_codec.decode(payloads.payloads))
78
+ end
79
+ payload_converter.from_payloads(payloads)
80
+ end
81
+
82
+ # Convert a Ruby error to a Temporal failure and encode it.
83
+ #
84
+ # @param error [Exception] Ruby error.
85
+ # @return [Api::Failure::V1::Failure] Converted and encoded failure.
86
+ def to_failure(error)
87
+ failure_converter.to_failure(error, self)
88
+ end
89
+
90
+ # Decode and convert a Temporal failure to a Ruby error.
91
+ #
92
+ # @param failure [Api::Failure::V1::Failure] Encoded failure.
93
+ # @return [Exception] Decoded and converted Ruby error.
94
+ def from_failure(failure)
95
+ failure_converter.from_failure(failure, self)
96
+ end
97
+ end
98
+ end
99
+ end