temporalio 1.4.0 → 1.5.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.
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/api'
4
+ require 'temporalio/client/activity_execution'
5
+ require 'temporalio/client/interceptor'
6
+ require 'temporalio/error'
7
+
8
+ module Temporalio
9
+ class Client
10
+ # Handle for interacting with a standalone activity. Usually created via {Client.activity_handle}
11
+ # or {Client#start_activity}.
12
+ #
13
+ # WARNING: Standalone Activities are experimental.
14
+ class ActivityHandle
15
+ # @return [String] ID for the activity.
16
+ attr_reader :id
17
+
18
+ # @return [String, nil] Run ID for this activity execution. When nil, this handle targets the latest run.
19
+ attr_reader :run_id
20
+
21
+ # @return [Object, nil] Result hint used when deserializing the activity's result. May be overridden per
22
+ # {#result} call.
23
+ attr_reader :result_hint
24
+
25
+ # @!visibility private
26
+ def initialize(client:, id:, run_id:, result_hint:)
27
+ @client = client
28
+ @id = id
29
+ @run_id = run_id
30
+ @result_hint = result_hint
31
+ end
32
+
33
+ # Wait for the activity's outcome (result or failure). Internally long-polls
34
+ # PollActivityExecution and reissues until the activity reaches a terminal state, so this can
35
+ # block indefinitely for long-running activities.
36
+ #
37
+ # @param result_hint [Object, nil] Override the result hint. If nil, uses {#result_hint}.
38
+ # @param rpc_options [RPCOptions, nil] Advanced RPC options.
39
+ #
40
+ # @return [Object, nil] Deserialized activity result.
41
+ #
42
+ # @raise [Error::ActivityFailedError] With `cause` populated from the activity failure.
43
+ # @raise [Error::RPCError] RPC error from call.
44
+ def result(result_hint: nil, rpc_options: nil)
45
+ hint = result_hint || @result_hint
46
+ outcome = @client._impl.fetch_activity_outcome(
47
+ Interceptor::FetchActivityOutcomeInput.new(
48
+ activity_id: id,
49
+ activity_run_id: run_id,
50
+ rpc_options:
51
+ )
52
+ )
53
+ _process_outcome(outcome, hint)
54
+ end
55
+
56
+ # Describe the activity.
57
+ #
58
+ # @param rpc_options [RPCOptions, nil] Advanced RPC options.
59
+ #
60
+ # @return [ActivityExecution::Description] Activity description.
61
+ # @raise [Error::RPCError] RPC error from call.
62
+ def describe(rpc_options: nil)
63
+ @client._impl.describe_activity(
64
+ Interceptor::DescribeActivityInput.new(
65
+ activity_id: id,
66
+ activity_run_id: run_id,
67
+ rpc_options:
68
+ )
69
+ )
70
+ end
71
+
72
+ # Request cancellation of the activity.
73
+ #
74
+ # @param reason [String, nil] Optional cancellation reason recorded on the server.
75
+ # @param rpc_options [RPCOptions, nil] Advanced RPC options.
76
+ # @raise [Error::RPCError] RPC error from call.
77
+ def cancel(reason = nil, rpc_options: nil)
78
+ @client._impl.cancel_activity(
79
+ Interceptor::CancelActivityInput.new(
80
+ activity_id: id,
81
+ activity_run_id: run_id,
82
+ reason:,
83
+ rpc_options:
84
+ )
85
+ )
86
+ nil
87
+ end
88
+
89
+ # Terminate the activity (force-close).
90
+ #
91
+ # @param reason [String, nil] Optional termination reason recorded on the activity's failure outcome.
92
+ # @param rpc_options [RPCOptions, nil] Advanced RPC options.
93
+ # @raise [Error::RPCError] RPC error from call.
94
+ def terminate(reason = nil, rpc_options: nil)
95
+ @client._impl.terminate_activity(
96
+ Interceptor::TerminateActivityInput.new(
97
+ activity_id: id,
98
+ activity_run_id: run_id,
99
+ reason:,
100
+ rpc_options:
101
+ )
102
+ )
103
+ nil
104
+ end
105
+
106
+ private
107
+
108
+ def _process_outcome(outcome, hint)
109
+ raise Error, 'Activity completed but outcome is missing from server response' if outcome.nil?
110
+
111
+ case outcome.value
112
+ when :failure
113
+ cause = @client.data_converter.from_failure(outcome.failure)
114
+ raise Error::ActivityFailedError.new, cause: cause
115
+ when :result
116
+ @client.data_converter.from_payloads(outcome.result, hints: Array(hint)).first
117
+ else
118
+ raise Error, "Unknown activity outcome: #{outcome.value.inspect}"
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -6,26 +6,61 @@ require 'temporalio/error'
6
6
 
