temporalio 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +2 -0
- data/Cargo.lock +980 -583
- data/Cargo.toml +2 -2
- data/Gemfile +7 -3
- data/README.md +769 -54
- data/Rakefile +10 -296
- data/ext/Cargo.toml +2 -0
- data/lib/temporalio/activity/complete_async_error.rb +1 -1
- data/lib/temporalio/activity/context.rb +18 -2
- data/lib/temporalio/activity/definition.rb +180 -65
- data/lib/temporalio/activity/info.rb +25 -21
- data/lib/temporalio/activity.rb +2 -59
- data/lib/temporalio/api/activity/v1/message.rb +25 -0
- data/lib/temporalio/api/batch/v1/message.rb +6 -1
- data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
- data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +34 -1
- data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +1 -1
- data/lib/temporalio/api/cloud/identity/v1/message.rb +6 -1
- data/lib/temporalio/api/cloud/namespace/v1/message.rb +8 -1
- data/lib/temporalio/api/cloud/nexus/v1/message.rb +31 -0
- data/lib/temporalio/api/cloud/operation/v1/message.rb +2 -1
- data/lib/temporalio/api/cloud/region/v1/message.rb +2 -1
- data/lib/temporalio/api/cloud/resource/v1/message.rb +23 -0
- data/lib/temporalio/api/cloud/sink/v1/message.rb +24 -0
- data/lib/temporalio/api/cloud/usage/v1/message.rb +31 -0
- data/lib/temporalio/api/command/v1/message.rb +1 -1
- data/lib/temporalio/api/common/v1/message.rb +8 -1
- data/lib/temporalio/api/deployment/v1/message.rb +38 -0
- data/lib/temporalio/api/enums/v1/batch_operation.rb +1 -1
- data/lib/temporalio/api/enums/v1/common.rb +1 -1
- data/lib/temporalio/api/enums/v1/deployment.rb +23 -0
- data/lib/temporalio/api/enums/v1/event_type.rb +1 -1
- data/lib/temporalio/api/enums/v1/failed_cause.rb +1 -1
- data/lib/temporalio/api/enums/v1/nexus.rb +21 -0
- data/lib/temporalio/api/enums/v1/reset.rb +1 -1
- data/lib/temporalio/api/enums/v1/workflow.rb +2 -1
- data/lib/temporalio/api/errordetails/v1/message.rb +3 -1
- data/lib/temporalio/api/failure/v1/message.rb +3 -1
- data/lib/temporalio/api/history/v1/message.rb +3 -1
- data/lib/temporalio/api/nexus/v1/message.rb +3 -2
- data/lib/temporalio/api/operatorservice/v1/service.rb +1 -1
- data/lib/temporalio/api/payload_visitor.rb +1581 -0
- data/lib/temporalio/api/query/v1/message.rb +2 -1
- data/lib/temporalio/api/schedule/v1/message.rb +2 -1
- data/lib/temporalio/api/taskqueue/v1/message.rb +4 -1
- data/lib/temporalio/api/testservice/v1/request_response.rb +31 -0
- data/lib/temporalio/api/testservice/v1/service.rb +23 -0
- data/lib/temporalio/api/workflow/v1/message.rb +9 -1
- data/lib/temporalio/api/workflowservice/v1/request_response.rb +46 -2
- data/lib/temporalio/api/workflowservice/v1/service.rb +1 -1
- data/lib/temporalio/api.rb +2 -0
- data/lib/temporalio/cancellation.rb +34 -14
- data/lib/temporalio/client/async_activity_handle.rb +12 -37
- data/lib/temporalio/client/connection/cloud_service.rb +309 -231
- data/lib/temporalio/client/connection/operator_service.rb +36 -84
- data/lib/temporalio/client/connection/service.rb +6 -5
- data/lib/temporalio/client/connection/test_service.rb +111 -0
- data/lib/temporalio/client/connection/workflow_service.rb +474 -441
- data/lib/temporalio/client/connection.rb +90 -44
- data/lib/temporalio/client/interceptor.rb +199 -60
- data/lib/temporalio/client/schedule.rb +991 -0
- data/lib/temporalio/client/schedule_handle.rb +126 -0
- data/lib/temporalio/client/with_start_workflow_operation.rb +115 -0
- data/lib/temporalio/client/workflow_execution.rb +26 -10
- data/lib/temporalio/client/workflow_handle.rb +41 -98
- data/lib/temporalio/client/workflow_update_handle.rb +3 -5
- data/lib/temporalio/client.rb +247 -44
- data/lib/temporalio/common_enums.rb +17 -0
- data/lib/temporalio/contrib/open_telemetry.rb +470 -0
- data/lib/temporalio/converters/data_converter.rb +4 -7
- data/lib/temporalio/converters/failure_converter.rb +5 -3
- data/lib/temporalio/converters/payload_converter/composite.rb +4 -0
- data/lib/temporalio/converters/payload_converter.rb +6 -8
- data/lib/temporalio/converters/raw_value.rb +20 -0
- data/lib/temporalio/error/failure.rb +1 -1
- data/lib/temporalio/error.rb +11 -2
- data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +1 -1
- data/lib/temporalio/internal/bridge/api/common/common.rb +2 -1
- data/lib/temporalio/internal/bridge/api/core_interface.rb +5 -1
- data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +33 -0
- data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +5 -1
- data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +4 -1
- data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +2 -1
- data/lib/temporalio/internal/bridge/client.rb +11 -6
- data/lib/temporalio/internal/bridge/runtime.rb +3 -0
- data/lib/temporalio/internal/bridge/testing.rb +23 -0
- data/lib/temporalio/internal/bridge/worker.rb +2 -0
- data/lib/temporalio/internal/bridge.rb +1 -1
- data/lib/temporalio/internal/client/implementation.rb +468 -71
- data/lib/temporalio/internal/metric.rb +122 -0
- data/lib/temporalio/internal/proto_utils.rb +118 -7
- data/lib/temporalio/internal/worker/activity_worker.rb +69 -29
- data/lib/temporalio/internal/worker/multi_runner.rb +53 -9
- data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
- data/lib/temporalio/internal/worker/workflow_instance/context.rb +383 -0
- data/lib/temporalio/internal/worker/workflow_instance/details.rb +46 -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 +400 -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 +183 -0
- data/lib/temporalio/internal/worker/workflow_instance.rb +774 -0
- data/lib/temporalio/internal/worker/workflow_worker.rb +239 -0
- data/lib/temporalio/metric.rb +109 -0
- data/lib/temporalio/retry_policy.rb +37 -14
- data/lib/temporalio/runtime/metric_buffer.rb +94 -0
- data/lib/temporalio/runtime.rb +160 -79
- data/lib/temporalio/search_attributes.rb +93 -37
- data/lib/temporalio/testing/activity_environment.rb +44 -16
- data/lib/temporalio/testing/workflow_environment.rb +276 -7
- data/lib/temporalio/version.rb +1 -1
- data/lib/temporalio/worker/activity_executor/thread_pool.rb +9 -217
- data/lib/temporalio/worker/activity_executor.rb +3 -3
- data/lib/temporalio/worker/interceptor.rb +343 -66
- data/lib/temporalio/worker/thread_pool.rb +237 -0
- data/lib/temporalio/worker/tuner.rb +38 -0
- data/lib/temporalio/worker/workflow_executor/thread_pool.rb +235 -0
- data/lib/temporalio/worker/workflow_executor.rb +26 -0
- data/lib/temporalio/worker/workflow_replayer.rb +350 -0
- data/lib/temporalio/worker.rb +235 -58
- 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 +598 -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 +104 -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 +575 -0
- data/lib/temporalio/workflow_history.rb +26 -1
- data/lib/temporalio.rb +4 -0
- data/temporalio.gemspec +4 -3
- metadata +73 -10
@@ -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(
|
9
|
-
return nil if
|
8
|
+
def self.seconds_to_duration(seconds_numeric)
|
9
|
+
return nil if seconds_numeric.nil?
|
10
10
|
|
11
|
-
seconds =
|
12
|
-
nanos = ((
|
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
|
-
|
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,61 @@ module Temporalio
|
|
49
105
|
|
50
106
|
converter.to_payloads(values).payloads.to_ary
|
51
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
|
52
163
|
end
|
53
164
|
end
|
54
165
|
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
|
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,10 @@ 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
|
96
|
+
# Find activity definition, falling back to dynamic if not found and not reserved name
|
92
97
|
defn = @activities[start.activity_type]
|
98
|
+
defn = @activities[nil] if !defn && !Internal::ProtoUtils.reserved_name?(start.activity_type)
|
99
|
+
|
93
100
|
if defn.nil?
|
94
101
|
raise Error::ApplicationError.new(
|
95
102
|
"Activity #{start.activity_type} for workflow #{start.workflow_execution.workflow_id} " \
|
@@ -109,7 +116,7 @@ module Temporalio
|
|
109
116
|
# Unset at the end
|
110
117
|
Activity::Context._current_executor = nil
|
111
118
|
end
|
112
|
-
rescue Exception => e # rubocop:disable Lint/RescueException We are intending to catch everything here
|
119
|
+
rescue Exception => e # rubocop:disable Lint/RescueException -- We are intending to catch everything here
|
113
120
|
remove_running_activity(task_token)
|
114
121
|
@scoped_logger.warn("Failed starting activity #{start.activity_type}")
|
115
122
|
@scoped_logger.warn(e)
|
@@ -158,17 +165,19 @@ module Temporalio
|
|
158
165
|
activity_id: start.activity_id,
|
159
166
|
activity_type: start.activity_type,
|
160
167
|
attempt: start.attempt,
|
161
|
-
current_attempt_scheduled_time:
|
168
|
+
current_attempt_scheduled_time: Internal::ProtoUtils.timestamp_to_time(
|
169
|
+
start.current_attempt_scheduled_time
|
170
|
+
) || raise, # Never nil
|
162
171
|
heartbeat_details: ProtoUtils.convert_from_payload_array(
|
163
172
|
@worker.options.client.data_converter,
|
164
173
|
start.heartbeat_details.to_ary
|
165
174
|
),
|
166
|
-
heartbeat_timeout: start.heartbeat_timeout
|
175
|
+
heartbeat_timeout: Internal::ProtoUtils.duration_to_seconds(start.heartbeat_timeout),
|
167
176
|
local?: start.is_local,
|
168
|
-
schedule_to_close_timeout: start.schedule_to_close_timeout
|
169
|
-
scheduled_time: start.scheduled_time
|
170
|
-
start_to_close_timeout: start.start_to_close_timeout
|
171
|
-
started_time: start.started_time
|
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
|
172
181
|
task_queue: @worker.options.task_queue,
|
173
182
|
task_token:,
|
174
183
|
workflow_id: start.workflow_execution.workflow_id,
|
@@ -178,27 +187,34 @@ module Temporalio
|
|
178
187
|
).freeze
|
179
188
|
|
180
189
|
# Build input
|
181
|
-
input = Temporalio::Worker::Interceptor::
|
190
|
+
input = Temporalio::Worker::Interceptor::Activity::ExecuteInput.new(
|
182
191
|
proc: defn.proc,
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
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) || {}
|
188
202
|
)
|
189
203
|
|
190
204
|
# Run
|
191
205
|
activity = RunningActivity.new(
|
206
|
+
worker: @worker,
|
192
207
|
info:,
|
193
208
|
cancellation: Cancellation.new,
|
194
209
|
worker_shutdown_cancellation: @worker._worker_shutdown_cancellation,
|
195
210
|
payload_converter: @worker.options.client.data_converter.payload_converter,
|
196
|
-
logger: @scoped_logger
|
211
|
+
logger: @scoped_logger,
|
212
|
+
runtime_metric_meter: @runtime_metric_meter
|
197
213
|
)
|
198
214
|
Activity::Context._current_executor&.set_activity_context(defn, activity)
|
199
215
|
set_running_activity(task_token, activity)
|
200
|
-
run_activity(activity, input)
|
201
|
-
rescue Exception => e # rubocop:disable Lint/RescueException We are intending to catch everything here
|
216
|
+
run_activity(defn, activity, input)
|
217
|
+
rescue Exception => e # rubocop:disable Lint/RescueException -- We are intending to catch everything here
|
202
218
|
@scoped_logger.warn("Failed starting or sending completion for activity #{start.activity_type}")
|
203
219
|
@scoped_logger.warn(e)
|
204
220
|
# This means that the activity couldn't start or send completion (run
|
@@ -223,12 +239,15 @@ module Temporalio
|
|
223
239
|
remove_running_activity(task_token)
|
224
240
|
end
|
225
241
|
|
226
|
-
def run_activity(activity, input)
|
242
|
+
def run_activity(defn, activity, input)
|
227
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
|
+
|
228
247
|
# Build impl with interceptors
|
229
|
-
# @type var impl: Temporalio::Worker::Interceptor::
|
248
|
+
# @type var impl: Temporalio::Worker::Interceptor::Activity::Inbound
|
230
249
|
impl = InboundImplementation.new(self)
|
231
|
-
impl = @worker.
|
250
|
+
impl = @worker._activity_interceptors.reverse_each.reduce(impl) do |acc, int|
|
232
251
|
int.intercept_activity(acc)
|
233
252
|
end
|
234
253
|
impl.init(OutboundImplementation.new(self))
|
@@ -242,7 +261,7 @@ module Temporalio
|
|
242
261
|
result: @worker.options.client.data_converter.to_payload(result)
|
243
262
|
)
|
244
263
|
)
|
245
|
-
rescue Exception => e # rubocop:disable Lint/RescueException We are intending to catch everything here
|
264
|
+
rescue Exception => e # rubocop:disable Lint/RescueException -- We are intending to catch everything here
|
246
265
|
if e.is_a?(Activity::CompleteAsyncError)
|
247
266
|
# Wanting to complete async
|
248
267
|
@scoped_logger.debug('Completing activity asynchronously')
|
@@ -280,20 +299,24 @@ module Temporalio
|
|
280
299
|
|
281
300
|
class RunningActivity < Activity::Context
|
282
301
|
attr_reader :info, :cancellation, :worker_shutdown_cancellation, :payload_converter, :logger
|
283
|
-
attr_accessor :_outbound_impl, :_server_requested_cancel
|
302
|
+
attr_accessor :instance, :_outbound_impl, :_server_requested_cancel
|
284
303
|
|
285
304
|
def initialize( # rubocop:disable Lint/MissingSuper
|
305
|
+
worker:,
|
286
306
|
info:,
|
287
307
|
cancellation:,
|
288
308
|
worker_shutdown_cancellation:,
|
289
309
|
payload_converter:,
|
290
|
-
logger
|
310
|
+
logger:,
|
311
|
+
runtime_metric_meter:
|
291
312
|
)
|
313
|
+
@worker = worker
|
292
314
|
@info = info
|
293
315
|
@cancellation = cancellation
|
294
316
|
@worker_shutdown_cancellation = worker_shutdown_cancellation
|
295
317
|
@payload_converter = payload_converter
|
296
318
|
@logger = logger
|
319
|
+
@runtime_metric_meter = runtime_metric_meter
|
297
320
|
@_outbound_impl = nil
|
298
321
|
@_server_requested_cancel = false
|
299
322
|
end
|
@@ -301,11 +324,28 @@ module Temporalio
|
|
301
324
|
def heartbeat(*details)
|
302
325
|
raise 'Implementation not set yet' if _outbound_impl.nil?
|
303
326
|
|
304
|
-
|
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
|
305
345
|
end
|
306
346
|
end
|
307
347
|
|
308
|
-
class InboundImplementation < Temporalio::Worker::Interceptor::
|
348
|
+
class InboundImplementation < Temporalio::Worker::Interceptor::Activity::Inbound
|
309
349
|
def initialize(worker)
|
310
350
|
super(nil) # steep:ignore
|
311
351
|
@worker = worker
|
@@ -323,7 +363,7 @@ module Temporalio
|
|
323
363
|
end
|
324
364
|
end
|
325
365
|
|
326
|
-
class OutboundImplementation < Temporalio::Worker::Interceptor::
|
366
|
+
class OutboundImplementation < Temporalio::Worker::Interceptor::Activity::Outbound
|
327
367
|
def initialize(worker)
|
328
368
|
super(nil) # steep:ignore
|
329
369
|
@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
|
@@ -32,7 +34,7 @@ module Temporalio
|
|
32
34
|
rescue InjectEventForTesting => e
|
33
35
|
@queue.push(e.event)
|
34
36
|
@queue.push(Event::BlockSuccess.new(result: e))
|
35
|
-
rescue Exception => e # rubocop:disable Lint/RescueException Intentionally catch all
|
37
|
+
rescue Exception => e # rubocop:disable Lint/RescueException -- Intentionally catch all
|
36
38
|
@queue.push(Event::BlockFailure.new(error: e))
|
37
39
|
end
|
38
40
|
else
|
@@ -41,12 +43,22 @@ module Temporalio
|
|
41
43
|
rescue InjectEventForTesting => e
|
42
44
|
@queue.push(e.event)
|
43
45
|
@queue.push(Event::BlockSuccess.new(result: e))
|
44
|
-
rescue Exception => e # rubocop:disable Lint/RescueException Intentionally catch all
|
46
|
+
rescue Exception => e # rubocop:disable Lint/RescueException -- Intentionally catch all
|
45
47
|
@queue.push(Event::BlockFailure.new(error: e))
|
46
48
|
end
|
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
|
-
|
88
|
-
if
|
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[
|
92
|
-
case
|
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
|
111
|
+
Event::PollFailure.new(worker:, worker_type: second, error: third)
|
97
112
|
else
|
98
|
-
Event::PollSuccess.new(worker:, worker_type
|
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
|