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.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -0
  3. data/Gemfile +3 -3
  4. data/Rakefile +10 -296
  5. data/lib/temporalio/activity/complete_async_error.rb +1 -1
  6. data/lib/temporalio/activity/context.rb +5 -2
  7. data/lib/temporalio/activity/definition.rb +163 -65
  8. data/lib/temporalio/activity/info.rb +22 -21
  9. data/lib/temporalio/activity.rb +2 -59
  10. data/lib/temporalio/api/activity/v1/message.rb +25 -0
  11. data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
  12. data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +34 -1
  13. data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +1 -1
  14. data/lib/temporalio/api/cloud/identity/v1/message.rb +6 -1
  15. data/lib/temporalio/api/cloud/namespace/v1/message.rb +8 -1
  16. data/lib/temporalio/api/cloud/nexus/v1/message.rb +31 -0
  17. data/lib/temporalio/api/cloud/operation/v1/message.rb +2 -1
  18. data/lib/temporalio/api/cloud/region/v1/message.rb +2 -1
  19. data/lib/temporalio/api/cloud/resource/v1/message.rb +23 -0
  20. data/lib/temporalio/api/cloud/sink/v1/message.rb +24 -0
  21. data/lib/temporalio/api/cloud/usage/v1/message.rb +31 -0
  22. data/lib/temporalio/api/common/v1/message.rb +7 -1
  23. data/lib/temporalio/api/enums/v1/event_type.rb +1 -1
  24. data/lib/temporalio/api/enums/v1/failed_cause.rb +1 -1
  25. data/lib/temporalio/api/enums/v1/reset.rb +1 -1
  26. data/lib/temporalio/api/history/v1/message.rb +1 -1
  27. data/lib/temporalio/api/nexus/v1/message.rb +2 -2
  28. data/lib/temporalio/api/operatorservice/v1/service.rb +1 -1
  29. data/lib/temporalio/api/payload_visitor.rb +1513 -0
  30. data/lib/temporalio/api/schedule/v1/message.rb +2 -1
  31. data/lib/temporalio/api/testservice/v1/request_response.rb +31 -0
  32. data/lib/temporalio/api/testservice/v1/service.rb +23 -0
  33. data/lib/temporalio/api/workflow/v1/message.rb +1 -1
  34. data/lib/temporalio/api/workflowservice/v1/request_response.rb +17 -2
  35. data/lib/temporalio/api/workflowservice/v1/service.rb +1 -1
  36. data/lib/temporalio/api.rb +1 -0
  37. data/lib/temporalio/cancellation.rb +34 -14
  38. data/lib/temporalio/client/async_activity_handle.rb +12 -37
  39. data/lib/temporalio/client/connection/cloud_service.rb +309 -231
  40. data/lib/temporalio/client/connection/operator_service.rb +36 -84
  41. data/lib/temporalio/client/connection/service.rb +6 -5
  42. data/lib/temporalio/client/connection/test_service.rb +111 -0
  43. data/lib/temporalio/client/connection/workflow_service.rb +264 -441
  44. data/lib/temporalio/client/connection.rb +90 -44
  45. data/lib/temporalio/client/interceptor.rb +160 -60
  46. data/lib/temporalio/client/schedule.rb +967 -0
  47. data/lib/temporalio/client/schedule_handle.rb +126 -0
  48. data/lib/temporalio/client/workflow_execution.rb +7 -10
  49. data/lib/temporalio/client/workflow_handle.rb +38 -95
  50. data/lib/temporalio/client/workflow_update_handle.rb +3 -5
  51. data/lib/temporalio/client.rb +122 -42
  52. data/lib/temporalio/common_enums.rb +17 -0
  53. data/lib/temporalio/converters/data_converter.rb +4 -7
  54. data/lib/temporalio/converters/failure_converter.rb +5 -3
  55. data/lib/temporalio/converters/payload_converter/composite.rb +4 -0
  56. data/lib/temporalio/converters/payload_converter.rb +6 -8
  57. data/lib/temporalio/converters/raw_value.rb +20 -0
  58. data/lib/temporalio/error/failure.rb +1 -1
  59. data/lib/temporalio/error.rb +10 -2
  60. data/lib/temporalio/internal/bridge/3.2/temporalio_bridge.bundle +0 -0
  61. data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.bundle +0 -0
  62. data/lib/temporalio/internal/bridge/{3.1 → 3.4}/temporalio_bridge.bundle +0 -0
  63. data/lib/temporalio/internal/bridge/api/core_interface.rb +5 -1
  64. data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +33 -0
  65. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +5 -1
  66. data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +4 -1
  67. data/lib/temporalio/internal/bridge/client.rb +11 -6
  68. data/lib/temporalio/internal/bridge/testing.rb +20 -0
  69. data/lib/temporalio/internal/bridge/worker.rb +2 -0
  70. data/lib/temporalio/internal/bridge.rb +1 -1
  71. data/lib/temporalio/internal/client/implementation.rb +245 -70
  72. data/lib/temporalio/internal/metric.rb +122 -0
  73. data/lib/temporalio/internal/proto_utils.rb +86 -7
  74. data/lib/temporalio/internal/worker/activity_worker.rb +52 -24
  75. data/lib/temporalio/internal/worker/multi_runner.rb +51 -7
  76. data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
  77. data/lib/temporalio/internal/worker/workflow_instance/context.rb +329 -0
  78. data/lib/temporalio/internal/worker/workflow_instance/details.rb +44 -0
  79. data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +32 -0
  80. data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +22 -0
  81. data/lib/temporalio/internal/worker/workflow_instance/handler_execution.rb +25 -0
  82. data/lib/temporalio/internal/worker/workflow_instance/handler_hash.rb +41 -0
  83. data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +97 -0
  84. data/lib/temporalio/internal/worker/workflow_instance/inbound_implementation.rb +62 -0
  85. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +415 -0
  86. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +37 -0
  87. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb +40 -0
  88. data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +163 -0
  89. data/lib/temporalio/internal/worker/workflow_instance.rb +730 -0
  90. data/lib/temporalio/internal/worker/workflow_worker.rb +196 -0
  91. data/lib/temporalio/metric.rb +109 -0
  92. data/lib/temporalio/retry_policy.rb +37 -14
  93. data/lib/temporalio/runtime.rb +118 -75
  94. data/lib/temporalio/search_attributes.rb +80 -37
  95. data/lib/temporalio/testing/activity_environment.rb +2 -2
  96. data/lib/temporalio/testing/workflow_environment.rb +251 -5
  97. data/lib/temporalio/version.rb +1 -1
  98. data/lib/temporalio/worker/activity_executor/thread_pool.rb +9 -217
  99. data/lib/temporalio/worker/activity_executor.rb +3 -3
  100. data/lib/temporalio/worker/interceptor.rb +340 -66
  101. data/lib/temporalio/worker/thread_pool.rb +237 -0
  102. data/lib/temporalio/worker/workflow_executor/thread_pool.rb +230 -0
  103. data/lib/temporalio/worker/workflow_executor.rb +26 -0
  104. data/lib/temporalio/worker.rb +201 -30
  105. data/lib/temporalio/workflow/activity_cancellation_type.rb +20 -0
  106. data/lib/temporalio/workflow/child_workflow_cancellation_type.rb +21 -0
  107. data/lib/temporalio/workflow/child_workflow_handle.rb +43 -0
  108. data/lib/temporalio/workflow/definition.rb +566 -0
  109. data/lib/temporalio/workflow/external_workflow_handle.rb +41 -0
  110. data/lib/temporalio/workflow/future.rb +151 -0
  111. data/lib/temporalio/workflow/handler_unfinished_policy.rb +13 -0
  112. data/lib/temporalio/workflow/info.rb +82 -0
  113. data/lib/temporalio/workflow/parent_close_policy.rb +19 -0
  114. data/lib/temporalio/workflow/update_info.rb +20 -0
  115. data/lib/temporalio/workflow.rb +523 -0
  116. data/lib/temporalio.rb +4 -0
  117. data/temporalio.gemspec +2 -2
  118. 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 = Struct.new(
19
- :initial_interval,
20
- :backoff_coefficient,
21
- :max_interval,
22
- :max_attempts,
23
- :non_retryable_error_types,
24
- keyword_init: true
25
- ) do
26
- def initialize(*, **kwargs)
27
- kwargs[:initial_interval] = 1.0 unless kwargs.key?(:initial_interval)
28
- kwargs[:backoff_coefficient] = 2.0 unless kwargs.key?(:backoff_coefficient)
29
- kwargs[:max_attempts] = 0 unless kwargs.key?(:max_attempts)
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 to_proto
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?
@@ -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 = Struct.new(
20
- :logging,
21
- :metrics,
22
- keyword_init: true
23
- ) do
24
- # @!visibility private
25
- def initialize(**kwargs)
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 = Struct.new(
47
- :log_filter,
48
- # TODO(cretz): forward_to
49
- keyword_init: true
50
- ) do
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, default is +'WARN'+.
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, default is +'WARN'+.
79
- LoggingFilterOptions = Struct.new(
80
- :core_level,
81
- :other_level,
82
- keyword_init: true
83
- ) do
84
- # @!visibility private
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, default +true+.
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 +temporal_+.
113
- MetricsOptions = Struct.new(
114
- :opentelemetry,
115
- :prometheus,
116
- :attach_service_name,
117
- :global_tags,
118
- :metric_prefix,
119
- keyword_init: true
120
- ) do
121
- # @!visibility private
122
- def initialize(**kwargs)
123
- # @type var kwargs: untyped
124
- kwargs[:attach_service_name] = true unless kwargs.key?(:attach_service_name)
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 = Struct.new(
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 # rubocop:disable Lint/ConstantDefinitionInBlock
186
+ module MetricTemporality
165
187
  CUMULATIVE = 1
166
188
  DELTA = 2
167
189
  end
168
190
 
169
- # @!visibility private
170
- def initialize(**kwargs)
171
- # @type var kwargs: untyped
172
- kwargs[:metric_temporality] = MetricTemporality::CUMULATIVE unless kwargs.key?(:metric_temporality)
173
- kwargs[:durations_as_seconds] = false unless kwargs.key?(:durations_as_seconds)
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 +true+, all counters will include a +_total+ suffix, default is +false+.
238
+ # @return [Boolean] If `true`, all counters will include a `_total` suffix.
200
239
  # @!attribute unit_suffix
201
- # @return [Boolean] If +true+, all histograms will include the unit in their name as a suffix, default is +false+.
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, default is
204
- # +false+.
205
- PrometheusMetricsOptions = Struct.new(
206
- :bind_address,
207
- :counters_total_suffix,
208
- :unit_suffix,
209
- :durations_as_seconds,
210
- keyword_init: true
211
- ) do
212
- # @!visibility private
213
- def initialize(**kwargs)
214
- # @type var kwargs: untyped
215
- kwargs[:counters_total_suffix] = false unless kwargs.key?(:counters_total_suffix)
216
- kwargs[:unit_suffix] = false unless kwargs.key?(:unit_suffix)
217
- kwargs[:durations_as_seconds] = false unless kwargs.key?(:durations_as_seconds)
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