7
7
  module Temporalio
8
8
  class Client
9
- # Reference to an existing activity by its workflow ID, run ID, and activity ID.
9
+ # Reference to an activity for use with {Client#async_activity_handle}. There are two shapes,
10
+ # depending on whether the activity is run in a workflow, or as a standalone activity.
11
+ #
12
+ # 1. Activity run in a workflow -- use {ActivityIDReference#initialize}:
13
+ # `ActivityIDReference.new(workflow_id:, run_id:, activity_id:)`.
14
+ #
15
+ # 2. Standalone Activity (started via {Client#start_activity}): use the class factory
16
+ # {ActivityIDReference.for_standalone}: `ActivityIDReference.for_standalone(activity_id:, activity_run_id:)`.
10
17
  class ActivityIDReference
11
- # @return [String] ID for the workflow.
18
+ # @return [String] ID for the activity.
19
+ attr_reader :activity_id
20
+
21
+ # @return [String, nil] Activity run ID. Set only for standalone activity references.
22
+ attr_reader :activity_run_id
23
+
24
+ # @return [String, nil] ID for the workflow. Set only for workflow-run activity references.
12
25
  attr_reader :workflow_id
13
26
 
14
- # @return [String, nil] Run ID for the workflow.
27
+ # @return [String, nil] Run ID for the workflow. Set only for workflow-run activity references.
15
28
  attr_reader :run_id
16
29
 
17
- # @return [String] ID for the activity.
18
- attr_reader :activity_id
30
+ # Construct a standalone activity reference.
31
+ #
32
+ # WARNING: Standalone Activities are experimental.
33
+ #
34
+ # @param activity_id [String] ID for the activity.
35
+ # @param activity_run_id [String, nil] Run ID for the activity execution. nil targets the latest run.
36
+ # @return [ActivityIDReference] A reference suitable for {Client#async_activity_handle}.
37
+ def self.for_standalone(activity_id:, activity_run_id: nil)
38
+ allocate.tap do |ref|
39
+ ref.instance_variable_set(:@activity_id, activity_id)
40
+ ref.instance_variable_set(:@activity_run_id, activity_run_id)
41
+ ref.instance_variable_set(:@workflow_id, nil)
42
+ ref.instance_variable_set(:@run_id, nil)
43
+ end
44
+ end
19
45
 
20
- # Create an activity ID reference.
46
+ # Construct a workflow-run activity reference.
21
47
  #
22
48
  # @param workflow_id [String] ID for the workflow.
23
49
  # @param run_id [String, nil] Run ID for the workflow.
24
- # @param activity_id [String] ID for the workflow.
50
+ # @param activity_id [String] ID for the activity.
51
+ # @return [ActivityIDReference] A reference suitable for {Client#async_activity_handle}.
25
52
  def initialize(workflow_id:, run_id:, activity_id:)
26
53
  @workflow_id = workflow_id
27
54
  @run_id = run_id
28
55
  @activity_id = activity_id
