amit-temporalio 0.3.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.
- checksums.yaml +7 -0
- data/.yardopts +2 -0
- data/Gemfile +23 -0
- data/Rakefile +101 -0
- data/lib/temporalio/activity/complete_async_error.rb +11 -0
- data/lib/temporalio/activity/context.rb +116 -0
- data/lib/temporalio/activity/definition.rb +189 -0
- data/lib/temporalio/activity/info.rb +64 -0
- data/lib/temporalio/activity.rb +12 -0
- data/lib/temporalio/api/activity/v1/message.rb +25 -0
- data/lib/temporalio/api/batch/v1/message.rb +31 -0
- data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
- data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +126 -0
- data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +25 -0
- data/lib/temporalio/api/cloud/cloudservice.rb +3 -0
- data/lib/temporalio/api/cloud/identity/v1/message.rb +41 -0
- data/lib/temporalio/api/cloud/namespace/v1/message.rb +42 -0
- data/lib/temporalio/api/cloud/nexus/v1/message.rb +31 -0
- data/lib/temporalio/api/cloud/operation/v1/message.rb +28 -0
- data/lib/temporalio/api/cloud/region/v1/message.rb +24 -0
- data/lib/temporalio/api/cloud/resource/v1/message.rb +23 -0
- data/lib/temporalio/api/cloud/sink/v1/message.rb +24 -0
- data/lib/temporalio/api/cloud/usage/v1/message.rb +31 -0
- data/lib/temporalio/api/command/v1/message.rb +46 -0
- data/lib/temporalio/api/common/v1/grpc_status.rb +23 -0
- data/lib/temporalio/api/common/v1/message.rb +47 -0
- data/lib/temporalio/api/enums/v1/batch_operation.rb +22 -0
- data/lib/temporalio/api/enums/v1/command_type.rb +21 -0
- data/lib/temporalio/api/enums/v1/common.rb +26 -0
- data/lib/temporalio/api/enums/v1/event_type.rb +21 -0
- data/lib/temporalio/api/enums/v1/failed_cause.rb +26 -0
- data/lib/temporalio/api/enums/v1/namespace.rb +23 -0
- data/lib/temporalio/api/enums/v1/query.rb +22 -0
- data/lib/temporalio/api/enums/v1/reset.rb +23 -0
- data/lib/temporalio/api/enums/v1/schedule.rb +21 -0
- data/lib/temporalio/api/enums/v1/task_queue.rb +25 -0
- data/lib/temporalio/api/enums/v1/update.rb +22 -0
- data/lib/temporalio/api/enums/v1/workflow.rb +30 -0
- data/lib/temporalio/api/errordetails/v1/message.rb +42 -0
- data/lib/temporalio/api/export/v1/message.rb +24 -0
- data/lib/temporalio/api/failure/v1/message.rb +35 -0
- data/lib/temporalio/api/filter/v1/message.rb +27 -0
- data/lib/temporalio/api/history/v1/message.rb +90 -0
- data/lib/temporalio/api/namespace/v1/message.rb +31 -0
- data/lib/temporalio/api/nexus/v1/message.rb +40 -0
- data/lib/temporalio/api/operatorservice/v1/request_response.rb +49 -0
- data/lib/temporalio/api/operatorservice/v1/service.rb +23 -0
- data/lib/temporalio/api/operatorservice.rb +3 -0
- data/lib/temporalio/api/payload_visitor.rb +1513 -0
- data/lib/temporalio/api/protocol/v1/message.rb +23 -0
- data/lib/temporalio/api/query/v1/message.rb +27 -0
- data/lib/temporalio/api/replication/v1/message.rb +26 -0
- data/lib/temporalio/api/schedule/v1/message.rb +43 -0
- data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +25 -0
- data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +21 -0
- data/lib/temporalio/api/sdk/v1/user_metadata.rb +23 -0
- data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +23 -0
- data/lib/temporalio/api/taskqueue/v1/message.rb +45 -0
- data/lib/temporalio/api/testservice/v1/request_response.rb +31 -0
- data/lib/temporalio/api/testservice/v1/service.rb +23 -0
- data/lib/temporalio/api/update/v1/message.rb +33 -0
- data/lib/temporalio/api/version/v1/message.rb +26 -0
- data/lib/temporalio/api/workflow/v1/message.rb +43 -0
- data/lib/temporalio/api/workflowservice/v1/request_response.rb +204 -0
- data/lib/temporalio/api/workflowservice/v1/service.rb +23 -0
- data/lib/temporalio/api/workflowservice.rb +3 -0
- data/lib/temporalio/api.rb +14 -0
- data/lib/temporalio/cancellation.rb +170 -0
- data/lib/temporalio/client/activity_id_reference.rb +32 -0
- data/lib/temporalio/client/async_activity_handle.rb +85 -0
- data/lib/temporalio/client/connection/cloud_service.rb +726 -0
- data/lib/temporalio/client/connection/operator_service.rb +201 -0
- data/lib/temporalio/client/connection/service.rb +42 -0
- data/lib/temporalio/client/connection/test_service.rb +111 -0
- data/lib/temporalio/client/connection/workflow_service.rb +1041 -0
- data/lib/temporalio/client/connection.rb +316 -0
- data/lib/temporalio/client/interceptor.rb +416 -0
- data/lib/temporalio/client/schedule.rb +967 -0
- data/lib/temporalio/client/schedule_handle.rb +126 -0
- data/lib/temporalio/client/workflow_execution.rb +100 -0
- data/lib/temporalio/client/workflow_execution_count.rb +36 -0
- data/lib/temporalio/client/workflow_execution_status.rb +18 -0
- data/lib/temporalio/client/workflow_handle.rb +389 -0
- data/lib/temporalio/client/workflow_query_reject_condition.rb +14 -0
- data/lib/temporalio/client/workflow_update_handle.rb +65 -0
- data/lib/temporalio/client/workflow_update_wait_stage.rb +17 -0
- data/lib/temporalio/client.rb +484 -0
- data/lib/temporalio/common_enums.rb +41 -0
- data/lib/temporalio/converters/data_converter.rb +99 -0
- data/lib/temporalio/converters/failure_converter.rb +202 -0
- data/lib/temporalio/converters/payload_codec.rb +26 -0
- data/lib/temporalio/converters/payload_converter/binary_null.rb +34 -0
- data/lib/temporalio/converters/payload_converter/binary_plain.rb +35 -0
- data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +42 -0
- data/lib/temporalio/converters/payload_converter/composite.rb +66 -0
- data/lib/temporalio/converters/payload_converter/encoding.rb +35 -0
- data/lib/temporalio/converters/payload_converter/json_plain.rb +44 -0
- data/lib/temporalio/converters/payload_converter/json_protobuf.rb +41 -0
- data/lib/temporalio/converters/payload_converter.rb +71 -0
- data/lib/temporalio/converters/raw_value.rb +20 -0
- data/lib/temporalio/converters.rb +9 -0
- data/lib/temporalio/error/failure.rb +219 -0
- data/lib/temporalio/error.rb +155 -0
- data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +34 -0
- data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +31 -0
- data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +33 -0
- data/lib/temporalio/internal/bridge/api/common/common.rb +26 -0
- data/lib/temporalio/internal/bridge/api/core_interface.rb +40 -0
- data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +27 -0
- data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +33 -0
- data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +56 -0
- data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +57 -0
- data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +30 -0
- data/lib/temporalio/internal/bridge/api.rb +3 -0
- data/lib/temporalio/internal/bridge/client.rb +95 -0
- data/lib/temporalio/internal/bridge/runtime.rb +53 -0
- data/lib/temporalio/internal/bridge/temporalio_bridge.so +0 -0
- data/lib/temporalio/internal/bridge/testing.rb +66 -0
- data/lib/temporalio/internal/bridge/worker.rb +85 -0
- data/lib/temporalio/internal/bridge.rb +36 -0
- data/lib/temporalio/internal/client/implementation.rb +700 -0
- data/lib/temporalio/internal/metric.rb +122 -0
- data/lib/temporalio/internal/proto_utils.rb +133 -0
- data/lib/temporalio/internal/worker/activity_worker.rb +376 -0
- data/lib/temporalio/internal/worker/multi_runner.rb +213 -0
- data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
- data/lib/temporalio/internal/worker/workflow_instance/context.rb +333 -0
- data/lib/temporalio/internal/worker/workflow_instance/details.rb +44 -0
- data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +32 -0
- data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +22 -0
- data/lib/temporalio/internal/worker/workflow_instance/handler_execution.rb +25 -0
- data/lib/temporalio/internal/worker/workflow_instance/handler_hash.rb +41 -0
- data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +97 -0
- data/lib/temporalio/internal/worker/workflow_instance/inbound_implementation.rb +62 -0
- data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +415 -0
- data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +37 -0
- data/lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb +40 -0
- data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +163 -0
- data/lib/temporalio/internal/worker/workflow_instance.rb +730 -0
- data/lib/temporalio/internal/worker/workflow_worker.rb +236 -0
- data/lib/temporalio/internal.rb +7 -0
- data/lib/temporalio/metric.rb +109 -0
- data/lib/temporalio/retry_policy.rb +74 -0
- data/lib/temporalio/runtime.rb +314 -0
- data/lib/temporalio/scoped_logger.rb +96 -0
- data/lib/temporalio/search_attributes.rb +343 -0
- data/lib/temporalio/testing/activity_environment.rb +136 -0
- data/lib/temporalio/testing/workflow_environment.rb +383 -0
- data/lib/temporalio/testing.rb +10 -0
- data/lib/temporalio/version.rb +5 -0
- data/lib/temporalio/worker/activity_executor/fiber.rb +49 -0
- data/lib/temporalio/worker/activity_executor/thread_pool.rb +46 -0
- data/lib/temporalio/worker/activity_executor.rb +55 -0
- data/lib/temporalio/worker/interceptor.rb +362 -0
- data/lib/temporalio/worker/thread_pool.rb +237 -0
- data/lib/temporalio/worker/tuner.rb +189 -0
- data/lib/temporalio/worker/workflow_executor/thread_pool.rb +230 -0
- data/lib/temporalio/worker/workflow_executor.rb +26 -0
- data/lib/temporalio/worker/workflow_replayer.rb +343 -0
- data/lib/temporalio/worker.rb +569 -0
- data/lib/temporalio/workflow/activity_cancellation_type.rb +20 -0
- data/lib/temporalio/workflow/child_workflow_cancellation_type.rb +21 -0
- data/lib/temporalio/workflow/child_workflow_handle.rb +43 -0
- data/lib/temporalio/workflow/definition.rb +566 -0
- data/lib/temporalio/workflow/external_workflow_handle.rb +41 -0
- data/lib/temporalio/workflow/future.rb +151 -0
- data/lib/temporalio/workflow/handler_unfinished_policy.rb +13 -0
- data/lib/temporalio/workflow/info.rb +82 -0
- data/lib/temporalio/workflow/parent_close_policy.rb +19 -0
- data/lib/temporalio/workflow/update_info.rb +20 -0
- data/lib/temporalio/workflow.rb +529 -0
- data/lib/temporalio/workflow_history.rb +47 -0
- data/lib/temporalio.rb +11 -0
- data/temporalio.gemspec +28 -0
- metadata +236 -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,133 @@
|
|
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
|
+
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
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,376 @@
|
|
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 present
|
97
|
+
defn = @activities[start.activity_type] || @activities[nil]
|
98
|
+
if defn.nil?
|
99
|
+
raise Error::ApplicationError.new(
|
100
|
+
"Activity #{start.activity_type} for workflow #{start.workflow_execution.workflow_id} " \
|
101
|
+
"is not registered on this worker, available activities: #{@activities.keys.sort.join(', ')}",
|
102
|
+
type: 'NotFoundError'
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Run everything else in the excecutor
|
107
|
+
executor = @worker.options.activity_executors[defn.executor]
|
108
|
+
executor.execute_activity(defn) do
|
109
|
+
# Set current executor
|
110
|
+
Activity::Context._current_executor = executor
|
111
|
+
# Execute with error handling
|
112
|
+
execute_activity(task_token, defn, start)
|
113
|
+
ensure
|
114
|
+
# Unset at the end
|
115
|
+
Activity::Context._current_executor = nil
|
116
|
+
end
|
117
|
+
rescue Exception => e # rubocop:disable Lint/RescueException We are intending to catch everything here
|
118
|
+
remove_running_activity(task_token)
|
119
|
+
@scoped_logger.warn("Failed starting activity #{start.activity_type}")
|
120
|
+
@scoped_logger.warn(e)
|
121
|
+
|
122
|
+
# We need to complete the activity task as failed, but this is on the
|
123
|
+
# hot path for polling, so we want to complete it in the background
|
124
|
+
begin
|
125
|
+
@bridge_worker.complete_activity_task_in_background(
|
126
|
+
Bridge::Api::CoreInterface::ActivityTaskCompletion.new(
|
127
|
+
task_token:,
|
128
|
+
result: Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
129
|
+
failed: Bridge::Api::ActivityResult::Failure.new(
|
130
|
+
# TODO(cretz): If failure conversion does slow failure
|
131
|
+
# encoding, it can gum up the system
|
132
|
+
failure: @worker.options.client.data_converter.to_failure(e)
|
133
|
+
)
|
134
|
+
)
|
135
|
+
)
|
136
|
+
)
|
137
|
+
rescue StandardError => e_inner
|
138
|
+
@scoped_logger.error("Failed building start failure to return for #{start.activity_type}")
|
139
|
+
@scoped_logger.error(e_inner)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def handle_cancel_task(task_token, cancel)
|
144
|
+
activity = get_running_activity(task_token)
|
145
|
+
if activity.nil?
|
146
|
+
@scoped_logger.warn("Cannot find activity to cancel for token #{task_token}")
|
147
|
+
return
|
148
|
+
end
|
149
|
+
activity._server_requested_cancel = true
|
150
|
+
_, cancel_proc = activity.cancellation
|
151
|
+
begin
|
152
|
+
cancel_proc.call(reason: cancel.reason.to_s)
|
153
|
+
rescue StandardError => e
|
154
|
+
@scoped_logger.warn("Failed cancelling activity #{activity.info.activity_type} \
|
155
|
+
with ID #{activity.info.activity_id}")
|
156
|
+
@scoped_logger.warn(e)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def execute_activity(task_token, defn, start)
|
161
|
+
# Build info
|
162
|
+
info = Activity::Info.new(
|
163
|
+
activity_id: start.activity_id,
|
164
|
+
activity_type: start.activity_type,
|
165
|
+
attempt: start.attempt,
|
166
|
+
current_attempt_scheduled_time: Internal::ProtoUtils.timestamp_to_time(
|
167
|
+
start.current_attempt_scheduled_time
|
168
|
+
) || raise, # Never nil
|
169
|
+
heartbeat_details: ProtoUtils.convert_from_payload_array(
|
170
|
+
@worker.options.client.data_converter,
|
171
|
+
start.heartbeat_details.to_ary
|
172
|
+
),
|
173
|
+
heartbeat_timeout: Internal::ProtoUtils.duration_to_seconds(start.heartbeat_timeout),
|
174
|
+
local?: start.is_local,
|
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
|
179
|
+
task_queue: @worker.options.task_queue,
|
180
|
+
task_token:,
|
181
|
+
workflow_id: start.workflow_execution.workflow_id,
|
182
|
+
workflow_namespace: start.workflow_namespace,
|
183
|
+
workflow_run_id: start.workflow_execution.run_id,
|
184
|
+
workflow_type: start.workflow_type
|
185
|
+
).freeze
|
186
|
+
|
187
|
+
# Build input
|
188
|
+
input = Temporalio::Worker::Interceptor::Activity::ExecuteInput.new(
|
189
|
+
proc: defn.proc,
|
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) || {}
|
200
|
+
)
|
201
|
+
|
202
|
+
# Run
|
203
|
+
activity = RunningActivity.new(
|
204
|
+
info:,
|
205
|
+
cancellation: Cancellation.new,
|
206
|
+
worker_shutdown_cancellation: @worker._worker_shutdown_cancellation,
|
207
|
+
payload_converter: @worker.options.client.data_converter.payload_converter,
|
208
|
+
logger: @scoped_logger,
|
209
|
+
runtime_metric_meter: @runtime_metric_meter
|
210
|
+
)
|
211
|
+
Activity::Context._current_executor&.set_activity_context(defn, activity)
|
212
|
+
set_running_activity(task_token, activity)
|
213
|
+
run_activity(defn, activity, input)
|
214
|
+
rescue Exception => e # rubocop:disable Lint/RescueException We are intending to catch everything here
|
215
|
+
@scoped_logger.warn("Failed starting or sending completion for activity #{start.activity_type}")
|
216
|
+
@scoped_logger.warn(e)
|
217
|
+
# This means that the activity couldn't start or send completion (run
|
218
|
+
# handles its own errors).
|
219
|
+
begin
|
220
|
+
@bridge_worker.complete_activity_task(
|
221
|
+
Bridge::Api::CoreInterface::ActivityTaskCompletion.new(
|
222
|
+
task_token:,
|
223
|
+
result: Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
224
|
+
failed: Bridge::Api::ActivityResult::Failure.new(
|
225
|
+
failure: @worker.options.client.data_converter.to_failure(e)
|
226
|
+
)
|
227
|
+
)
|
228
|
+
)
|
229
|
+
)
|
230
|
+
rescue StandardError => e_inner
|
231
|
+
@scoped_logger.error("Failed sending failure for activity #{start.activity_type}")
|
232
|
+
@scoped_logger.error(e_inner)
|
233
|
+
end
|
234
|
+
ensure
|
235
|
+
Activity::Context._current_executor&.set_activity_context(defn, nil)
|
236
|
+
remove_running_activity(task_token)
|
237
|
+
end
|
238
|
+
|
239
|
+
def run_activity(defn, activity, input)
|
240
|
+
result = begin
|
241
|
+
# Create the instance. We choose to do this before interceptors so that it is available in the interceptor.
|
242
|
+
activity.instance = defn.instance.is_a?(Proc) ? defn.instance.call : defn.instance # steep:ignore
|
243
|
+
|
244
|
+
# Build impl with interceptors
|
245
|
+
# @type var impl: Temporalio::Worker::Interceptor::Activity::Inbound
|
246
|
+
impl = InboundImplementation.new(self)
|
247
|
+
impl = @worker._activity_interceptors.reverse_each.reduce(impl) do |acc, int|
|
248
|
+
int.intercept_activity(acc)
|
249
|
+
end
|
250
|
+
impl.init(OutboundImplementation.new(self))
|
251
|
+
|
252
|
+
# Execute
|
253
|
+
result = impl.execute(input)
|
254
|
+
|
255
|
+
# Success
|
256
|
+
Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
257
|
+
completed: Bridge::Api::ActivityResult::Success.new(
|
258
|
+
result: @worker.options.client.data_converter.to_payload(result)
|
259
|
+
)
|
260
|
+
)
|
261
|
+
rescue Exception => e # rubocop:disable Lint/RescueException We are intending to catch everything here
|
262
|
+
if e.is_a?(Activity::CompleteAsyncError)
|
263
|
+
# Wanting to complete async
|
264
|
+
@scoped_logger.debug('Completing activity asynchronously')
|
265
|
+
Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
266
|
+
will_complete_async: Bridge::Api::ActivityResult::WillCompleteAsync.new
|
267
|
+
)
|
268
|
+
elsif e.is_a?(Error::CanceledError) && activity._server_requested_cancel
|
269
|
+
# Server requested cancel
|
270
|
+
@scoped_logger.debug('Completing activity as canceled')
|
271
|
+
Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
272
|
+
cancelled: Bridge::Api::ActivityResult::Cancellation.new(
|
273
|
+
failure: @worker.options.client.data_converter.to_failure(e)
|
274
|
+
)
|
275
|
+
)
|
276
|
+
else
|
277
|
+
# General failure
|
278
|
+
@scoped_logger.warn('Completing activity as failed')
|
279
|
+
@scoped_logger.warn(e)
|
280
|
+
Bridge::Api::ActivityResult::ActivityExecutionResult.new(
|
281
|
+
failed: Bridge::Api::ActivityResult::Failure.new(
|
282
|
+
failure: @worker.options.client.data_converter.to_failure(e)
|
283
|
+
)
|
284
|
+
)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
@scoped_logger.debug("Sending activity completion: #{result}") if LOG_TASKS
|
289
|
+
@bridge_worker.complete_activity_task(
|
290
|
+
Bridge::Api::CoreInterface::ActivityTaskCompletion.new(
|
291
|
+
task_token: activity.info.task_token,
|
292
|
+
result:
|
293
|
+
)
|
294
|
+
)
|
295
|
+
end
|
296
|
+
|
297
|
+
class RunningActivity < Activity::Context
|
298
|
+
attr_reader :info, :cancellation, :worker_shutdown_cancellation, :payload_converter, :logger
|
299
|
+
attr_accessor :instance, :_outbound_impl, :_server_requested_cancel
|
300
|
+
|
301
|
+
def initialize( # rubocop:disable Lint/MissingSuper
|
302
|
+
info:,
|
303
|
+
cancellation:,
|
304
|
+
worker_shutdown_cancellation:,
|
305
|
+
payload_converter:,
|
306
|
+
logger:,
|
307
|
+
runtime_metric_meter:
|
308
|
+
)
|
309
|
+
@info = info
|
310
|
+
@cancellation = cancellation
|
311
|
+
@worker_shutdown_cancellation = worker_shutdown_cancellation
|
312
|
+
@payload_converter = payload_converter
|
313
|
+
@logger = logger
|
314
|
+
@runtime_metric_meter = runtime_metric_meter
|
315
|
+
@_outbound_impl = nil
|
316
|
+
@_server_requested_cancel = false
|
317
|
+
end
|
318
|
+
|
319
|
+
def heartbeat(*details)
|
320
|
+
raise 'Implementation not set yet' if _outbound_impl.nil?
|
321
|
+
|
322
|
+
# No-op if local
|
323
|
+
return if info.local?
|
324
|
+
|
325
|
+
_outbound_impl.heartbeat(Temporalio::Worker::Interceptor::Activity::HeartbeatInput.new(details:))
|
326
|
+
end
|
327
|
+
|
328
|
+
def metric_meter
|
329
|
+
@metric_meter ||= @runtime_metric_meter.with_additional_attributes(
|
330
|
+
{
|
331
|
+
namespace: info.workflow_namespace,
|
332
|
+
task_queue: info.task_queue,
|
333
|
+
activity_type: info.activity_type
|
334
|
+
}
|
335
|
+
)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
class InboundImplementation < Temporalio::Worker::Interceptor::Activity::Inbound
|
340
|
+
def initialize(worker)
|
341
|
+
super(nil) # steep:ignore
|
342
|
+
@worker = worker
|
343
|
+
end
|
344
|
+
|
345
|
+
def init(outbound)
|
346
|
+
context = Activity::Context.current
|
347
|
+
raise 'Unexpected context type' unless context.is_a?(RunningActivity)
|
348
|
+
|
349
|
+
context._outbound_impl = outbound
|
350
|
+
end
|
351
|
+
|
352
|
+
def execute(input)
|
353
|
+
input.proc.call(*input.args)
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
class OutboundImplementation < Temporalio::Worker::Interceptor::Activity::Outbound
|
358
|
+
def initialize(worker)
|
359
|
+
super(nil) # steep:ignore
|
360
|
+
@worker = worker
|
361
|
+
end
|
362
|
+
|
363
|
+
def heartbeat(input)
|
364
|
+
@worker.bridge_worker.record_activity_heartbeat(
|
365
|
+
Bridge::Api::CoreInterface::ActivityHeartbeat.new(
|
366
|
+
task_token: Activity::Context.current.info.task_token,
|
367
|
+
details: ProtoUtils.convert_to_payload_array(@worker.worker.options.client.data_converter,
|
368
|
+
input.details)
|
369
|
+
).to_proto
|
370
|
+
)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|