temporalio 0.4.0-aarch64-linux-musl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +2 -0
  3. data/Gemfile +27 -0
  4. data/Rakefile +101 -0
  5. data/lib/temporalio/activity/complete_async_error.rb +11 -0
  6. data/lib/temporalio/activity/context.rb +123 -0
  7. data/lib/temporalio/activity/definition.rb +192 -0
  8. data/lib/temporalio/activity/info.rb +67 -0
  9. data/lib/temporalio/activity.rb +12 -0
  10. data/lib/temporalio/api/activity/v1/message.rb +25 -0
  11. data/lib/temporalio/api/batch/v1/message.rb +36 -0
  12. data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
  13. data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +126 -0
  14. data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +25 -0
  15. data/lib/temporalio/api/cloud/cloudservice.rb +3 -0
  16. data/lib/temporalio/api/cloud/identity/v1/message.rb +41 -0
  17. data/lib/temporalio/api/cloud/namespace/v1/message.rb +42 -0
  18. data/lib/temporalio/api/cloud/nexus/v1/message.rb +31 -0
  19. data/lib/temporalio/api/cloud/operation/v1/message.rb +28 -0
  20. data/lib/temporalio/api/cloud/region/v1/message.rb +24 -0
  21. data/lib/temporalio/api/cloud/resource/v1/message.rb +23 -0
  22. data/lib/temporalio/api/cloud/sink/v1/message.rb +24 -0
  23. data/lib/temporalio/api/cloud/usage/v1/message.rb +31 -0
  24. data/lib/temporalio/api/command/v1/message.rb +46 -0
  25. data/lib/temporalio/api/common/v1/grpc_status.rb +23 -0
  26. data/lib/temporalio/api/common/v1/message.rb +48 -0
  27. data/lib/temporalio/api/deployment/v1/message.rb +38 -0
  28. data/lib/temporalio/api/enums/v1/batch_operation.rb +22 -0
  29. data/lib/temporalio/api/enums/v1/command_type.rb +21 -0
  30. data/lib/temporalio/api/enums/v1/common.rb +26 -0
  31. data/lib/temporalio/api/enums/v1/deployment.rb +23 -0
  32. data/lib/temporalio/api/enums/v1/event_type.rb +21 -0
  33. data/lib/temporalio/api/enums/v1/failed_cause.rb +26 -0
  34. data/lib/temporalio/api/enums/v1/namespace.rb +23 -0
  35. data/lib/temporalio/api/enums/v1/nexus.rb +21 -0
  36. data/lib/temporalio/api/enums/v1/query.rb +22 -0
  37. data/lib/temporalio/api/enums/v1/reset.rb +23 -0
  38. data/lib/temporalio/api/enums/v1/schedule.rb +21 -0
  39. data/lib/temporalio/api/enums/v1/task_queue.rb +25 -0
  40. data/lib/temporalio/api/enums/v1/update.rb +22 -0
  41. data/lib/temporalio/api/enums/v1/workflow.rb +31 -0
  42. data/lib/temporalio/api/errordetails/v1/message.rb +44 -0
  43. data/lib/temporalio/api/export/v1/message.rb +24 -0
  44. data/lib/temporalio/api/failure/v1/message.rb +37 -0
  45. data/lib/temporalio/api/filter/v1/message.rb +27 -0
  46. data/lib/temporalio/api/history/v1/message.rb +92 -0
  47. data/lib/temporalio/api/namespace/v1/message.rb +31 -0
  48. data/lib/temporalio/api/nexus/v1/message.rb +41 -0
  49. data/lib/temporalio/api/operatorservice/v1/request_response.rb +49 -0
  50. data/lib/temporalio/api/operatorservice/v1/service.rb +23 -0
  51. data/lib/temporalio/api/operatorservice.rb +3 -0
  52. data/lib/temporalio/api/payload_visitor.rb +1581 -0
  53. data/lib/temporalio/api/protocol/v1/message.rb +23 -0
  54. data/lib/temporalio/api/query/v1/message.rb +28 -0
  55. data/lib/temporalio/api/replication/v1/message.rb +26 -0
  56. data/lib/temporalio/api/schedule/v1/message.rb +43 -0
  57. data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +25 -0
  58. data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +21 -0
  59. data/lib/temporalio/api/sdk/v1/user_metadata.rb +23 -0
  60. data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +23 -0
  61. data/lib/temporalio/api/taskqueue/v1/message.rb +48 -0
  62. data/lib/temporalio/api/testservice/v1/request_response.rb +31 -0
  63. data/lib/temporalio/api/testservice/v1/service.rb +23 -0
  64. data/lib/temporalio/api/update/v1/message.rb +33 -0
  65. data/lib/temporalio/api/version/v1/message.rb +26 -0
  66. data/lib/temporalio/api/workflow/v1/message.rb +51 -0
  67. data/lib/temporalio/api/workflowservice/v1/request_response.rb +233 -0
  68. data/lib/temporalio/api/workflowservice/v1/service.rb +23 -0
  69. data/lib/temporalio/api/workflowservice.rb +3 -0
  70. data/lib/temporalio/api.rb +15 -0
  71. data/lib/temporalio/cancellation.rb +170 -0
  72. data/lib/temporalio/client/activity_id_reference.rb +32 -0
  73. data/lib/temporalio/client/async_activity_handle.rb +85 -0
  74. data/lib/temporalio/client/connection/cloud_service.rb +726 -0
  75. data/lib/temporalio/client/connection/operator_service.rb +201 -0
  76. data/lib/temporalio/client/connection/service.rb +42 -0
  77. data/lib/temporalio/client/connection/test_service.rb +111 -0
  78. data/lib/temporalio/client/connection/workflow_service.rb +1251 -0
  79. data/lib/temporalio/client/connection.rb +316 -0
  80. data/lib/temporalio/client/interceptor.rb +455 -0
  81. data/lib/temporalio/client/schedule.rb +991 -0
  82. data/lib/temporalio/client/schedule_handle.rb +126 -0
  83. data/lib/temporalio/client/with_start_workflow_operation.rb +115 -0
  84. data/lib/temporalio/client/workflow_execution.rb +119 -0
  85. data/lib/temporalio/client/workflow_execution_count.rb +36 -0
  86. data/lib/temporalio/client/workflow_execution_status.rb +18 -0
  87. data/lib/temporalio/client/workflow_handle.rb +389 -0
  88. data/lib/temporalio/client/workflow_query_reject_condition.rb +14 -0
  89. data/lib/temporalio/client/workflow_update_handle.rb +65 -0
  90. data/lib/temporalio/client/workflow_update_wait_stage.rb +17 -0
  91. data/lib/temporalio/client.rb +607 -0
  92. data/lib/temporalio/common_enums.rb +41 -0
  93. data/lib/temporalio/contrib/open_telemetry.rb +470 -0
  94. data/lib/temporalio/converters/data_converter.rb +99 -0
  95. data/lib/temporalio/converters/failure_converter.rb +202 -0
  96. data/lib/temporalio/converters/payload_codec.rb +26 -0
  97. data/lib/temporalio/converters/payload_converter/binary_null.rb +34 -0
  98. data/lib/temporalio/converters/payload_converter/binary_plain.rb +35 -0
  99. data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +42 -0
  100. data/lib/temporalio/converters/payload_converter/composite.rb +66 -0
  101. data/lib/temporalio/converters/payload_converter/encoding.rb +35 -0
  102. data/lib/temporalio/converters/payload_converter/json_plain.rb +44 -0
  103. data/lib/temporalio/converters/payload_converter/json_protobuf.rb +41 -0
  104. data/lib/temporalio/converters/payload_converter.rb +71 -0
  105. data/lib/temporalio/converters/raw_value.rb +20 -0
  106. data/lib/temporalio/converters.rb +9 -0
  107. data/lib/temporalio/error/failure.rb +219 -0
  108. data/lib/temporalio/error.rb +156 -0
  109. data/lib/temporalio/internal/bridge/3.2/temporalio_bridge.so +0 -0
  110. data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.so +0 -0
  111. data/lib/temporalio/internal/bridge/3.4/temporalio_bridge.so +0 -0
  112. data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +34 -0
  113. data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +31 -0
  114. data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +33 -0
  115. data/lib/temporalio/internal/bridge/api/common/common.rb +27 -0
  116. data/lib/temporalio/internal/bridge/api/core_interface.rb +40 -0
  117. data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +27 -0
  118. data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +33 -0
  119. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +56 -0
  120. data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +57 -0
  121. data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +31 -0
  122. data/lib/temporalio/internal/bridge/api.rb +3 -0
  123. data/lib/temporalio/internal/bridge/client.rb +95 -0
  124. data/lib/temporalio/internal/bridge/runtime.rb +56 -0
  125. data/lib/temporalio/internal/bridge/testing.rb +69 -0
  126. data/lib/temporalio/internal/bridge/worker.rb +85 -0
  127. data/lib/temporalio/internal/bridge.rb +36 -0
  128. data/lib/temporalio/internal/client/implementation.rb +922 -0
  129. data/lib/temporalio/internal/metric.rb +122 -0
  130. data/lib/temporalio/internal/proto_utils.rb +165 -0
  131. data/lib/temporalio/internal/worker/activity_worker.rb +385 -0
  132. data/lib/temporalio/internal/worker/multi_runner.rb +213 -0
  133. data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
  134. data/lib/temporalio/internal/worker/workflow_instance/context.rb +383 -0
  135. data/lib/temporalio/internal/worker/workflow_instance/details.rb +46 -0
  136. data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +32 -0
  137. data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +22 -0
  138. data/lib/temporalio/internal/worker/workflow_instance/handler_execution.rb +25 -0
  139. data/lib/temporalio/internal/worker/workflow_instance/handler_hash.rb +41 -0
  140. data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +97 -0
  141. data/lib/temporalio/internal/worker/workflow_instance/inbound_implementation.rb +62 -0
  142. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +400 -0
  143. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +37 -0
  144. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb +40 -0
  145. data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +183 -0
  146. data/lib/temporalio/internal/worker/workflow_instance.rb +774 -0
  147. data/lib/temporalio/internal/worker/workflow_worker.rb +239 -0
  148. data/lib/temporalio/internal.rb +7 -0
  149. data/lib/temporalio/metric.rb +109 -0
  150. data/lib/temporalio/retry_policy.rb +74 -0
  151. data/lib/temporalio/runtime/metric_buffer.rb +94 -0
  152. data/lib/temporalio/runtime.rb +352 -0
  153. data/lib/temporalio/scoped_logger.rb +96 -0
  154. data/lib/temporalio/search_attributes.rb +356 -0
  155. data/lib/temporalio/testing/activity_environment.rb +160 -0
  156. data/lib/temporalio/testing/workflow_environment.rb +406 -0
  157. data/lib/temporalio/testing.rb +10 -0
  158. data/lib/temporalio/version.rb +5 -0
  159. data/lib/temporalio/worker/activity_executor/fiber.rb +49 -0
  160. data/lib/temporalio/worker/activity_executor/thread_pool.rb +46 -0
  161. data/lib/temporalio/worker/activity_executor.rb +55 -0
  162. data/lib/temporalio/worker/interceptor.rb +365 -0
  163. data/lib/temporalio/worker/thread_pool.rb +237 -0
  164. data/lib/temporalio/worker/tuner.rb +189 -0
  165. data/lib/temporalio/worker/workflow_executor/thread_pool.rb +235 -0
  166. data/lib/temporalio/worker/workflow_executor.rb +26 -0
  167. data/lib/temporalio/worker/workflow_replayer.rb +350 -0
  168. data/lib/temporalio/worker.rb +603 -0
  169. data/lib/temporalio/workflow/activity_cancellation_type.rb +20 -0
  170. data/lib/temporalio/workflow/child_workflow_cancellation_type.rb +21 -0
  171. data/lib/temporalio/workflow/child_workflow_handle.rb +43 -0
  172. data/lib/temporalio/workflow/definition.rb +598 -0
  173. data/lib/temporalio/workflow/external_workflow_handle.rb +41 -0
  174. data/lib/temporalio/workflow/future.rb +151 -0
  175. data/lib/temporalio/workflow/handler_unfinished_policy.rb +13 -0
  176. data/lib/temporalio/workflow/info.rb +104 -0
  177. data/lib/temporalio/workflow/parent_close_policy.rb +19 -0
  178. data/lib/temporalio/workflow/update_info.rb +20 -0
  179. data/lib/temporalio/workflow.rb +575 -0
  180. data/lib/temporalio/workflow_history.rb +47 -0
  181. data/lib/temporalio.rb +11 -0
  182. data/temporalio.gemspec +29 -0
  183. metadata +258 -0
