temporalio 0.2.0-x86_64-darwin → 0.3.0-x86_64-darwin
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +2 -0
- data/Gemfile +3 -3
- data/Rakefile +10 -296
- data/lib/temporalio/activity/complete_async_error.rb +1 -1
- data/lib/temporalio/activity/context.rb +5 -2
- data/lib/temporalio/activity/definition.rb +163 -65
- data/lib/temporalio/activity/info.rb +22 -21
- data/lib/temporalio/activity.rb +2 -59
- data/lib/temporalio/api/activity/v1/message.rb +25 -0
- 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/common/v1/message.rb +7 -1
- 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/reset.rb +1 -1
- data/lib/temporalio/api/history/v1/message.rb +1 -1
- data/lib/temporalio/api/nexus/v1/message.rb +2 -2
- data/lib/temporalio/api/operatorservice/v1/service.rb +1 -1
- data/lib/temporalio/api/payload_visitor.rb +1513 -0
- data/lib/temporalio/api/schedule/v1/message.rb +2 -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 +1 -1
- data/lib/temporalio/api/workflowservice/v1/request_response.rb +17 -2
- data/lib/temporalio/api/workflowservice/v1/service.rb +1 -1
- data/lib/temporalio/api.rb +1 -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 +264 -441
- data/lib/temporalio/client/connection.rb +90 -44
- data/lib/temporalio/client/interceptor.rb +160 -60
- data/lib/temporalio/client/schedule.rb +967 -0
- data/lib/temporalio/client/schedule_handle.rb +126 -0
- data/lib/temporalio/client/workflow_execution.rb +7 -10
- data/lib/temporalio/client/workflow_handle.rb +38 -95
- data/lib/temporalio/client/workflow_update_handle.rb +3 -5
- data/lib/temporalio/client.rb +122 -42
- data/lib/temporalio/common_enums.rb +17 -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 +10 -2
- data/lib/temporalio/internal/bridge/3.2/temporalio_bridge.bundle +0 -0
- data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.bundle +0 -0
- data/lib/temporalio/internal/bridge/{3.1 → 3.4}/temporalio_bridge.bundle +0 -0
- 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/client.rb +11 -6
- data/lib/temporalio/internal/bridge/testing.rb +20 -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 +245 -70
- data/lib/temporalio/internal/metric.rb +122 -0
- data/lib/temporalio/internal/proto_utils.rb +86 -7
- data/lib/temporalio/internal/worker/activity_worker.rb +52 -24
- data/lib/temporalio/internal/worker/multi_runner.rb +51 -7
- data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
- data/lib/temporalio/internal/worker/workflow_instance/context.rb +329 -0
- data/lib/temporalio/internal/worker/workflow_instance/details.rb +44 -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 +415 -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 +163 -0
- data/lib/temporalio/internal/worker/workflow_instance.rb +730 -0
- data/lib/temporalio/internal/worker/workflow_worker.rb +196 -0
- data/lib/temporalio/metric.rb +109 -0
- data/lib/temporalio/retry_policy.rb +37 -14
- data/lib/temporalio/runtime.rb +118 -75
- data/lib/temporalio/search_attributes.rb +80 -37
- data/lib/temporalio/testing/activity_environment.rb +2 -2
- data/lib/temporalio/testing/workflow_environment.rb +251 -5
- 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 +340 -66
- data/lib/temporalio/worker/thread_pool.rb +237 -0
- data/lib/temporalio/worker/workflow_executor/thread_pool.rb +230 -0
- data/lib/temporalio/worker/workflow_executor.rb +26 -0
- data/lib/temporalio/worker.rb +201 -30
- 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 +566 -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 +82 -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 +523 -0
- data/lib/temporalio.rb +4 -0
- data/temporalio.gemspec +2 -2
- metadata +52 -6
@@ -0,0 +1,196 @@
|
|
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)
|
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
|
+
hash[defn.name] = defn
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(worker:, bridge_worker:, workflow_definitions:)
|
37
|
+
@executor = worker.options.workflow_executor
|
38
|
+
|
39
|
+
payload_codec = worker.options.client.data_converter.payload_codec
|
40
|
+
@workflow_payload_codec_thread_pool = worker.options.workflow_payload_codec_thread_pool
|
41
|
+
if !Fiber.current_scheduler && payload_codec && !@workflow_payload_codec_thread_pool
|
42
|
+
raise ArgumentError, 'Must have workflow payload codec thread pool if providing codec and not using fibers'
|
43
|
+
end
|
44
|
+
|
45
|
+
# If there is a payload codec, we need to build encoding and decoding visitors
|
46
|
+
if payload_codec
|
47
|
+
@payload_encoding_visitor = Api::PayloadVisitor.new(skip_search_attributes: true) do |payload_or_payloads|
|
48
|
+
apply_codec_on_payload_visit(payload_or_payloads) { |payloads| payload_codec.encode(payloads) }
|
49
|
+
end
|
50
|
+
@payload_decoding_visitor = Api::PayloadVisitor.new(skip_search_attributes: true) do |payload_or_payloads|
|
51
|
+
apply_codec_on_payload_visit(payload_or_payloads) { |payloads| payload_codec.decode(payloads) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
@state = State.new(
|
56
|
+
workflow_definitions:,
|
57
|
+
bridge_worker:,
|
58
|
+
logger: worker.options.logger,
|
59
|
+
metric_meter: worker.options.client.connection.options.runtime.metric_meter,
|
60
|
+
data_converter: worker.options.client.data_converter,
|
61
|
+
deadlock_timeout: worker.options.debug_mode ? nil : 2.0,
|
62
|
+
# TODO(cretz): Make this more performant for the default set?
|
63
|
+
illegal_calls: WorkflowInstance::IllegalCallTracer.frozen_validated_illegal_calls(
|
64
|
+
worker.options.illegal_workflow_calls || {}
|
65
|
+
),
|
66
|
+
namespace: worker.options.client.namespace,
|
67
|
+
task_queue: worker.options.task_queue,
|
68
|
+
disable_eager_activity_execution: worker.options.disable_eager_activity_execution,
|
69
|
+
workflow_interceptors: worker._workflow_interceptors,
|
70
|
+
workflow_failure_exception_types: worker.options.workflow_failure_exception_types.map do |t|
|
71
|
+
unless t.is_a?(Class) && t < Exception
|
72
|
+
raise ArgumentError, 'All failure types must classes inheriting Exception'
|
73
|
+
end
|
74
|
+
|
75
|
+
t
|
76
|
+
end.freeze
|
77
|
+
)
|
78
|
+
|
79
|
+
# Validate worker
|
80
|
+
@executor._validate_worker(worker, @state)
|
81
|
+
end
|
82
|
+
|
83
|
+
def handle_activation(runner:, activation:, decoded:)
|
84
|
+
# Encode in background if not encoded but it needs to be
|
85
|
+
if @payload_encoding_visitor && !decoded
|
86
|
+
if Fiber.current_scheduler
|
87
|
+
Fiber.schedule { decode_activation(runner, activation) }
|
88
|
+
else
|
89
|
+
@workflow_payload_codec_thread_pool.execute { decode_activation(runner, activation) }
|
90
|
+
end
|
91
|
+
else
|
92
|
+
@executor._activate(activation, @state) do |activation_completion|
|
93
|
+
runner.apply_workflow_activation_complete(workflow_worker: self, activation_completion:, encoded: false)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
97
|
+
# Should never happen, executors are expected to trap things
|
98
|
+
@state.logger.error("Failed issuing activation on workflow run ID: #{activation.run_id}")
|
99
|
+
@state.logger.error(e)
|
100
|
+
end
|
101
|
+
|
102
|
+
def handle_activation_complete(runner:, activation_completion:, encoded:, completion_complete_queue:)
|
103
|
+
if @payload_encoding_visitor && !encoded
|
104
|
+
if Fiber.current_scheduler
|
105
|
+
Fiber.schedule { encode_activation_completion(runner, activation_completion) }
|
106
|
+
else
|
107
|
+
@workflow_payload_codec_thread_pool.execute do
|
108
|
+
encode_activation_completion(runner, activation_completion)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
else
|
112
|
+
@state.bridge_worker.async_complete_workflow_activation(
|
113
|
+
activation_completion.run_id, activation_completion.to_proto, completion_complete_queue
|
114
|
+
)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def on_shutdown_complete
|
119
|
+
@state.evict_all
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def decode_activation(runner, activation)
|
125
|
+
@payload_decoding_visitor.run(activation)
|
126
|
+
runner.apply_workflow_activation_decoded(workflow_worker: self, activation:)
|
127
|
+
end
|
128
|
+
|
129
|
+
def encode_activation_completion(runner, activation_completion)
|
130
|
+
@payload_encoding_visitor.run(activation_completion)
|
131
|
+
runner.apply_workflow_activation_complete(workflow_worker: self, activation_completion:, encoded: true)
|
132
|
+
end
|
133
|
+
|
134
|
+
def apply_codec_on_payload_visit(payload_or_payloads, &)
|
135
|
+
case payload_or_payloads
|
136
|
+
when Temporalio::Api::Common::V1::Payload
|
137
|
+
new_payloads = yield [payload_or_payloads]
|
138
|
+
payload_or_payloads.metadata = new_payloads.first.metadata
|
139
|
+
payload_or_payloads.data = new_payloads.first.data
|
140
|
+
when Enumerable
|
141
|
+
payload_or_payloads.replace(yield payload_or_payloads) # steep:ignore
|
142
|
+
else
|
143
|
+
raise 'Unrecognized visitor type'
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class State
|
148
|
+
attr_reader :workflow_definitions, :bridge_worker, :logger, :metric_meter, :data_converter, :deadlock_timeout,
|
149
|
+
:illegal_calls, :namespace, :task_queue, :disable_eager_activity_execution,
|
150
|
+
:workflow_interceptors, :workflow_failure_exception_types
|
151
|
+
|
152
|
+
def initialize(
|
153
|
+
workflow_definitions:, bridge_worker:, logger:, metric_meter:, data_converter:, deadlock_timeout:,
|
154
|
+
illegal_calls:, namespace:, task_queue:, disable_eager_activity_execution:,
|
155
|
+
workflow_interceptors:, workflow_failure_exception_types:
|
156
|
+
)
|
157
|
+
@workflow_definitions = workflow_definitions
|
158
|
+
@bridge_worker = bridge_worker
|
159
|
+
@logger = logger
|
160
|
+
@metric_meter = metric_meter
|
161
|
+
@data_converter = data_converter
|
162
|
+
@deadlock_timeout = deadlock_timeout
|
163
|
+
@illegal_calls = illegal_calls
|
164
|
+
@namespace = namespace
|
165
|
+
@task_queue = task_queue
|
166
|
+
@disable_eager_activity_execution = disable_eager_activity_execution
|
167
|
+
@workflow_interceptors = workflow_interceptors
|
168
|
+
@workflow_failure_exception_types = workflow_failure_exception_types
|
169
|
+
|
170
|
+
@running_workflows = {}
|
171
|
+
@running_workflows_mutex = Mutex.new
|
172
|
+
end
|
173
|
+
|
174
|
+
# This can never be called at the same time for the same run ID on the same state object
|
175
|
+
def get_or_create_running_workflow(run_id, &)
|
176
|
+
instance = @running_workflows_mutex.synchronize { @running_workflows[run_id] }
|
177
|
+
# If instance is not there, we create it out of lock then store it under lock
|
178
|
+
unless instance
|
179
|
+
instance = yield
|
180
|
+
@running_workflows_mutex.synchronize { @running_workflows[run_id] = instance }
|
181
|
+
end
|
182
|
+
instance
|
183
|
+
end
|
184
|
+
|
185
|
+
def evict_running_workflow(run_id)
|
186
|
+
@running_workflows_mutex.synchronize { @running_workflows.delete(run_id) }
|
187
|
+
end
|
188
|
+
|
189
|
+
def evict_all
|
190
|
+
@running_workflows_mutex.synchronize { @running_workflows.clear }
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
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
|
@@ -3,6 +3,14 @@
|
|
3
3
|
require 'temporalio/internal/proto_utils'
|
4
4
|
|
5
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
|
+
|
6
14
|
# Options for retrying workflows and activities.
|
7
15
|
#
|
8
16
|
# @!attribute initial_interval
|
@@ -15,24 +23,39 @@ module Temporalio
|
|
15
23
|
# @return [Integer] Maximum number of attempts. If `0`, the default, there is no maximum.
|
16
24
|
# @!attribute non_retryable_error_types
|
17
25
|
# @return [Array<String>, nil] List of error types that are not retryable.
|
18
|
-
RetryPolicy
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
+
)
|
30
54
|
super
|
31
55
|
end
|
32
56
|
|
33
57
|
# @!visibility private
|
34
|
-
def
|
35
|
-
# @type self: RetryPolicy
|
58
|
+
def _to_proto
|
36
59
|
raise 'Initial interval cannot be negative' if initial_interval.negative?
|
37
60
|
raise 'Backoff coefficient cannot be less than 1' if backoff_coefficient < 1
|
38
61
|
raise 'Max interval cannot be negative' if max_interval&.negative?
|
data/lib/temporalio/runtime.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'temporalio/internal/bridge'
|
3
4
|
require 'temporalio/internal/bridge/runtime'
|
5
|
+
require 'temporalio/internal/metric'
|
6
|
+
require 'temporalio/metric'
|
4
7
|
|
5
8
|
module Temporalio
|
6
9
|
# Runtime for Temporal Ruby SDK.
|
@@ -9,6 +12,11 @@ module Temporalio
|
|
9
12
|
# before any clients are created, and set it via {default=}. Every time a new runtime is created, a new internal Rust
|
10
13
|
# thread pool is created.
|
11
14
|
class Runtime
|
15
|
+
TelemetryOptions = Data.define(
|
16
|
+
:logging,
|
17
|
+
:metrics
|
18
|
+
)
|
19
|
+
|
12
20
|
# Telemetry options for the runtime.
|
13
21
|
#
|
14
22
|
# @!attribute logging
|
@@ -16,15 +24,13 @@ module Temporalio
|
|
16
24
|
# to nil to disable logging.
|
17
25
|
# @!attribute metrics
|
18
26
|
# @return [MetricsOptions, nil] Metrics options.
|
19
|
-
TelemetryOptions
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
#
|
25
|
-
def initialize(
|
26
|
-
# @type var kwargs: untyped
|
27
|
-
kwargs[:logging] = LoggingOptions.new unless kwargs.key?(:logging)
|
27
|
+
class TelemetryOptions
|
28
|
+
# Create telemetry options.
|
29
|
+
#
|
30
|
+
# @param logging [LoggingOptions, nil] Logging options, default is new {LoggingOptions} with no parameters. Can be
|
31
|
+
# set to nil to disable logging.
|
32
|
+
# @param metrics [MetricsOptions, nil] Metrics options.
|
33
|
+
def initialize(logging: LoggingOptions.new, metrics: nil)
|
28
34
|
super
|
29
35
|
end
|
30
36
|
|
@@ -38,20 +44,21 @@ module Temporalio
|
|
38
44
|
end
|
39
45
|
end
|
40
46
|
|
47
|
+
LoggingOptions = Data.define(
|
48
|
+
:log_filter
|
49
|
+
# TODO(cretz): forward_to
|
50
|
+
)
|
51
|
+
|
41
52
|
# Logging options for runtime telemetry.
|
42
53
|
#
|
43
54
|
# @!attribute log_filter
|
44
55
|
# @return [LoggingFilterOptions, String] Logging filter for Core, default is new {LoggingFilterOptions} with no
|
45
56
|
# parameters.
|
46
|
-
LoggingOptions
|
47
|
-
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
# @!visibility private
|
52
|
-
def initialize(**kwargs)
|
53
|
-
# @type var kwargs: untyped
|
54
|
-
kwargs[:log_filter] = LoggingFilterOptions.new unless kwargs.key?(:log_filter)
|
57
|
+
class LoggingOptions
|
58
|
+
# Create logging options
|
59
|
+
#
|
60
|
+
# @param log_filter [LoggingFilterOptions, String] Logging filter for Core.
|
61
|
+
def initialize(log_filter: LoggingFilterOptions.new)
|
55
62
|
super
|
56
63
|
end
|
57
64
|
|
@@ -70,22 +77,23 @@ module Temporalio
|
|
70
77
|
end
|
71
78
|
end
|
72
79
|
|
80
|
+
LoggingFilterOptions = Data.define(
|
81
|
+
:core_level,
|
82
|
+
:other_level
|
83
|
+
)
|
84
|
+
|
73
85
|
# Logging filter options for Core.
|
74
86
|
#
|
75
87
|
# @!attribute core_level
|
76
|
-
# @return ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'] Log level for Core log messages
|
88
|
+
# @return ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'] Log level for Core log messages.
|
77
89
|
# @!attribute other_level
|
78
|
-
# @return ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'] Log level for other Rust log messages
|
79
|
-
LoggingFilterOptions
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
def initialize(**kwargs)
|
86
|
-
# @type var kwargs: untyped
|
87
|
-
kwargs[:core_level] = 'WARN' unless kwargs.key?(:core_level)
|
88
|
-
kwargs[:other_level] = 'ERROR' unless kwargs.key?(:other_level)
|
90
|
+
# @return ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'] Log level for other Rust log messages.
|
91
|
+
class LoggingFilterOptions
|
92
|
+
# Create logging filter options.
|
93
|
+
#
|
94
|
+
# @param core_level ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'] Log level for Core log messages.
|
95
|
+
# @!attribute other_level ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'] Log level for other Rust log messages.
|
96
|
+
def initialize(core_level: 'WARN', other_level: 'ERROR')
|
89
97
|
super
|
90
98
|
end
|
91
99
|
|
@@ -96,6 +104,14 @@ module Temporalio
|
|
96
104
|
end
|
97
105
|
end
|
98
106
|
|
107
|
+
MetricsOptions = Data.define(
|
108
|
+
:opentelemetry,
|
109
|
+
:prometheus,
|
110
|
+
:attach_service_name,
|
111
|
+
:global_tags,
|
112
|
+
:metric_prefix
|
113
|
+
)
|
114
|
+
|
99
115
|
# Metrics options for runtime telemetry. Either {opentelemetry} or {prometheus} required, but not both.
|
100
116
|
#
|
101
117
|
# @!attribute opentelemetry
|
@@ -105,23 +121,28 @@ module Temporalio
|
|
105
121
|
# @return [PrometheusMetricsOptions, nil] Prometheus options if using Prometheus. This is mutually exclusive with
|
106
122
|
# +opentelemetry+.
|
107
123
|
# @!attribute attach_service_name
|
108
|
-
# @return [Boolean] Whether to put the service_name on every metric
|
124
|
+
# @return [Boolean] Whether to put the service_name on every metric.
|
109
125
|
# @!attribute global_tags
|
110
126
|
# @return [Hash<String, String>, nil] Resource tags to be applied to all metrics.
|
111
127
|
# @!attribute metric_prefix
|
112
|
-
# @return [String, nil] Prefix to put on every Temporal metric. If unset, defaults to
|
113
|
-
MetricsOptions
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
#
|
122
|
-
|
123
|
-
|
124
|
-
|
128
|
+
# @return [String, nil] Prefix to put on every Temporal metric. If unset, defaults to `temporal_`.
|
129
|
+
class MetricsOptions
|
130
|
+
# Create metrics options. Either `opentelemetry` or `prometheus` required, but not both.
|
131
|
+
#
|
132
|
+
# @param opentelemetry [OpenTelemetryMetricsOptions, nil] OpenTelemetry options if using OpenTelemetry. This is
|
133
|
+
# mutually exclusive with `prometheus`.
|
134
|
+
# @param prometheus [PrometheusMetricsOptions, nil] Prometheus options if using Prometheus. This is mutually
|
135
|
+
# exclusive with `opentelemetry`.
|
136
|
+
# @param attach_service_name [Boolean] Whether to put the service_name on every metric.
|
137
|
+
# @param global_tags [Hash<String, String>, nil] Resource tags to be applied to all metrics.
|
138
|
+
# @param metric_prefix [String, nil] Prefix to put on every Temporal metric. If unset, defaults to `temporal_`.
|
139
|
+
def initialize(
|
140
|
+
opentelemetry: nil,
|
141
|
+
prometheus: nil,
|
142
|
+
attach_service_name: true,
|
143
|
+
global_tags: nil,
|
144
|
+
metric_prefix: nil
|
145
|
+
)
|
125
146
|
super
|
126
147
|
end
|
127
148
|
|
@@ -138,6 +159,14 @@ module Temporalio
|
|
138
159
|
end
|
139
160
|
end
|
140
161
|
|
162
|
+
OpenTelemetryMetricsOptions = Data.define(
|
163
|
+
:url,
|
164
|
+
:headers,
|
165
|
+
:metric_periodicity,
|
166
|
+
:metric_temporality,
|
167
|
+
:durations_as_seconds
|
168
|
+
)
|
169
|
+
|
141
170
|
# Options for exporting metrics to OpenTelemetry.
|
142
171
|
#
|
143
172
|
# @!attribute url
|
@@ -152,25 +181,28 @@ module Temporalio
|
|
152
181
|
# @!attribute durations_as_seconds
|
153
182
|
# @return [Boolean] Whether to use float seconds instead of integer milliseconds for durations, default is
|
154
183
|
# +false+.
|
155
|
-
OpenTelemetryMetricsOptions
|
156
|
-
:url,
|
157
|
-
:headers,
|
158
|
-
:metric_periodicity,
|
159
|
-
:metric_temporality,
|
160
|
-
:durations_as_seconds,
|
161
|
-
keyword_init: true
|
162
|
-
) do
|
184
|
+
class OpenTelemetryMetricsOptions
|
163
185
|
# OpenTelemetry metric temporality.
|
164
|
-
module MetricTemporality
|
186
|
+
module MetricTemporality
|
165
187
|
CUMULATIVE = 1
|
166
188
|
DELTA = 2
|
167
189
|
end
|
168
190
|
|
169
|
-
#
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
191
|
+
# Create OpenTelemetry options.
|
192
|
+
#
|
193
|
+
# @param url [String] URL for OpenTelemetry endpoint.
|
194
|
+
# @param headers [Hash<String, String>, nil] Headers for OpenTelemetry endpoint.
|
195
|
+
# @param metric_periodicity [Float, nil] How frequently metrics should be exported, unset uses internal default.
|
196
|
+
# @param metric_temporality [MetricTemporality] How frequently metrics should be exported.
|
197
|
+
# @param durations_as_seconds [Boolean] Whether to use float seconds instead of integer milliseconds for
|
198
|
+
# durations.
|
199
|
+
def initialize(
|
200
|
+
url:,
|
201
|
+
headers: nil,
|
202
|
+
metric_periodicity: nil,
|
203
|
+
metric_temporality: MetricTemporality::CUMULATIVE,
|
204
|
+
durations_as_seconds: false
|
205
|
+
)
|
174
206
|
super
|
175
207
|
end
|
176
208
|
|
@@ -191,30 +223,37 @@ module Temporalio
|
|
191
223
|
end
|
192
224
|
end
|
193
225
|
|
226
|
+
PrometheusMetricsOptions = Data.define(
|
227
|
+
:bind_address,
|
228
|
+
:counters_total_suffix,
|
229
|
+
:unit_suffix,
|
230
|
+
:durations_as_seconds
|
231
|
+
)
|
232
|
+
|
194
233
|
# Options for exporting metrics to Prometheus.
|
195
234
|
#
|
196
235
|
# @!attribute bind_address
|
197
236
|
# @return [String] Address to bind to for Prometheus endpoint.
|
198
237
|
# @!attribute counters_total_suffix
|
199
|
-
# @return [Boolean] If
|
238
|
+
# @return [Boolean] If `true`, all counters will include a `_total` suffix.
|
200
239
|
# @!attribute unit_suffix
|
201
|
-
# @return [Boolean] If
|
240
|
+
# @return [Boolean] If `true`, all histograms will include the unit in their name as a suffix.
|
202
241
|
# @!attribute durations_as_seconds
|
203
|
-
# @return [Boolean] Whether to use float seconds instead of integer milliseconds for durations
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
242
|
+
# @return [Boolean] Whether to use float seconds instead of integer milliseconds for durations.
|
243
|
+
class PrometheusMetricsOptions
|
244
|
+
# Create Prometheus options.
|
245
|
+
#
|
246
|
+
# @param bind_address [String] Address to bind to for Prometheus endpoint.
|
247
|
+
# @param counters_total_suffix [Boolean] If `true`, all counters will include a `_total` suffix.
|
248
|
+
# @param unit_suffix [Boolean] If `true`, all histograms will include the unit in their name as a suffix.
|
249
|
+
# @param durations_as_seconds [Boolean] Whether to use float seconds instead of integer milliseconds for
|
250
|
+
# durations.
|
251
|
+
def initialize(
|
252
|
+
bind_address:,
|
253
|
+
counters_total_suffix: false,
|
254
|
+
unit_suffix: false,
|
255
|
+
durations_as_seconds: false
|
256
|
+
)
|
218
257
|
super
|
219
258
|
end
|
220
259
|
|
@@ -248,6 +287,9 @@ module Temporalio
|
|
248
287
|
@default = runtime
|
249
288
|
end
|
250
289
|
|
290
|
+
# @return [Metric::Meter] Metric meter that can create and record metric values.
|
291
|
+
attr_reader :metric_meter
|
292
|
+
|
251
293
|
# Create new Runtime. For most users, this should only be done once globally. In addition to creating a Rust thread
|
252
294
|
# pool, this also consumes a Ruby thread for its lifetime.
|
253
295
|
#
|
@@ -256,6 +298,7 @@ module Temporalio
|
|
256
298
|
@core_runtime = Internal::Bridge::Runtime.new(
|
257
299
|
Internal::Bridge::Runtime::Options.new(telemetry: telemetry._to_bridge)
|
258
300
|
)
|
301
|
+
@metric_meter = Internal::Metric::Meter.create_from_runtime(self) || Metric::Meter.null
|
259
302
|
# We need a thread to run the command loop
|
260
303
|
# TODO(cretz): Is this something users should be concerned about or need control over?
|
261
304
|
Thread.new do
|