temporalio 0.2.0-x86_64-linux → 0.3.0-x86_64-linux

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 +23 -0
  4. data/Rakefile +101 -0
  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.so +0 -0
  61. data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.so +0 -0
  62. data/lib/temporalio/internal/bridge/{3.1 → 3.4}/temporalio_bridge.so +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 +54 -6
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require 'temporalio/internal/bridge'
5
+ require 'temporalio/metric'
6
+
7
+ module Temporalio
8
+ module Internal
9
+ class Metric < Temporalio::Metric
10
+ attr_reader :metric_type, :name, :description, :unit, :value_type
11
+
12
+ def initialize(metric_type:, name:, description:, unit:, value_type:, bridge:, bridge_attrs:) # rubocop:disable Lint/MissingSuper
13
+ @metric_type = metric_type
14
+ @name = name
15
+ @description = description
16
+ @unit = unit
17
+ @value_type = value_type
18
+ @bridge = bridge
19
+ @bridge_attrs = bridge_attrs
20
+ end
21
+
22
+ def record(value, additional_attributes: nil)
23
+ bridge_attrs = @bridge_attrs
24
+ bridge_attrs = @bridge_attrs.with_additional(additional_attributes) if additional_attributes
25
+ @bridge.record_value(value, bridge_attrs)
26
+ end
27
+
28
+ def with_additional_attributes(additional_attributes)
29
+ Metric.new(
30
+ metric_type:,
31
+ name:,
32
+ description:,
33
+ unit:,
34
+ value_type:,
35
+ bridge: @bridge,
36
+ bridge_attrs: @bridge_attrs.with_additional(additional_attributes)
37
+ )
38
+ end
39
+
40
+ class Meter < Temporalio::Metric::Meter
41
+ def self.create_from_runtime(runtime)
42
+ bridge = Bridge::Metric::Meter.new(runtime._core_runtime)
43
+ return nil unless bridge
44
+
45
+ Meter.new(bridge, bridge.default_attributes)
46
+ end
47
+
48
+ def initialize(bridge, bridge_attrs) # rubocop:disable Lint/MissingSuper
49
+ @bridge = bridge
50
+ @bridge_attrs = bridge_attrs
51
+ end
52
+
53
+ def create_metric(
54
+ metric_type,
55
+ name,
56
+ description: nil,
57
+ unit: nil,
58
+ value_type: :integer
59
+ )
60
+ Metric.new(
61
+ metric_type:,
62
+ name:,
63
+ description:,
64
+ unit:,
65
+ value_type:,
66
+ bridge: Bridge::Metric.new(@bridge, metric_type, name, description, unit, value_type),
67
+ bridge_attrs: @bridge_attrs
68
+ )
69
+ end
70
+
71
+ def with_additional_attributes(additional_attributes)
72
+ Meter.new(@bridge, @bridge_attrs.with_additional(additional_attributes))
73
+ end
74
+ end
75
+
76
+ class NullMeter < Temporalio::Metric::Meter
77
+ include Singleton
78
+
79
+ def initialize # rubocop:disable Style/RedundantInitialize,Lint/MissingSuper
80
+ end
81
+
82
+ def create_metric(
83
+ metric_type,
84
+ name,
85
+ description: nil,
86
+ unit: nil,
87
+ value_type: :integer
88
+ )
89
+ NullMetric.new(
90
+ metric_type:,
91
+ name:,
92
+ description:,
93
+ unit:,
94
+ value_type:
95
+ )
96
+ end
97
+
98
+ def with_additional_attributes(_additional_attributes)
99
+ self
100
+ end
101
+ end
102
+
103
+ class NullMetric < Temporalio::Metric
104
+ attr_reader :metric_type, :name, :description, :unit, :value_type
105
+
106
+ def initialize(metric_type:, name:, description:, unit:, value_type:) # rubocop:disable Lint/MissingSuper
107
+ @metric_type = metric_type
108
+ @name = name
109
+ @description = description
110
+ @unit = unit
111
+ @value_type = value_type
112
+ end
113
+
114
+ def record(value, additional_attributes: nil); end
115
+
116
+ def with_additional_attributes(_additional_attributes)
117
+ self
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -5,26 +5,82 @@ require 'temporalio/api'
5
5
  module Temporalio