@@ -0,0 +1,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
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/api'
4
+
5
+ module Temporalio
6
+ module Internal
7
+ module ProtoUtils
8
+ def self.seconds_to_duration(seconds_numeric)
9
+ return nil if seconds_numeric.nil?
10
+
11
+ seconds = seconds_numeric.to_i
12
+ nanos = ((seconds_numeric - seconds) * 1_000_000_000).round
13
+ Google::Protobuf::Duration.new(seconds:, nanos:)
14
+ end
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
+
41
+ def self.memo_to_proto(hash, converter)
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?
49
+
50
+ hash.transform_keys(&:to_s).transform_values { |val| converter.to_payload(val) }
51
+ end
52
+
53
+ def self.memo_from_proto(memo, converter)
54
+ return nil if memo.nil? || memo.fields.size.zero? # rubocop:disable Style/ZeroLengthPredicate -- Google Maps don't have empty
55
+
56
+ memo.fields.each_with_object({}) { |(key, val), h| h[key] = converter.from_payload(val) } # rubocop:disable Style/HashTransformValues
57
+ end
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
+
84
+ def self.string_or(str, default = nil)
85
+ str && !str.empty? ? str : default
86
+ end
87
+
88
+ def self.enum_to_int(enum_mod, enum_val, zero_means_nil: false)
89
+ # Per https://protobuf.dev/reference/ruby/ruby-generated/#enum when
90
+ # enums are read back, they are symbols if they are known or number
91
+ # otherwise
92
+ enum_val = enum_mod.resolve(enum_val) || raise('Unexpected missing symbol') if enum_val.is_a?(Symbol)
93
+ enum_val = nil if zero_means_nil && enum_val.zero?
94
+ enum_val
95
+ end
96
+
97
+ def self.convert_from_payload_array(converter, payloads)
98
+ return [] if payloads.empty?
99
+
100
+ converter.from_payloads(Api::Common::V1::Payloads.new(payloads:))
101
+ end
102
+
103
+ def self.convert_to_payload_array(converter, values)
104
+ return [] if values.empty?
105
+
106
+ converter.to_payloads(values).payloads.to_ary
107
+ end
108
+
109
+ def self.assert_non_reserved_name(name)
110
+ name = name&.to_s # In case it's a symbol or not present
111
+ return unless name
112
+ raise "'#{name}' cannot start with '__temporal_'" if name.start_with?('__temporal_')
113
+ # Might as well disable __stack_trace and __enhanced_stack_trace everywhere even though technically it's only
114
+ # reserved for queries
115
+ raise "'#{name}' name invalid" if name == '__stack_trace' || name == '__enhanced_stack_trace'
116
+ end
117
+
118
+ def self.reserved_name?(name)
119
+ name = name&.to_s # In case it's a symbol or not present
120
+ return false unless name
121
+
122
+ name.start_with?('__temporal_') || name == '__stack_trace' || name == '__enhanced_stack_trace'
123
+ end
124
+
125
+ def self.to_user_metadata(summary, details, converter)
126
+ return nil if (!summary || summary.empty?) && (!details || details.empty?)
127
+
128
+ metadata = Temporalio::Api::Sdk::V1::UserMetadata.new
129
+ metadata.summary = converter.to_payload(summary) if summary && !summary.empty?
130
+ metadata.details = converter.to_payload(details) if details && !details.empty?
131
+ metadata
132
+ end
133
+
134
+ def self.from_user_metadata(metadata, converter)
135
+ [
136
+ (converter.from_payload(metadata.summary) if metadata&.summary), #: String?
137
+ (converter.from_payload(metadata.details) if metadata&.details) #: String?
138
+ ]
139
+ end
140
+
141
+ class LazyMemo
142
+ def initialize(raw_memo, converter)
143
+ @raw_memo = raw_memo
144
+ @converter = converter
145
+ end
146
+
147
+ def get
148
+ @memo = ProtoUtils.memo_from_proto(@raw_memo, @converter) unless defined?(@memo)
149
+ @memo
150
+ end
151
+ end
152
+
153
+ class LazySearchAttributes
154
+ def initialize(raw_search_attributes)
155
+ @raw_search_attributes = raw_search_attributes
156
+ end
157
+
158
+ def get
159
+ @search_attributes = SearchAttributes._from_proto(@raw_search_attributes) unless defined?(@search_attributes)
160
+ @search_attributes
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,385 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/activity'
4
+ require 'temporalio/activity/definition'
5
+ require 'temporalio/cancellation'
6
+ require 'temporalio/converters/raw_value'
7
+ require 'temporalio/internal/bridge/api'
8
+ require 'temporalio/internal/proto_utils'
9
+ require 'temporalio/scoped_logger'
10
+ require 'temporalio/worker/interceptor'
11
+
12
+ module Temporalio
13
+ module Internal
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.
17
+ class ActivityWorker
18
+ LOG_TASKS = false
19
+
20
+ attr_reader :worker, :bridge_worker
21
+
22
+ def initialize(worker:, bridge_worker:)
23
+ @worker = worker
24
+ @bridge_worker = bridge_worker
25
+ @runtime_metric_meter = worker.options.client.connection.options.runtime.metric_meter
26
+
27
+ # Create shared logger that gives scoped activity details
28
+ @scoped_logger = ScopedLogger.new(@worker.options.logger)
29
+ @scoped_logger.scoped_values_getter = proc {
30
+ Activity::Context.current_or_nil&._scoped_logger_info
31
+ }
32
+
33
+ # Build up activity hash by name (can be nil for dynamic), failing if any fail validation
34
+ @activities = worker.options.activities.each_with_object({}) do |act, hash|
35
+ # Class means create each time, instance means just call, definition
36
+ # does nothing special
37
+ defn = Activity::Definition::Info.from_activity(act)
38
+ # Confirm name not in use
39
+ raise ArgumentError, 'Only one dynamic activity allowed' if !defn.name && hash.key?(defn.name)
40
+ raise ArgumentError, "Multiple activities named #{defn.name}" if hash.key?(defn.name)
41
+
42
+ # Confirm executor is a known executor and let it initialize
43
+ executor = worker.options.activity_executors[defn.executor]
44
+ raise ArgumentError, "Unknown executor '#{defn.executor}'" if executor.nil?
45
+
46
+ executor.initialize_activity(defn)
47
+
48
+ hash[defn.name] = defn
49
+ end
50
+
51
+ # Need mutex for the rest of these
52
+ @running_activities_mutex = Mutex.new
53
+ @running_activities = {}
54
+ @running_activities_empty_condvar = ConditionVariable.new
55
+ end
56
+
57
+ def set_running_activity(task_token, activity)
58
+ @running_activities_mutex.synchronize do
59
+ @running_activities[task_token] = activity
60
+ end
61
+ end
62
+
63
+ def get_running_activity(task_token)
64
+ @running_activities_mutex.synchronize do
65
+ @running_activities[task_token]
66
+ end
67
+ end
68
+
69
+ def remove_running_activity(task_token)
70
+ @running_activities_mutex.synchronize do
71
+ @running_activities.delete(task_token)
72
+ @running_activities_empty_condvar.broadcast if @running_activities.empty?
73
+ end
74
+ end
75
+
76
+ def wait_all_complete
77
+ @running_activities_mutex.synchronize do
78
+ @running_activities_empty_condvar.wait(@running_activities_mutex) until @running_activities.empty?
79
+ end
80
+ end
81
+
82
+ def handle_task(task)
83
+ @scoped_logger.debug("Received activity task: #{task}") if LOG_TASKS
84
+ if !task.start.nil?
85
+ handle_start_task(task.task_token, task.start)
86
+ elsif !task.cancel.nil?
87
+ handle_cancel_task(task.task_token, task.cancel)
88
+ else
89
+ raise "Unrecognized activity task: #{task}"
90
+ end
91
+ end
92
+
93
+ def handle_start_task(task_token, start)
94
+ set_running_activity(task_token, nil)
95
+
96
+ # Find activity definition, falling back to dynamic if not found and not reserved name
97
+ defn = @activities[start.activity_type]
98
+ defn = @activities[nil] if !defn && !Internal::ProtoUtils.reserved_name?(start.activity_type)
99
+
100
+ if defn.nil?
101
+ raise Error::ApplicationError.new(
102
+ "Activity #{start.activity_type} for workflow #{start.workflow_execution.workflow_id} " \
103
+ "is not registered on this worker, available activities: #{@activities.keys.sort.join(', ')}",
104
+ type: 'NotFoundError'
105
+ )
106
+ end
107
+
108
+ # Run everything else in the excecutor
109
+ executor = @worker.options.activity_executors[defn.executor]
110
+ executor.execute_activity(defn) do
111
+ # Set current executor
112
+ Activity::Context._current_executor = executor
113
+ # Execute with error handling
114
+ execute_activity(task_token, defn, start)
115
+ ensure
116
+ # Unset at the end
117
+ Activity::Context._current_executor = nil
118
+ end
119
+ rescue Exception => e # rubocop:disable Lint/RescueException -- We are intending to catch everything here
120
+ remove_running_activity(task_token)
121
+ @scoped_logger.warn("Failed starting activity #{start.activity_type}")
122
+ @scoped_logger.warn(e)
123
+
124
+ # We need to complete the activity task as failed, but this is on the
125
+ # hot path for polling, so we want to complete it in the background
126
+ begin
127
+ @bridge_worker.complete_activity_task_in_background(
128
+ Bridge::Api::CoreInterface::ActivityTaskCompletion.new(
129
+ task_token:,
130
+ result: Bridge::Api::ActivityResult::ActivityExecutionResult.new(
131
+ failed: Bridge::Api::ActivityResult::Failure.new(
132
+ # TODO(cretz): If failure conversion does slow failure
133
+ # encoding, it can gum up the system
134
+ failure: @worker.options.client.data_converter.to_failure(e)
135
+ )
136
+ )
137
+ )
138
+ )
139
+ rescue StandardError => e_inner
140
+ @scoped_logger.error("Failed building start failure to return for #{start.activity_type}")
141
+ @scoped_logger.error(e_inner)
142
+ end
143
+ end
144
+
145
+ def handle_cancel_task(task_token, cancel)
146
+ activity = get_running_activity(task_token)
147
+ if activity.nil?
148
+ @scoped_logger.warn("Cannot find activity to cancel for token #{task_token}")
149
+ return
150
+ end
151
+ activity._server_requested_cancel = true
152
+ _, cancel_proc = activity.cancellation
153
+ begin
154
+ cancel_proc.call(reason: cancel.reason.to_s)
155
+ rescue StandardError => e
156
+ @scoped_logger.warn("Failed cancelling activity #{activity.info.activity_type} \
157
+ with ID #{activity.info.activity_id}")
158
+ @scoped_logger.warn(e)
159
+ end
160
+ end
161
+
162
+ def execute_activity(task_token, defn, start)
163
+ # Build info
164
+ info = Activity::Info.new(
165
+ activity_id: start.activity_id,
166
+ activity_type: start.activity_type,
167
+ attempt: start.attempt,
168
+ current_attempt_scheduled_time: Internal::ProtoUtils.timestamp_to_time(
169
+ start.current_attempt_scheduled_time
170
+ ) || raise, # Never nil
171
+ heartbeat_details: ProtoUtils.convert_from_payload_array(
172
+ @worker.options.client.data_converter,
173
+ start.heartbeat_details.to_ary
174
+ ),
175
+ heartbeat_timeout: Internal::ProtoUtils.duration_to_seconds(start.heartbeat_timeout),
176
+ local?: start.is_local,
177
+ schedule_to_close_timeout: Internal::ProtoUtils.duration_to_seconds(start.schedule_to_close_timeout),
178
+ scheduled_time: Internal::ProtoUtils.timestamp_to_time(start.scheduled_time) || raise, # Never nil
179
+ start_to_close_timeout: Internal::ProtoUtils.duration_to_seconds(start.start_to_close_timeout),
180
+ started_time: Internal::ProtoUtils.timestamp_to_time(start.started_time) || raise, # Never nil
181
+ task_queue: @worker.options.task_queue,
182
+ task_token:,
183
+ workflow_id: start.workflow_execution.workflow_id,
184
+ workflow_namespace: start.workflow_namespace,
185
+ workflow_run_id: start.workflow_execution.run_id,
186
+ workflow_type: start.workflow_type
187
+ ).freeze
188
+
189
+ # Build input
190
+ input = Temporalio::Worker::Interceptor::Activity::ExecuteInput.new(
191
+ proc: defn.proc,
192
+ # If the activity wants raw_args, we only decode we don't convert
193
+ args: if defn.raw_args
194
+ payloads = start.input.to_ary
195
+ codec = @worker.options.client.data_converter.payload_codec
196
+ payloads = codec.decode(payloads) if codec
197
+ payloads.map { |p| Temporalio::Converters::RawValue.new(p) }
198
+ else
199
+ ProtoUtils.convert_from_payload_array(@worker.options.client.data_converter, start.input.to_ary)
200
+ end,
201
+ headers: ProtoUtils.headers_from_proto_map(start.header_fields, @worker.options.client.data_converter) || {}
202
+ )
203
+
204
+ # Run
205
+ activity = RunningActivity.new(
206
+ worker: @worker,
207
+ info:,
208
+ cancellation: Cancellation.new,
209
+ worker_shutdown_cancellation: @worker._worker_shutdown_cancellation,
210
+ payload_converter: @worker.options.client.data_converter.payload_converter,
211
+ logger: @scoped_logger,
212
+ runtime_metric_meter: @runtime_metric_meter
213
+ )
214
+ Activity::Context._current_executor&.set_activity_context(defn, activity)
215
+ set_running_activity(task_token, activity)
216
+ run_activity(defn, activity, input)
217
+ rescue Exception => e # rubocop:disable Lint/RescueException -- We are intending to catch everything here
218
+ @scoped_logger.warn("Failed starting or sending completion for activity #{start.activity_type}")
219
+ @scoped_logger.warn(e)
220
+ # This means that the activity couldn't start or send completion (run
221
+ # handles its own errors).
222
+ begin
223
+ @bridge_worker.complete_activity_task(
224
+ Bridge::Api::CoreInterface::ActivityTaskCompletion.new(
225
+ task_token:,
226
+ result: Bridge::Api::ActivityResult::ActivityExecutionResult.new(
227
+ failed: Bridge::Api::ActivityResult::Failure.new(
228
+ failure: @worker.options.client.data_converter.to_failure(e)
229
+ )
230
+ )
231
+ )
232
+ )
233
+ rescue StandardError => e_inner
234
+ @scoped_logger.error("Failed sending failure for activity #{start.activity_type}")
235
+ @scoped_logger.error(e_inner)
236
+ end
237
+ ensure
238
+ Activity::Context._current_executor&.set_activity_context(defn, nil)
239
+ remove_running_activity(task_token)
240
+ end
241
+
242
+ def run_activity(defn, activity, input)
243
+ result = begin
244
+ # Create the instance. We choose to do this before interceptors so that it is available in the interceptor.
245
+ activity.instance = defn.instance.is_a?(Proc) ? defn.instance.call : defn.instance # steep:ignore
246
+
247
+ # Build impl with interceptors
248
+ # @type var impl: Temporalio::Worker::Interceptor::Activity::Inbound
249
+ impl = InboundImplementation.new(self)
250
+ impl = @worker._activity_interceptors.reverse_each.reduce(impl) do |acc, int|
251
+ int.intercept_activity(acc)
252
+ end
253
+ impl.init(OutboundImplementation.new(self))
254
+
255
+ # Execute
256
+ result = impl.execute(input)
257
+
258
+ # Success
259
+ Bridge::Api::ActivityResult::ActivityExecutionResult.new(
260
+ completed: Bridge::Api::ActivityResult::Success.new(
261
+ result: @worker.options.client.data_converter.to_payload(result)
262
+ )
263
+ )
264
+ rescue Exception => e # rubocop:disable Lint/RescueException -- We are intending to catch everything here
265
+ if e.is_a?(Activity::CompleteAsyncError)
266
+ # Wanting to complete async
267
+ @scoped_logger.debug('Completing activity asynchronously')
268
+ Bridge::Api::ActivityResult::ActivityExecutionResult.new(
269
+ will_complete_async: Bridge::Api::ActivityResult::WillCompleteAsync.new
270
+ )
271
+ elsif e.is_a?(Error::CanceledError) && activity._server_requested_cancel
272
+ # Server requested cancel
273
+ @scoped_logger.debug('Completing activity as canceled')
274
+ Bridge::Api::ActivityResult::ActivityExecutionResult.new(
275
+ cancelled: Bridge::Api::ActivityResult::Cancellation.new(
276
+ failure: @worker.options.client.data_converter.to_failure(e)
277
+ )
278
+ )
279
+ else
280
+ # General failure
281
+ @scoped_logger.warn('Completing activity as failed')
282
+ @scoped_logger.warn(e)
283
+ Bridge::Api::ActivityResult::ActivityExecutionResult.new(
284
+ failed: Bridge::Api::ActivityResult::Failure.new(
285
+ failure: @worker.options.client.data_converter.to_failure(e)
286
+ )
287
+ )
288
+ end
289
+ end
290
+
291
+ @scoped_logger.debug("Sending activity completion: #{result}") if LOG_TASKS
292
+ @bridge_worker.complete_activity_task(
293
+ Bridge::Api::CoreInterface::ActivityTaskCompletion.new(
294
+ task_token: activity.info.task_token,
295
+ result:
296
+ )
297
+ )
298
+ end
299
+
300
+ class RunningActivity < Activity::Context
301
+ attr_reader :info, :cancellation, :worker_shutdown_cancellation, :payload_converter, :logger
302
+ attr_accessor :instance, :_outbound_impl, :_server_requested_cancel
303
+
304
+ def initialize( # rubocop:disable Lint/MissingSuper
305
+ worker:,
306
+ info:,
307
+ cancellation:,
308
+ worker_shutdown_cancellation:,
309
+ payload_converter:,
310
+ logger:,
311
+ runtime_metric_meter:
312
+ )
313
+ @worker = worker
314
+ @info = info
315
+ @cancellation = cancellation
316
+ @worker_shutdown_cancellation = worker_shutdown_cancellation
317
+ @payload_converter = payload_converter
318
+ @logger = logger
319
+ @runtime_metric_meter = runtime_metric_meter
320
+ @_outbound_impl = nil
321
+ @_server_requested_cancel = false
322
+ end
323
+
324
+ def heartbeat(*details)
325
+ raise 'Implementation not set yet' if _outbound_impl.nil?
326
+
327
+ # No-op if local
328
+ return if info.local?
329
+
330
+ _outbound_impl.heartbeat(Temporalio::Worker::Interceptor::Activity::HeartbeatInput.new(details:))
331
+ end
332
+
333
+ def metric_meter
334
+ @metric_meter ||= @runtime_metric_meter.with_additional_attributes(
335
+ {
336
+ namespace: info.workflow_namespace,
337
+ task_queue: info.task_queue,
338
+ activity_type: info.activity_type
339
+ }
340
+ )
341
+ end
342
+
343
+ def client
344
+ @worker.client
345
+ end
346
+ end
347
+
348
+ class InboundImplementation < Temporalio::Worker::Interceptor::Activity::Inbound
349
+ def initialize(worker)
350
+ super(nil) # steep:ignore
351
+ @worker = worker
352
+ end
353
+
354
+ def init(outbound)
355
+ context = Activity::Context.current
356
+ raise 'Unexpected context type' unless context.is_a?(RunningActivity)
357
+
358
+ context._outbound_impl = outbound
359
+ end
360
+
361
+ def execute(input)
362
+ input.proc.call(*input.args)
363
+ end
364
+ end
365
+
366
+ class OutboundImplementation < Temporalio::Worker::Interceptor::Activity::Outbound
367
+ def initialize(worker)
368
+ super(nil) # steep:ignore
369
+ @worker = worker
370
+ end
371
+
372
+ def heartbeat(input)
373
+ @worker.bridge_worker.record_activity_heartbeat(
374
+ Bridge::Api::CoreInterface::ActivityHeartbeat.new(
375
+ task_token: Activity::Context.current.info.task_token,
376
+ details: ProtoUtils.convert_to_payload_array(@worker.worker.options.client.data_converter,
377
+ input.details)
378
+ ).to_proto
379
+ )
380
+ end
381
+ end
382
+ end
383
+ end
384
+ end
385
+ end