temporalio 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +2 -0
- data/Cargo.lock +980 -583
- data/Cargo.toml +2 -2
- data/Gemfile +7 -3
- data/README.md +769 -54
- data/Rakefile +10 -296
- data/ext/Cargo.toml +2 -0
- data/lib/temporalio/activity/complete_async_error.rb +1 -1
- data/lib/temporalio/activity/context.rb +18 -2
- data/lib/temporalio/activity/definition.rb +180 -65
- data/lib/temporalio/activity/info.rb +25 -21
- data/lib/temporalio/activity.rb +2 -59
- data/lib/temporalio/api/activity/v1/message.rb +25 -0
- data/lib/temporalio/api/batch/v1/message.rb +6 -1
- data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
- data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +34 -1
- data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +1 -1
- data/lib/temporalio/api/cloud/identity/v1/message.rb +6 -1
- data/lib/temporalio/api/cloud/namespace/v1/message.rb +8 -1
- data/lib/temporalio/api/cloud/nexus/v1/message.rb +31 -0
- data/lib/temporalio/api/cloud/operation/v1/message.rb +2 -1
- data/lib/temporalio/api/cloud/region/v1/message.rb +2 -1
- data/lib/temporalio/api/cloud/resource/v1/message.rb +23 -0
- data/lib/temporalio/api/cloud/sink/v1/message.rb +24 -0
- data/lib/temporalio/api/cloud/usage/v1/message.rb +31 -0
- data/lib/temporalio/api/command/v1/message.rb +1 -1
- data/lib/temporalio/api/common/v1/message.rb +8 -1
- data/lib/temporalio/api/deployment/v1/message.rb +38 -0
- data/lib/temporalio/api/enums/v1/batch_operation.rb +1 -1
- data/lib/temporalio/api/enums/v1/common.rb +1 -1
- data/lib/temporalio/api/enums/v1/deployment.rb +23 -0
- data/lib/temporalio/api/enums/v1/event_type.rb +1 -1
- data/lib/temporalio/api/enums/v1/failed_cause.rb +1 -1
- data/lib/temporalio/api/enums/v1/nexus.rb +21 -0
- data/lib/temporalio/api/enums/v1/reset.rb +1 -1
- data/lib/temporalio/api/enums/v1/workflow.rb +2 -1
- data/lib/temporalio/api/errordetails/v1/message.rb +3 -1
- data/lib/temporalio/api/failure/v1/message.rb +3 -1
- data/lib/temporalio/api/history/v1/message.rb +3 -1
- data/lib/temporalio/api/nexus/v1/message.rb +3 -2
- data/lib/temporalio/api/operatorservice/v1/service.rb +1 -1
- data/lib/temporalio/api/payload_visitor.rb +1581 -0
- data/lib/temporalio/api/query/v1/message.rb +2 -1
- data/lib/temporalio/api/schedule/v1/message.rb +2 -1
- data/lib/temporalio/api/taskqueue/v1/message.rb +4 -1
- data/lib/temporalio/api/testservice/v1/request_response.rb +31 -0
- data/lib/temporalio/api/testservice/v1/service.rb +23 -0
- data/lib/temporalio/api/workflow/v1/message.rb +9 -1
- data/lib/temporalio/api/workflowservice/v1/request_response.rb +46 -2
- data/lib/temporalio/api/workflowservice/v1/service.rb +1 -1
- data/lib/temporalio/api.rb +2 -0
- data/lib/temporalio/cancellation.rb +34 -14
- data/lib/temporalio/client/async_activity_handle.rb +12 -37
- data/lib/temporalio/client/connection/cloud_service.rb +309 -231
- data/lib/temporalio/client/connection/operator_service.rb +36 -84
- data/lib/temporalio/client/connection/service.rb +6 -5
- data/lib/temporalio/client/connection/test_service.rb +111 -0
- data/lib/temporalio/client/connection/workflow_service.rb +474 -441
- data/lib/temporalio/client/connection.rb +90 -44
- data/lib/temporalio/client/interceptor.rb +199 -60
- data/lib/temporalio/client/schedule.rb +991 -0
- data/lib/temporalio/client/schedule_handle.rb +126 -0
- data/lib/temporalio/client/with_start_workflow_operation.rb +115 -0
- data/lib/temporalio/client/workflow_execution.rb +26 -10
- data/lib/temporalio/client/workflow_handle.rb +41 -98
- data/lib/temporalio/client/workflow_update_handle.rb +3 -5
- data/lib/temporalio/client.rb +247 -44
- data/lib/temporalio/common_enums.rb +17 -0
- data/lib/temporalio/contrib/open_telemetry.rb +470 -0
- data/lib/temporalio/converters/data_converter.rb +4 -7
- data/lib/temporalio/converters/failure_converter.rb +5 -3
- data/lib/temporalio/converters/payload_converter/composite.rb +4 -0
- data/lib/temporalio/converters/payload_converter.rb +6 -8
- data/lib/temporalio/converters/raw_value.rb +20 -0
- data/lib/temporalio/error/failure.rb +1 -1
- data/lib/temporalio/error.rb +11 -2
- data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +1 -1
- data/lib/temporalio/internal/bridge/api/common/common.rb +2 -1
- data/lib/temporalio/internal/bridge/api/core_interface.rb +5 -1
- data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +33 -0
- data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +5 -1
- data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +4 -1
- data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +2 -1
- data/lib/temporalio/internal/bridge/client.rb +11 -6
- data/lib/temporalio/internal/bridge/runtime.rb +3 -0
- data/lib/temporalio/internal/bridge/testing.rb +23 -0
- data/lib/temporalio/internal/bridge/worker.rb +2 -0
- data/lib/temporalio/internal/bridge.rb +1 -1
- data/lib/temporalio/internal/client/implementation.rb +468 -71
- data/lib/temporalio/internal/metric.rb +122 -0
- data/lib/temporalio/internal/proto_utils.rb +118 -7
- data/lib/temporalio/internal/worker/activity_worker.rb +69 -29
- data/lib/temporalio/internal/worker/multi_runner.rb +53 -9
- data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
- data/lib/temporalio/internal/worker/workflow_instance/context.rb +383 -0
- data/lib/temporalio/internal/worker/workflow_instance/details.rb +46 -0
- data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +32 -0
- data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +22 -0
- data/lib/temporalio/internal/worker/workflow_instance/handler_execution.rb +25 -0
- data/lib/temporalio/internal/worker/workflow_instance/handler_hash.rb +41 -0
- data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +97 -0
- data/lib/temporalio/internal/worker/workflow_instance/inbound_implementation.rb +62 -0
- data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +400 -0
- data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +37 -0
- data/lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb +40 -0
- data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +183 -0
- data/lib/temporalio/internal/worker/workflow_instance.rb +774 -0
- data/lib/temporalio/internal/worker/workflow_worker.rb +239 -0
- data/lib/temporalio/metric.rb +109 -0
- data/lib/temporalio/retry_policy.rb +37 -14
- data/lib/temporalio/runtime/metric_buffer.rb +94 -0
- data/lib/temporalio/runtime.rb +160 -79
- data/lib/temporalio/search_attributes.rb +93 -37
- data/lib/temporalio/testing/activity_environment.rb +44 -16
- data/lib/temporalio/testing/workflow_environment.rb +276 -7
- data/lib/temporalio/version.rb +1 -1
- data/lib/temporalio/worker/activity_executor/thread_pool.rb +9 -217
- data/lib/temporalio/worker/activity_executor.rb +3 -3
- data/lib/temporalio/worker/interceptor.rb +343 -66
- data/lib/temporalio/worker/thread_pool.rb +237 -0
- data/lib/temporalio/worker/tuner.rb +38 -0
- data/lib/temporalio/worker/workflow_executor/thread_pool.rb +235 -0
- data/lib/temporalio/worker/workflow_executor.rb +26 -0
- data/lib/temporalio/worker/workflow_replayer.rb +350 -0
- data/lib/temporalio/worker.rb +235 -58
- data/lib/temporalio/workflow/activity_cancellation_type.rb +20 -0
- data/lib/temporalio/workflow/child_workflow_cancellation_type.rb +21 -0
- data/lib/temporalio/workflow/child_workflow_handle.rb +43 -0
- data/lib/temporalio/workflow/definition.rb +598 -0
- data/lib/temporalio/workflow/external_workflow_handle.rb +41 -0
- data/lib/temporalio/workflow/future.rb +151 -0
- data/lib/temporalio/workflow/handler_unfinished_policy.rb +13 -0
- data/lib/temporalio/workflow/info.rb +104 -0
- data/lib/temporalio/workflow/parent_close_policy.rb +19 -0
- data/lib/temporalio/workflow/update_info.rb +20 -0
- data/lib/temporalio/workflow.rb +575 -0
- data/lib/temporalio/workflow_history.rb +26 -1
- data/lib/temporalio.rb +4 -0
- data/temporalio.gemspec +4 -3
- metadata +73 -10
@@ -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
|
@@ -8,10 +8,10 @@ module Temporalio
|
|
8
8
|
module Converters
|
9
9
|
# Data converter for converting/encoding payloads to/from Ruby values.
|
10
10
|
class DataConverter
|
11
|
-
# @return [PayloadConverter] Payload converter.
|
11
|
+
# @return [PayloadConverter] Payload converter.
|
12
12
|
attr_reader :payload_converter
|
13
13
|
|
14
|
-
# @return [FailureConverter] Failure converter.
|
14
|
+
# @return [FailureConverter] Failure converter.
|
15
15
|
attr_reader :failure_converter
|
16
16
|
|
17
17
|
# @return [PayloadCodec, nil] Optional codec for encoding/decoding payload bytes such as for encryption.
|
@@ -24,17 +24,14 @@ module Temporalio
|
|
24
24
|
|
25
25
|
# Create data converter.
|
26
26
|
#
|
27
|
-
# @param payload_converter [PayloadConverter] Payload converter to use.
|
28
|
-
# @param failure_converter [FailureConverter] Failure converter to use.
|
27
|
+
# @param payload_converter [PayloadConverter] Payload converter to use.
|
28
|
+
# @param failure_converter [FailureConverter] Failure converter to use.
|
29
29
|
# @param payload_codec [PayloadCodec, nil] Payload codec to use.
|
30
30
|
def initialize(
|
31
31
|
payload_converter: PayloadConverter.default,
|
32
32
|
failure_converter: FailureConverter.default,
|
33
33
|
payload_codec: nil
|
34
34
|
)
|
35
|
-
raise 'Payload converter not shareable' unless Ractor.shareable?(payload_converter)
|
36
|
-
raise 'Failure converter not shareable' unless Ractor.shareable?(failure_converter)
|
37
|
-
|
38
35
|
@payload_converter = payload_converter
|
39
36
|
@failure_converter = failure_converter
|
40
37
|
@payload_codec = payload_codec
|
@@ -10,7 +10,7 @@ module Temporalio
|
|
10
10
|
class FailureConverter
|
11
11
|
# @return [FailureConverter] Default failure converter.
|
12
12
|
def self.default
|
13
|
-
@default ||=
|
13
|
+
@default ||= FailureConverter.new
|
14
14
|
end
|
15
15
|
|
16
16
|
# @return [Boolean] If +true+, the message and stack trace of the failure will be moved into the encoded attribute
|
@@ -85,7 +85,7 @@ module Temporalio
|
|
85
85
|
)
|
86
86
|
else
|
87
87
|
failure.application_failure_info = Api::Failure::V1::ApplicationFailureInfo.new(
|
88
|
-
type: error.class.name
|
88
|
+
type: error.class.name
|
89
89
|
)
|
90
90
|
end
|
91
91
|
|
@@ -130,7 +130,9 @@ module Temporalio
|
|
130
130
|
*converter.from_payloads(failure.application_failure_info.details),
|
131
131
|
type: Internal::ProtoUtils.string_or(failure.application_failure_info.type),
|
132
132
|
non_retryable: failure.application_failure_info.non_retryable,
|
133
|
-
next_retry_delay:
|
133
|
+
next_retry_delay: Internal::ProtoUtils.duration_to_seconds(
|
134
|
+
failure.application_failure_info.next_retry_delay
|
135
|
+
)
|
134
136
|
)
|
135
137
|
elsif failure.timeout_failure_info
|
136
138
|
Error::TimeoutError.new(
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'temporalio/api'
|
4
4
|
require 'temporalio/converters/payload_converter'
|
5
|
+
require 'temporalio/converters/raw_value'
|
5
6
|
|
6
7
|
module Temporalio
|
7
8
|
module Converters
|
@@ -34,6 +35,9 @@ module Temporalio
|
|
34
35
|
# @return [Api::Common::V1::Payload] Converted payload.
|
35
36
|
# @raise [ConverterNotFound] If no converters can process the value.
|
36
37
|
def to_payload(value)
|
38
|
+
# As a special case, raw values just return the payload within
|
39
|
+
return value.payload if value.is_a?(RawValue)
|
40
|
+
|
37
41
|
converters.each_value do |converter|
|
38
42
|
payload = converter.to_payload(value)
|
39
43
|
return payload unless payload.nil?
|
@@ -22,14 +22,12 @@ module Temporalio
|
|
22
22
|
# @param json_generate_options [Hash] Options for {::JSON.generate}.
|
23
23
|
# @return [PayloadConverter::Composite] Created payload converter.
|
24
24
|
def self.new_with_defaults(json_parse_options: { create_additions: true }, json_generate_options: {})
|
25
|
-
|
26
|
-
PayloadConverter::
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
PayloadConverter::JSONPlain.new(parse_options: json_parse_options, generate_options: json_generate_options)
|
32
|
-
)
|
25
|
+
PayloadConverter::Composite.new(
|
26
|
+
PayloadConverter::BinaryNull.new,
|
27
|
+
PayloadConverter::BinaryPlain.new,
|
28
|
+
PayloadConverter::JSONProtobuf.new,
|
29
|
+
PayloadConverter::BinaryProtobuf.new,
|
30
|
+
PayloadConverter::JSONPlain.new(parse_options: json_parse_options, generate_options: json_generate_options)
|
33
31
|
)
|
34
32
|
end
|
35
33
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Temporalio
|
4
|
+
module Converters
|
5
|
+
# Raw value wrapper that has the raw payload. When raw args are configured at implementation time, the inbound
|
6
|
+
# arguments will be instances of this class. When instances of this class are sent outbound or returned from
|
7
|
+
# inbound calls, the raw payload will be serialized instead of applying traditional conversion.
|
8
|
+
class RawValue
|
9
|
+
# @return [Api::Common::V1::Payload] Payload.
|
10
|
+
attr_reader :payload
|
11
|
+
|
12
|
+
# Create a raw value.
|
13
|
+
#
|
14
|
+
# @param payload [Api::Common::V1::Payload] Payload.
|
15
|
+
def initialize(payload)
|
16
|
+
@payload = payload
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -17,7 +17,7 @@ module Temporalio
|
|
17
17
|
# @return [String] Workflow type name of the already-started workflow.
|
18
18
|
attr_reader :workflow_type
|
19
19
|
|
20
|
-
# @return [String] Run ID of the already-started workflow if this was raised by the client.
|
20
|
+
# @return [String, nil] Run ID of the already-started workflow if this was raised by the client.
|
21
21
|
attr_reader :run_id
|
22
22
|
|
23
23
|
# @!visibility private
|
data/lib/temporalio/error.rb
CHANGED
@@ -35,8 +35,8 @@ module Temporalio
|
|
35
35
|
# Error that is returned from when a workflow is unsuccessful.
|
36
36
|
class WorkflowFailedError < Error
|
37
37
|
# @!visibility private
|
38
|
-
def initialize
|
39
|
-
super
|
38
|
+
def initialize(message = 'Workflow execution failed')
|
39
|
+
super
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
@@ -87,6 +87,14 @@ module Temporalio
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
+
# Error when a schedule is already running.
|
91
|
+
class ScheduleAlreadyRunningError < Error
|
92
|
+
# @!visibility private
|
93
|
+
def initialize
|
94
|
+
super('Schedule already running')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
90
98
|
# Error that occurs when an async activity handle tries to heartbeat and the activity is marked as canceled.
|
91
99
|
class AsyncActivityCanceledError < Error
|
92
100
|
# @!visibility private
|
@@ -116,6 +124,7 @@ module Temporalio
|
|
116
124
|
|
117
125
|
def create_grpc_status
|
118
126
|
return Api::Common::V1::GrpcStatus.new(code: @code) unless @raw_grpc_status
|
127
|
+
return @raw_grpc_status if @raw_grpc_status.is_a?(Api::Common::V1::GrpcStatus)
|
119
128
|
|
120
129
|
Api::Common::V1::GrpcStatus.decode(@raw_grpc_status)
|
121
130
|
end
|
@@ -10,7 +10,7 @@ require 'temporalio/api/common/v1/message'
|
|
10
10
|
require 'temporalio/internal/bridge/api/common/common'
|
11
11
|
|
12
12
|
|
13
|
-
descriptor_data = "\n3temporal/sdk/core/activity_task/activity_task.proto\x12\x15\x63oresdk.activity_task\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a$temporal/api/common/v1/message.proto\x1a%temporal/sdk/core/common/common.proto\"\x8d\x01\n\x0c\x41\x63tivityTask\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12-\n\x05start\x18\x03 \x01(\x0b\x32\x1c.coresdk.activity_task.StartH\x00\x12/\n\x06\x63\x61ncel\x18\x04 \x01(\x0b\x32\x1d.coresdk.activity_task.CancelH\x00\x42\t\n\x07variant\"\
|
13
|
+
descriptor_data = "\n3temporal/sdk/core/activity_task/activity_task.proto\x12\x15\x63oresdk.activity_task\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a$temporal/api/common/v1/message.proto\x1a%temporal/sdk/core/common/common.proto\"\x8d\x01\n\x0c\x41\x63tivityTask\x12\x12\n\ntask_token\x18\x01 \x01(\x0c\x12-\n\x05start\x18\x03 \x01(\x0b\x32\x1c.coresdk.activity_task.StartH\x00\x12/\n\x06\x63\x61ncel\x18\x04 \x01(\x0b\x32\x1d.coresdk.activity_task.CancelH\x00\x42\t\n\x07variant\"\xa1\x07\n\x05Start\x12\x1a\n\x12workflow_namespace\x18\x01 \x01(\t\x12\x15\n\rworkflow_type\x18\x02 \x01(\t\x12\x45\n\x12workflow_execution\x18\x03 \x01(\x0b\x32).temporal.api.common.v1.WorkflowExecution\x12\x13\n\x0b\x61\x63tivity_id\x18\x04 \x01(\t\x12\x15\n\ractivity_type\x18\x05 \x01(\t\x12\x45\n\rheader_fields\x18\x06 \x03(\x0b\x32..coresdk.activity_task.Start.HeaderFieldsEntry\x12.\n\x05input\x18\x07 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12:\n\x11heartbeat_details\x18\x08 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\x32\n\x0escheduled_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x42\n\x1e\x63urrent_attempt_scheduled_time\x18\n \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x30\n\x0cstarted_time\x18\x0b \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07\x61ttempt\x18\x0c \x01(\r\x12<\n\x19schedule_to_close_timeout\x18\r \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\x0e \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11heartbeat_timeout\x18\x0f \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x10 \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x32\n\x08priority\x18\x12 \x01(\x0b\x32 .temporal.api.common.v1.Priority\x12\x10\n\x08is_local\x18\x11 \x01(\x08\x1aT\n\x11HeaderFieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01\"E\n\x06\x43\x61ncel\x12;\n\x06reason\x18\x01 \x01(\x0e\x32+.coresdk.activity_task.ActivityCancelReason*X\n\x14\x41\x63tivityCancelReason\x12\r\n\tNOT_FOUND\x10\x00\x12\r\n\tCANCELLED\x10\x01\x12\r\n\tTIMED_OUT\x10\x02\x12\x13\n\x0fWORKER_SHUTDOWN\x10\x03\x42\x32\xea\x02/Temporalio::Internal::Bridge::Api::ActivityTaskb\x06proto3"
|
14
14
|
|
15
15
|
pool = Google::Protobuf::DescriptorPool.generated_pool
|
16
16
|
pool.add_serialized_file(descriptor_data)
|
@@ -7,7 +7,7 @@ require 'google/protobuf'
|
|
7
7
|
require 'google/protobuf/duration_pb'
|
8
8
|
|
9
9
|
|
10
|
-
descriptor_data = "\n%temporal/sdk/core/common/common.proto\x12\x0e\x63oresdk.common\x1a\x1egoogle/protobuf/duration.proto\"U\n\x1bNamespacedWorkflowExecution\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t*@\n\x10VersioningIntent\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x0e\n\nCOMPATIBLE\x10\x01\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x02\x42,\xea\x02)Temporalio::Internal::Bridge::Api::Commonb\x06proto3"
|
10
|
+
descriptor_data = "\n%temporal/sdk/core/common/common.proto\x12\x0e\x63oresdk.common\x1a\x1egoogle/protobuf/duration.proto\"U\n\x1bNamespacedWorkflowExecution\x12\x11\n\tnamespace\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\t\"D\n\x17WorkerDeploymentVersion\x12\x17\n\x0f\x64\x65ployment_name\x18\x01 \x01(\t\x12\x10\n\x08\x62uild_id\x18\x02 \x01(\t*@\n\x10VersioningIntent\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x0e\n\nCOMPATIBLE\x10\x01\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x02\x42,\xea\x02)Temporalio::Internal::Bridge::Api::Commonb\x06proto3"
|
11
11
|
|
12
12
|
pool = Google::Protobuf::DescriptorPool.generated_pool
|
13
13
|
pool.add_serialized_file(descriptor_data)
|
@@ -18,6 +18,7 @@ module Temporalio
|
|
18
18
|
module Api
|
19
19
|
module Common
|
20
20
|
NamespacedWorkflowExecution = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("coresdk.common.NamespacedWorkflowExecution").msgclass
|
21
|
+
WorkerDeploymentVersion = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("coresdk.common.WorkerDeploymentVersion").msgclass
|
21
22
|
VersioningIntent = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("coresdk.common.VersioningIntent").enummodule
|
22
23
|
end
|
23
24
|
end
|