6
6
  module Internal
7
7
  module ProtoUtils
8
- def self.seconds_to_duration(seconds_float)
9
- return nil if seconds_float.nil?
8
+ def self.seconds_to_duration(seconds_numeric)
9
+ return nil if seconds_numeric.nil?
10
10
 
11
- seconds = seconds_float.to_i
12
- nanos = ((seconds_float - seconds) * 1_000_000_000).round
11
+ seconds = seconds_numeric.to_i
12
+ nanos = ((seconds_numeric - seconds) * 1_000_000_000).round
13
13
  Google::Protobuf::Duration.new(seconds:, nanos:)
14
14
  end
15
15
 
16
+ def self.duration_to_seconds(duration)
17
+ return nil if duration.nil?
18
+
19
+ # This logic was corrected for timestamp at
20
+ # https://github.com/protocolbuffers/protobuf/pull/2482 but not for
21
+ # duration, so 4.56 is not properly represented in to_f, it becomes
22
+ # 4.5600000000000005.
23
+ (duration.seconds + duration.nanos.quo(1_000_000_000)).to_f
24
+ end
25
+
26
+ def self.time_to_timestamp(time)
27
+ return nil if time.nil?
28
+
29
+ Google::Protobuf::Timestamp.from_time(time)
30
+ end
31
+
32
+ def self.timestamp_to_time(timestamp)
33
+ return nil if timestamp.nil?
34
+
35
+ # The regular to_time on the timestamp converts to local timezone,
36
+ # and we prefer not to make a separate .utc call (converts to local
37
+ # then back to UTC unnecessarily)
38
+ Time.at(timestamp.seconds, timestamp.nanos, :nanosecond, in: 'UTC')
39
+ end
40
+
16
41
  def self.memo_to_proto(hash, converter)
17
- return nil if hash.nil?
42
+ return nil if hash.nil? || hash.empty?
43
+
44
+ Api::Common::V1::Memo.new(fields: memo_to_proto_hash(hash, converter))
45
+ end
46
+
47
+ def self.memo_to_proto_hash(hash, converter)
48
+ return nil if hash.nil? || hash.empty?
18
49
 
19
- Api::Common::V1::Memo.new(fields: hash.transform_values { |val| converter.to_payload(val) })
50
+ hash.transform_keys(&:to_s).transform_values { |val| converter.to_payload(val) }
20
51
  end
21
52
 
22
53
  def self.memo_from_proto(memo, converter)
23
- return nil if memo.nil?
54
+ return nil if memo.nil? || memo.fields.size.zero? # rubocop:disable Style/ZeroLengthPredicate Google Maps don't have empty
24
55
 
25
56
  memo.fields.each_with_object({}) { |(key, val), h| h[key] = converter.from_payload(val) } # rubocop:disable Style/HashTransformValues
26
57
  end
27
58
 
59
+ def self.headers_to_proto(headers, converter)
60
+ return nil if headers.nil? || headers.empty?
61
+
62
+ Api::Common::V1::Header.new(fields: headers_to_proto_hash(headers, converter))
63
+ end
64
+
65
+ def self.headers_to_proto_hash(headers, converter)
66
+ return nil if headers.nil? || headers.empty?
67
+
68
+ headers.transform_values { |val| converter.to_payload(val) }
69
+ end
70
+
71
+ def self.headers_from_proto(headers, converter)
72
+ headers_from_proto_map(headers&.fields, converter)
73
+ end
74
+
75
+ def self.headers_from_proto_map(headers, converter)
76
+ return nil if headers.nil? || headers.size.zero? # rubocop:disable Style/ZeroLengthPredicate Google Maps don't have empty
77
+
78
+ headers.each_with_object({}) do |(key, val), h| # rubocop:disable Style/HashTransformValues
79
+ # @type var h: Hash[String, Object?]
80
+ h[key] = converter.from_payload(val)
81
+ end
82
+ end
83
+
28
84
  def self.string_or(str, default = nil)
29
85
  str && !str.empty? ? str : default