56
+ @activity_run_id = nil
57
+ end
58
+
59
+ # @return [Boolean] True if this reference is the standalone-form (activity_run_id without workflow_id).
60
+ #
61
+ # WARNING: Standalone Activities are experimental.
62
+ def standalone?
63
+ @workflow_id.nil?
29
64
  end
30
65
  end
31
66
  end
@@ -24,7 +24,8 @@ module Temporalio
24
24
  :keep_alive,
25
25
  :http_connect_proxy,
26
26
  :runtime,
27
- :lazy_connect
27
+ :lazy_connect,
28
+ :dns_load_balancing
28
29
  )
29
30
 
30
31
  # Options as returned from {options} for +**to_h+ splat use in {initialize}. See {initialize} for details.
@@ -132,6 +133,22 @@ module Temporalio
132
133
  # @return [String, nil] Pass for HTTP basic auth for the proxy, must be combined with {basic_auth_user}.
133
134
  class HTTPConnectProxyOptions; end # rubocop:disable Lint/EmptyClass
134
135
 
136
+ DnsLoadBalancingOptions = Data.define(
137
+ :resolution_interval
138
+ )
139
+
140
+ # DNS load balancing options for client connections. When set, Core periodically re-resolves the target host's
141
+ # DNS records and round-robins requests across the resolved addresses. Mutually exclusive with
142
+ # {HTTPConnectProxyOptions} -- DNS load balancing is silently disabled when an HTTP CONNECT proxy is configured.
143
+ #
144
+ # @!attribute resolution_interval
145
+ # @return [Float] How often to re-resolve DNS, in seconds. Default 30.0.
146
+ class DnsLoadBalancingOptions
147
+ def initialize(resolution_interval: 30.0)
148
+ super
149
+ end
150
+ end
151
+
135
152
  # @return [Options] Frozen options for this client which has the same attributes as {initialize}. Note that if
136
153
  # {api_key=} or {rpc_metadata=} are updated, the options object is replaced with those changes (it is not
137
154
  # mutated in place).
@@ -169,6 +186,8 @@ module Temporalio
169
186
  # @param lazy_connect [Boolean] If true, there is no connection until the first call is attempted or a worker
170
187
  # is created with it. Clients from lazy connections cannot be used for workers if they have not performed a
171
188
  # connection.
189
+ # @param dns_load_balancing [DnsLoadBalancingOptions, nil] DNS load balancing options for this connection. Default
190
+ # is +nil+ (disabled). Silently disabled when +http_connect_proxy+ is set, since the two are mutually exclusive.
172
191
  # @param around_connect [Proc, nil] If present, this proc accepts two values: options and a block. The block must
173
192
  # be yielded to only once with the options. The block does not return a meaningful value, nor should
174
193
  # around_connect.
@@ -185,6 +204,7 @@ module Temporalio
185
204
  http_connect_proxy: nil,
186
205
  runtime: Runtime.default,
187
206
  lazy_connect: false,
207
+ dns_load_balancing: nil,
188
208
  around_connect: nil
189
209
  )
190
210
  @options = Options.new(
@@ -197,7 +217,8 @@ module Temporalio
197
217
  keep_alive:,
198
218
  http_connect_proxy:,
199
219
  runtime:,
200
- lazy_connect:
220
+ lazy_connect:,
221
+ dns_load_balancing:
201
222
  ).freeze
202
223
  @core_client_mutex = Mutex.new
203
224
  # Create core client now if not lazy, applying around_connect if present
@@ -329,6 +350,11 @@ module Temporalio
329
350
  basic_auth_pass: @options.http_connect_proxy.basic_auth_pass
330
351
  )
331
352
  end
353
+ if (dns_load_balancing = @options.dns_load_balancing)
354
+ options.dns_load_balancing = Internal::Bridge::Client::DnsLoadBalancingOptions.new(
355
+ resolution_interval: dns_load_balancing.resolution_interval
356
+ )
357
+ end
332
358
  Internal::Bridge::Client.new(@options.runtime._core_runtime, options)