30
86
  end
@@ -49,6 +105,29 @@ module Temporalio
49
105
 
50
106
  converter.to_payloads(values).payloads.to_ary
51
107
  end
108
+
109
+ class LazyMemo
110
+ def initialize(raw_memo, converter)
111
+ @raw_memo = raw_memo
112
+ @converter = converter
113
+ end
114
+
115
+ def get
116
+ @memo = ProtoUtils.memo_from_proto(@raw_memo, @converter) unless defined?(@memo)
117
+ @memo
118
+ end
119
+ end
120
+
121
+ class LazySearchAttributes
122
+ def initialize(raw_search_attributes)
123
+ @raw_search_attributes = raw_search_attributes
124
+ end
125
+
126
+ def get
127
+ @search_attributes = SearchAttributes._from_proto(@raw_search_attributes) unless defined?(@search_attributes)
128
+ @search_attributes
129
+ end
130
+ end
52
131
  end
53
132
  end
54
133
  end
@@ -3,6 +3,7 @@
3
3
  require 'temporalio/activity'
4
4
  require 'temporalio/activity/definition'
5
5
  require 'temporalio/cancellation'
6
+ require 'temporalio/converters/raw_value'
6
7
  require 'temporalio/internal/bridge/api'
7
8
  require 'temporalio/internal/proto_utils'
8
9
  require 'temporalio/scoped_logger'
@@ -11,14 +12,17 @@ require 'temporalio/worker/interceptor'
11
12
  module Temporalio
12
13
  module Internal
13
14
  module Worker
15
+ # Worker for handling activity tasks. Upon overarching worker shutdown, {wait_all_complete} should be used to wait
16
+ # for the activities to complete.
14
17
  class ActivityWorker
15
18
  LOG_TASKS = false
16
19
 
17
20
  attr_reader :worker, :bridge_worker
18
21
 
19
- def initialize(worker, bridge_worker)
22
+ def initialize(worker:, bridge_worker:)
20
23
  @worker = worker
21
24
  @bridge_worker = bridge_worker
25
+ @runtime_metric_meter = worker.options.client.connection.options.runtime.metric_meter
22
26
 
23
27
  # Create shared logger that gives scoped activity details
24
28
  @scoped_logger = ScopedLogger.new(@worker.options.logger)
@@ -26,12 +30,13 @@ module Temporalio
26
30
  Activity::Context.current_or_nil&._scoped_logger_info
27
31
  }
28
32
 
29
- # Build up activity hash by name, failing if any fail validation
33
+ # Build up activity hash by name (can be nil for dynamic), failing if any fail validation
30
34
  @activities = worker.options.activities.each_with_object({}) do |act, hash|
31
35
  # Class means create each time, instance means just call, definition
32
36
  # does nothing special
33
- defn = Activity::Definition.from_activity(act)
37
+ defn = Activity::Definition::Info.from_activity(act)
34
38
  # Confirm name not in use
39
+ raise ArgumentError, 'Only one dynamic activity allowed' if !defn.name && hash.key?(defn.name)
35
40
  raise ArgumentError, "Multiple activities named #{defn.name}" if hash.key?(defn.name)
36
41
 
37
42
  # Confirm executor is a known executor and let it initialize
@@ -88,8 +93,8 @@ module Temporalio
88
93
  def handle_start_task(task_token, start)
89
94
  set_running_activity(task_token, nil)
90
95
 
91
- # Find activity definition
92
- defn = @activities[start.activity_type]
96
+ # Find activity definition, falling back to dynamic if present
97
+ defn = @activities[start.activity_type] || @activities[nil]
93
98
  if defn.nil?
94
99
  raise Error::ApplicationError.new(
95
100
  "Activity #{start.activity_type} for workflow #{start.workflow_execution.workflow_id} " \
@@ -158,17 +163,19 @@ module Temporalio
158
163
  activity_id: start.activity_id,
159
164
  activity_type: start.activity_type,
160
165
  attempt: start.attempt,
161
- current_attempt_scheduled_time: start.current_attempt_scheduled_time.to_time,
166
+ current_attempt_scheduled_time: Internal::ProtoUtils.timestamp_to_time(
167
+ start.current_attempt_scheduled_time
168
+ ) || raise, # Never nil
162
169
  heartbeat_details: ProtoUtils.convert_from_payload_array(
163
170
  @worker.options.client.data_converter,
164
171
  start.heartbeat_details.to_ary
165
172
  ),
166
- heartbeat_timeout: start.heartbeat_timeout&.to_f,
173
+ heartbeat_timeout: Internal::ProtoUtils.duration_to_seconds(start.heartbeat_timeout),
167
174
  local?: start.is_local,
168
- schedule_to_close_timeout: start.schedule_to_close_timeout&.to_f,
169
- scheduled_time: start.scheduled_time.to_time,
170
- start_to_close_timeout: start.start_to_close_timeout&.to_f,
171
- started_time: start.started_time.to_time,
175
+ schedule_to_close_timeout: Internal::ProtoUtils.duration_to_seconds(start.schedule_to_close_timeout),
176
+ scheduled_time: Internal::ProtoUtils.timestamp_to_time(start.scheduled_time) || raise, # Never nil
177
+ start_to_close_timeout: Internal::ProtoUtils.duration_to_seconds(start.start_to_close_timeout),
178
+ started_time: Internal::ProtoUtils.timestamp_to_time(start.started_time) || raise, # Never nil
172
179
  task_queue: @worker.options.task_queue,
173
180
  task_token:,
174
181
  workflow_id: start.workflow_execution.workflow_id,
@@ -178,13 +185,18 @@ module Temporalio
178
185
  ).freeze
179
186
 
180
187
  # Build input
181
- input = Temporalio::Worker::Interceptor::ExecuteActivityInput.new(
188
+ input = Temporalio::Worker::Interceptor::Activity::ExecuteInput.new(
182
189
  proc: defn.proc,
183
- args: ProtoUtils.convert_from_payload_array(
184
- @worker.options.client.data_converter,
185
- start.input.to_ary
186
- ),
187
- headers: start.header_fields
190
+ # If the activity wants raw_args, we only decode we don't convert
191
+ args: if defn.raw_args
192
+ payloads = start.input.to_ary
193
+ codec = @worker.options.client.data_converter.payload_codec
194
+ payloads = codec.decode(payloads) if codec
195
+ payloads.map { |p| Temporalio::Converters::RawValue.new(p) }
196
+ else
197
+ ProtoUtils.convert_from_payload_array(@worker.options.client.data_converter, start.input.to_ary)
198
+ end,
199
+ headers: ProtoUtils.headers_from_proto_map(start.header_fields, @worker.options.client.data_converter) || {}
188
200
  )
189
201
 
190
202
  # Run
@@ -193,7 +205,8 @@ module Temporalio
193
205
  cancellation: Cancellation.new,
194
206
  worker_shutdown_cancellation: @worker._worker_shutdown_cancellation,
195
207
  payload_converter: @worker.options.client.data_converter.payload_converter,
196
- logger: @scoped_logger
208
+ logger: @scoped_logger,
209
+ runtime_metric_meter: @runtime_metric_meter
197
210
  )
198
211
  Activity::Context._current_executor&.set_activity_context(defn, activity)
199
212
  set_running_activity(task_token, activity)
@@ -226,9 +239,9 @@ module Temporalio
226
239
  def run_activity(activity, input)
227
240
  result = begin
228
241
  # Build impl with interceptors
229
- # @type var impl: Temporalio::Worker::Interceptor::ActivityInbound
242
+ # @type var impl: Temporalio::Worker::Interceptor::Activity::Inbound
230
243
  impl = InboundImplementation.new(self)
231
- impl = @worker._all_interceptors.reverse_each.reduce(impl) do |acc, int|
244
+ impl = @worker._activity_interceptors.reverse_each.reduce(impl) do |acc, int|
232
245
  int.intercept_activity(acc)
233
246
  end
234
247
  impl.init(OutboundImplementation.new(self))
@@ -287,13 +300,15 @@ module Temporalio
287
300
  cancellation:,
288
301
  worker_shutdown_cancellation:,
289
302
  payload_converter:,