333
359
  end
334
360
  end
@@ -259,6 +259,87 @@ module Temporalio
259
259
  :rpc_options
260
260
  )
261
261
 
262
+ # Input for {Outbound.start_activity}.
263
+ #
264
+ # WARNING: Standalone Activities are experimental.
265
+ StartActivityInput = Data.define(
266
+ :activity,
267
+ :args,
268
+ :activity_id,
269
+ :task_queue,
270
+ :schedule_to_close_timeout,
271
+ :schedule_to_start_timeout,
272
+ :start_to_close_timeout,
273
+ :heartbeat_timeout,
274
+ :id_reuse_policy,
275
+ :id_conflict_policy,
276
+ :retry_policy,
277
+ :search_attributes,
278
+ :static_summary,
279
+ :static_details,
280
+ :headers,
281
+ :priority,
282
+ :start_delay,
283
+ :arg_hints,
284
+ :result_hint,
285
+ :rpc_options
286
+ )
287
+
288
+ # Input for {Outbound.describe_activity}.
289
+ #
290
+ # WARNING: Standalone Activities are experimental.
291
+ DescribeActivityInput = Data.define(
292
+ :activity_id,
293
+ :activity_run_id,
294
+ :rpc_options
295
+ )
296
+
297
+ # Input for {Outbound.cancel_activity}.
298
+ #
299
+ # WARNING: Standalone Activities are experimental.
300
+ CancelActivityInput = Data.define(
301
+ :activity_id,
302
+ :activity_run_id,
303
+ :reason,
304
+ :rpc_options
305
+ )
306
+
307
+ # Input for {Outbound.terminate_activity}.
308
+ #
309
+ # WARNING: Standalone Activities are experimental.
310
+ TerminateActivityInput = Data.define(
311
+ :activity_id,
312
+ :activity_run_id,
313
+ :reason,
314
+ :rpc_options
315
+ )
316
+
317
+ # Input for {Outbound.list_activities}.
318
+ #
319
+ # WARNING: Standalone Activities are experimental.
320
+ ListActivitiesInput = Data.define(
321
+ :query,
322
+ :rpc_options
323
+ )
324
+
325
+ # Input for {Outbound.count_activities}.
326
+ #
327
+ # WARNING: Standalone Activities are experimental.
328
+ CountActivitiesInput = Data.define(
329
+ :query,
330
+ :rpc_options
331
+ )
332
+
333
+ # Input for {Outbound.fetch_activity_outcome}. Used by `ActivityHandle#result` for long-polling
334
+ # the activity outcome via `PollActivityExecution`.
335
+ #
336
+ # WARNING: Standalone Activities are experimental.
337
+ FetchActivityOutcomeInput = Data.define(
338
+ :activity_id,
339
+ :activity_run_id,
340
+ :rpc_options
341
+ )
342
+
262
343
  # Outbound interceptor for intercepting client calls. This should be extended by users needing to intercept client
263
344
  # actions.
264
345
  class Outbound
@@ -467,6 +548,74 @@ module Temporalio
467
548
  def report_cancellation_async_activity(input)
468
549
  next_interceptor.report_cancellation_async_activity(input)
469
550
  end