290
- logger:
303
+ logger:,
304
+ runtime_metric_meter:
291
305
  )
292
306
  @info = info
293
307
  @cancellation = cancellation
294
308
  @worker_shutdown_cancellation = worker_shutdown_cancellation
295
309
  @payload_converter = payload_converter
296
310
  @logger = logger
311
+ @runtime_metric_meter = runtime_metric_meter
297
312
  @_outbound_impl = nil
298
313
  @_server_requested_cancel = false
299
314
  end
@@ -301,11 +316,24 @@ module Temporalio
301
316
  def heartbeat(*details)
302
317
  raise 'Implementation not set yet' if _outbound_impl.nil?
303
318
 
304
- _outbound_impl.heartbeat(Temporalio::Worker::Interceptor::HeartbeatActivityInput.new(details:))
319
+ # No-op if local
320
+ return if info.local?
321
+
322
+ _outbound_impl.heartbeat(Temporalio::Worker::Interceptor::Activity::HeartbeatInput.new(details:))
323
+ end
324
+
325
+ def metric_meter
326
+ @metric_meter ||= @runtime_metric_meter.with_additional_attributes(
327
+ {
328
+ namespace: info.workflow_namespace,
329
+ task_queue: info.task_queue,
330
+ activity_type: info.activity_type
331
+ }
332
+ )
305
333
  end
306
334
  end
307
335
 
308
- class InboundImplementation < Temporalio::Worker::Interceptor::ActivityInbound
336
+ class InboundImplementation < Temporalio::Worker::Interceptor::Activity::Inbound
309
337
  def initialize(worker)
310
338
  super(nil) # steep:ignore
311
339
  @worker = worker
@@ -323,7 +351,7 @@ module Temporalio
323
351
  end
324
352
  end
325
353
 
326
- class OutboundImplementation < Temporalio::Worker::Interceptor::ActivityOutbound
354
+ class OutboundImplementation < Temporalio::Worker::Interceptor::Activity::Outbound
327
355
  def initialize(worker)
328
356
  super(nil) # steep:ignore
329
357
  @worker = worker
@@ -6,6 +6,8 @@ require 'temporalio/internal/bridge/worker'
6
6
  module Temporalio
7
7
  module Internal
8
8
  module Worker
9
+ # Primary worker (re)actor-style event handler. This handles multiple workers, receiving events from the bridge,
10
+ # and handling a user block.
9
11
  class MultiRunner
10
12
  def initialize(workers:, shutdown_signals:)
11
13
  @workers = workers
@@ -47,6 +49,16 @@ module Temporalio
47
49
  end
48
50
  end
49
51
 
52
+ def apply_workflow_activation_decoded(workflow_worker:, activation:)
53
+ @queue.push(Event::WorkflowActivationDecoded.new(workflow_worker:, activation:))
54
+ end
55
+
56
+ def apply_workflow_activation_complete(workflow_worker:, activation_completion:, encoded:)
57
+ @queue.push(Event::WorkflowActivationComplete.new(
58
+ workflow_worker:, activation_completion:, encoded:, completion_complete_queue: @queue
59
+ ))
60
+ end
61
+
50
62
  def raise_in_thread_or_fiber_block(error)
51
63
  @thread_or_fiber&.raise(error)
52
64
  end
@@ -80,22 +92,25 @@ module Temporalio
80
92
  # * [worker index, :activity/:workflow, error] - poll fail
81
93
  # * [worker index, :activity/:workflow, nil] - worker shutdown
82
94
  # * [nil, nil, nil] - all pollers done
95
+ # * [-1, run_id_string, error_or_nil] - workflow activation completion complete
83
96
  result = @queue.pop
84
97
  if result.is_a?(Event)
85
98
  result
86
99
  else
87
- worker_index, worker_type, poll_result = result
88
- if worker_index.nil? || worker_type.nil?
100
+ first, second, third = result
101
+ if first.nil? || second.nil?
89
102
  Event::AllPollersShutDown.instance
103
+ elsif first == -1
104
+ Event::WorkflowActivationCompletionComplete.new(run_id: second, error: third)
90
105
  else
91
- worker = @workers[worker_index]
92
- case poll_result
106
+ worker = @workers[first]
107
+ case third
93
108
  when nil
94
- Event::PollerShutDown.new(worker:, worker_type:)
109
+ Event::PollerShutDown.new(worker:, worker_type: second)
95
110
  when Exception
96
- Event::PollFailure.new(worker:, worker_type:, error: poll_result)
111
+ Event::PollFailure.new(worker:, worker_type: second, error: third)
97
112
  else
98
- Event::PollSuccess.new(worker:, worker_type:, bytes: poll_result)
113
+ Event::PollSuccess.new(worker:, worker_type: second, bytes: third)
99
114
  end
100
115
  end
101
116
  end
@@ -122,6 +137,35 @@ module Temporalio
122
137
  end
123
138
  end
124
139
 
140
+ class WorkflowActivationDecoded < Event
141
+ attr_reader :workflow_worker, :activation
142
+
143
+ def initialize(workflow_worker:, activation:) # rubocop:disable Lint/MissingSuper
144
+ @workflow_worker = workflow_worker
145
+ @activation = activation
146
+ end
147
+ end
148
+
149
+ class WorkflowActivationComplete < Event
150
+ attr_reader :workflow_worker, :activation_completion, :encoded, :completion_complete_queue
151
+
152
+ def initialize(workflow_worker:, activation_completion:, encoded:, completion_complete_queue:) # rubocop:disable Lint/MissingSuper
153
+ @workflow_worker = workflow_worker
154
+ @activation_completion = activation_completion
155
+ @encoded = encoded
156
+ @completion_complete_queue = completion_complete_queue
157
+ end
158
+ end
159
+
160
+ class WorkflowActivationCompletionComplete < Event
161
+ attr_reader :run_id, :error
162
+
163
+ def initialize(run_id:, error:) # rubocop:disable Lint/MissingSuper
164
+ @run_id = run_id
165
+ @error = error
166
+ end
167
+ end
168
+
125
169
  class PollerShutDown < Event
126
170
  attr_reader :worker, :worker_type
127
171
 
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/cancellation'
4
+ require 'temporalio/workflow'
5
+ require 'temporalio/workflow/child_workflow_handle'
6
+
7
+ module Temporalio
8
+ module Internal
9
+ module Worker
10
+ class WorkflowInstance
11
+ # Implementation of the child workflow handle.
12
+ class ChildWorkflowHandle < Workflow::ChildWorkflowHandle
13
+ attr_reader :id, :first_execution_run_id
14
+
15
+ def initialize(id:, first_execution_run_id:, instance:, cancellation:, cancel_callback_key:) # rubocop:disable Lint/MissingSuper
16
+ @id = id
17
+ @first_execution_run_id = first_execution_run_id
18
+ @instance = instance
19
+ @cancellation = cancellation
20
+ @cancel_callback_key = cancel_callback_key
21
+ @resolution = nil
22
+ end
23
+
24
+ def result
25
+ # Notice that we actually provide a detached cancellation here instead of defaulting to workflow
26
+ # cancellation because we don't want workflow cancellation (or a user-provided cancellation to this result
27
+ # call) to be able to interrupt waiting on a child that may be processing the cancellation.
28
+ Workflow.wait_condition(cancellation: Cancellation.new) { @resolution }
29
+
30
+ case @resolution.status
31
+ when :completed
32
+ @instance.payload_converter.from_payload(@resolution.completed.result)
33
+ when :failed
34
+ raise @instance.failure_converter.from_failure(@resolution.failed.failure, @instance.payload_converter)
35
+ when :cancelled
36
+ raise @instance.failure_converter.from_failure(@resolution.cancelled.failure, @instance.payload_converter)
37
+ else
38
+ raise "Unrecognized resolution status: #{@resolution.status}"
39
+ end
40
+ end
41
+
42
+ def _resolve(resolution)
43
+ @cancellation.remove_cancel_callback(@cancel_callback_key)
44
+ @resolution = resolution
45
+ end
46
+
47
+ def signal(signal, *args, cancellation: Workflow.cancellation)
48
+ @instance.context._signal_child_workflow(id:, signal:, args:, cancellation:)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end