551
+
552
+ # Called for every {Client.start_activity} and {Client.execute_activity} call.
553
+ #
554
+ # WARNING: Standalone Activities are experimental.
555
+ #
556
+ # @param input [StartActivityInput] Input.
557
+ # @return [ActivityHandle] Activity handle.
558
+ def start_activity(input)
559
+ next_interceptor.start_activity(input)
560
+ end
561
+
562
+ # Called for every {ActivityHandle.describe} call.
563
+ #
564
+ # WARNING: Standalone Activities are experimental.
565
+ #
566
+ # @param input [DescribeActivityInput] Input.
567
+ # @return [ActivityExecution::Description] Activity description.
568
+ def describe_activity(input)
569
+ next_interceptor.describe_activity(input)
570
+ end
571
+
572
+ # Called for every {ActivityHandle.cancel} call.
573
+ #
574
+ # WARNING: Standalone Activities are experimental.
575
+ #
576
+ # @param input [CancelActivityInput] Input.
577
+ def cancel_activity(input)
578
+ next_interceptor.cancel_activity(input)
579
+ end
580
+
581
+ # Called for every {ActivityHandle.terminate} call.
582
+ #
583
+ # WARNING: Standalone Activities are experimental.
584
+ #
585
+ # @param input [TerminateActivityInput] Input.
586
+ def terminate_activity(input)
587
+ next_interceptor.terminate_activity(input)
588
+ end
589
+
590
+ # Called for every {Client.list_activities} call.
591
+ #
592
+ # WARNING: Standalone Activities are experimental.
593
+ #
594
+ # @param input [ListActivitiesInput] Input.
595
+ # @return [Enumerator<ActivityExecution>] Activity executions.
596
+ def list_activities(input)
597
+ next_interceptor.list_activities(input)
598
+ end
599
+
600
+ # Called for every {Client.count_activities} call.
601
+ #
602
+ # WARNING: Standalone Activities are experimental.
603
+ #
604
+ # @param input [CountActivitiesInput] Input.
605
+ # @return [ActivityExecutionCount] Activity count.
606
+ def count_activities(input)
607
+ next_interceptor.count_activities(input)
608
+ end
609
+
610
+ # Called by {ActivityHandle.result} to long-poll the activity outcome.
611
+ #
612
+ # WARNING: Standalone Activities are experimental.
613
+ #
614
+ # @param input [FetchActivityOutcomeInput] Input.
615
+ # @return [Api::Activity::V1::ActivityExecutionOutcome] Activity outcome.
616
+ def fetch_activity_outcome(input)
617
+ next_interceptor.fetch_activity_outcome(input)
618
+ end
470
619
  end
471
620
  end
472
621
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/api'
4
+
5
+ module Temporalio
6
+ class Client
7
+ # More detailed breakdown of a running activity's state.
8
+ #
9
+ # WARNING: Standalone Activities are experimental.
10
+ module PendingActivityState
11
+ SCHEDULED = Api::Enums::V1::PendingActivityState::PENDING_ACTIVITY_STATE_SCHEDULED
12
+ STARTED = Api::Enums::V1::PendingActivityState::PENDING_ACTIVITY_STATE_STARTED
13
+ CANCEL_REQUESTED = Api::Enums::V1::PendingActivityState::PENDING_ACTIVITY_STATE_CANCEL_REQUESTED
14
+ PAUSED = Api::Enums::V1::PendingActivityState::PENDING_ACTIVITY_STATE_PAUSED
15
+ PAUSE_REQUESTED = Api::Enums::V1::PendingActivityState::PENDING_ACTIVITY_STATE_PAUSE_REQUESTED
16
+ end
17
+ end
18
+ end
@@ -3,6 +3,7 @@
3
3
  require 'temporalio/api'
4
4
  require 'temporalio/converters'
5
5
  require 'temporalio/internal/proto_utils'
6
+ require 'temporalio/priority'
6
7
  require 'temporalio/retry_policy'
7
8
  require 'temporalio/search_attributes'
8
9
 
@@ -175,6 +176,7 @@ module Temporalio
175
176
  :retry_policy,
176
177
  :memo,
177
178
  :search_attributes,
179
+ :priority,
178
180
  :arg_hints,
179
181
  :headers
180
182
  )
@@ -209,6 +211,8 @@ module Temporalio
209
211
  # @return [Hash<String, Object>, nil] Memo for the workflow.
210
212
  # @!attribute search_attributes
211
213
  # @return [SearchAttributes, nil] Search attributes for the workflow.
214
+ # @!attribute priority
215
+ # @return [Priority] Priority of the workflow. This is currently experimental.
212
216
  # @!attribute arg_hints
213
217
  # @return [Array<Object>, nil] Converter hints for workflow arguments. This is only user-set (e.g. on create)
214
218
  # and is not persisted and therefore will not be set when describing a workflow.
@@ -239,6 +243,7 @@ module Temporalio
239
243
  # @param retry_policy [RetryPolicy, nil] Retry policy for the workflow.
240
244
  # @param memo [Hash<String, Object>, nil] Memo for the workflow.
241
245
  # @param search_attributes [SearchAttributes, nil] Search attributes for the workflow.
246
+ # @param priority [Priority] Priority of the workflow. This is currently experimental.
242
247
  # @param headers [Hash<String, Object>, nil] Headers for the workflow.
243
248
  def new(
244
249
  workflow,
@@ -253,6 +258,7 @@ module Temporalio
253
258
  retry_policy: nil,
254
259
  memo: nil,
255
260
  search_attributes: nil,
261
+ priority: Priority.default,
256
262
  arg_hints: nil,
257
263
  headers: nil
258
264
  )
@@ -271,6 +277,7 @@ module Temporalio
271
277
  retry_policy:,
272
278
  memo:,
273
279
  search_attributes:,
280
+ priority:,
274
281
  arg_hints: arg_hints || defn_arg_hints,
275
282
  headers:
276
283
  )
@@ -293,6 +300,7 @@ module Temporalio
293
300
  retry_policy: raw_info.retry_policy ? RetryPolicy._from_proto(raw_info.retry_policy) : nil,
294
301
  memo: Internal::ProtoUtils.memo_from_proto(raw_info.memo, data_converter),
295
302
  search_attributes: SearchAttributes._from_proto(raw_info.search_attributes),
303
+ priority: Priority._from_proto(raw_info.priority),
296
304
  headers: Internal::ProtoUtils.headers_from_proto(raw_info.header, data_converter)
297
305
  )
298
306
  end
@@ -312,6 +320,7 @@ module Temporalio
312
320
  memo: Internal::ProtoUtils.memo_to_proto(memo, data_converter),
313
321
  search_attributes: search_attributes&._to_proto,
314
322
  header: Internal::ProtoUtils.headers_to_proto(headers, data_converter),
323
+ priority: priority._to_proto,
315
324
  user_metadata: Internal::ProtoUtils.to_user_metadata(static_summary, static_details, data_converter)
316
325
  )
317
326
  )
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'temporalio/common_enums'
4
+ require 'temporalio/priority'
4
5
 
5
6
  module Temporalio
6
7
  class Client
@@ -24,6 +25,7 @@ module Temporalio
24
25
  :memo,
25
26
  :search_attributes,
26
27
  :start_delay,
28
+ :priority,
27
29
  :arg_hints,
28
30
  :result_hint,
29
31
  :headers
@@ -40,6 +42,8 @@ module Temporalio
40
42
  #
41
43
  # Note, for {Client.start_update_with_start_workflow} and {Client.execute_update_with_start_workflow},
42
44
  # `id_conflict_policy` is required.
45
+ #
46
+ # @param priority [Priority] Priority of the workflow that may be started. This is currently experimental.
43
47
  def initialize(
44
48
  workflow,
45
49
  *args,
@@ -57,6 +61,7 @@ module Temporalio
57
61
  memo: nil,
58
62
  search_attributes: nil,
59
63
  start_delay: nil,
64
+ priority: Priority.default,
60
65
  arg_hints: nil,
61
66
  result_hint: nil,
62
67
  headers: {}
@@ -80,6 +85,7 @@ module Temporalio
80
85
  memo:,
81
86
  search_attributes:,
82
87
  start_delay:,
88
+ priority:,
83
89
  arg_hints: arg_hints || defn_arg_hints,
84
90
  result_hint: result_hint || defn_result_hint,
85
91
  headers: