temporalio 1.2.0 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f975392bb8cb3a4fddc500af9bed6b6c1462a2c959ff32590082f71e0506900d
4
- data.tar.gz: 0662e05ad2df429a11e9afca0c53310353ec483e61149eccaedcc636ed6cb184
3
+ metadata.gz: 402b9eb0039d985a8e98f5cebfcae841af4483c06d070a76f9f3b213b4853049
4
+ data.tar.gz: 2d2d14d2e8d13d825db718267ea086fedefc5562843e1eb584bbd20b2cf02c90
5
5
  SHA512:
6
- metadata.gz: 07007a45cb8c01381cec79c406fe2f43ea0146b57a99599057d1d5784e3e6d110c765c01ecd7370936dba8123a6f7e2c245456c412ac5125e55e1bb3d41c2e5e
7
- data.tar.gz: fb1db70aa8ab4ca4d42c107830b544576996fa8411664d8adaae2e894f6e424032c25e855d607fc24e30b53167b2cf023205f1f0e568002c80cbb42170798554
6
+ metadata.gz: 90dda134d91f20010315e248caf76065f21c4d806219612b14e20823ed947480129aea0098c602d6b639ecc296e9a22c0694fae484b46aad630432307c4c0cc6
7
+ data.tar.gz: f64e68cb9b0a58be826314ea1a40dff5bd86ea5b230eebea5ba129a9f6100b72ce454247b314781c2541b784ac4141681f0a2da3f03406996c5c030426aa1c61
data/Cargo.lock CHANGED
@@ -2,6 +2,15 @@
2
2
  # It is not intended for manual editing.
3
3
  version = 4
4
4
 
5
+ [[package]]
6
+ name = "addr2line"
7
+ version = "0.25.1"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
10
+ dependencies = [
11
+ "gimli",
12
+ ]
13
+
5
14
  [[package]]
6
15
  name = "adler2"
7
16
  version = "2.0.1"
@@ -262,6 +271,21 @@ dependencies = [
262
271
  "rand 0.8.5",
263
272
  ]
264
273
 
274
+ [[package]]
275
+ name = "backtrace"
276
+ version = "0.3.76"
277
+ source = "registry+https://github.com/rust-lang/crates.io-index"
278
+ checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
279
+ dependencies = [
280
+ "addr2line",
281
+ "cfg-if",
282
+ "libc",
283
+ "miniz_oxide",
284
+ "object",
285
+ "rustc-demangle",
286
+ "windows-link 0.2.1",
287
+ ]
288
+
265
289
  [[package]]
266
290
  name = "base64"
267
291
  version = "0.21.7"
@@ -345,9 +369,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
345
369
 
346
370
  [[package]]
347
371
  name = "bytes"
348
- version = "1.11.0"
372
+ version = "1.11.1"
349
373
  source = "registry+https://github.com/rust-lang/crates.io-index"
350
- checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
374
+ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
351
375
 
352
376
  [[package]]
353
377
  name = "bzip2"
@@ -730,6 +754,22 @@ dependencies = [
730
754
  "unicode-xid",
731
755
  ]
732
756
 
757
+ [[package]]
758
+ name = "dhat"
759
+ version = "0.3.3"
760
+ source = "registry+https://github.com/rust-lang/crates.io-index"
761
+ checksum = "98cd11d84628e233de0ce467de10b8633f4ddaecafadefc86e13b84b8739b827"
762
+ dependencies = [
763
+ "backtrace",
764
+ "lazy_static",
765
+ "mintex",
766
+ "parking_lot",
767
+ "rustc-hash 1.1.0",
768
+ "serde",
769
+ "serde_json",
770
+ "thousands",
771
+ ]
772
+
733
773
  [[package]]
734
774
  name = "dirs"
735
775
  version = "6.0.0"
@@ -1056,6 +1096,12 @@ dependencies = [
1056
1096
  "wasm-bindgen",
1057
1097
  ]
1058
1098
 
1099
+ [[package]]
1100
+ name = "gimli"
1101
+ version = "0.32.3"
1102
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1103
+ checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
1104
+
1059
1105
  [[package]]
1060
1106
  name = "glob"
1061
1107
  version = "0.3.3"
@@ -1694,6 +1740,12 @@ dependencies = [
1694
1740
  "simd-adler32",
1695
1741
  ]
1696
1742
 
1743
+ [[package]]
1744
+ name = "mintex"
1745
+ version = "0.1.4"
1746
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1747
+ checksum = "c505b3e17ed6b70a7ed2e67fbb2c560ee327353556120d6e72f5232b6880d536"
1748
+
1697
1749
  [[package]]
1698
1750
  name = "mio"
1699
1751
  version = "1.1.1"
@@ -1799,6 +1851,15 @@ dependencies = [
1799
1851
  "objc2-core-foundation",
1800
1852
  ]
1801
1853
 
1854
+ [[package]]
1855
+ name = "object"
1856
+ version = "0.37.3"
1857
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1858
+ checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
1859
+ dependencies = [
1860
+ "memchr",
1861
+ ]
1862
+
1802
1863
  [[package]]
1803
1864
  name = "once_cell"
1804
1865
  version = "1.21.3"
@@ -2656,6 +2717,12 @@ dependencies = [
2656
2717
  "unicode-ident",
2657
2718
  ]
2658
2719
 
2720
+ [[package]]
2721
+ name = "rustc-demangle"
2722
+ version = "0.1.27"
2723
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2724
+ checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
2725
+
2659
2726
  [[package]]
2660
2727
  name = "rustc-hash"
2661
2728
  version = "1.1.0"
@@ -3172,6 +3239,7 @@ dependencies = [
3172
3239
  "anyhow",
3173
3240
  "assert_matches",
3174
3241
  "async-trait",
3242
+ "backoff",
3175
3243
  "bimap",
3176
3244
  "bon",
3177
3245
  "bytes",
@@ -3238,6 +3306,7 @@ name = "temporalio_bridge"
3238
3306
  version = "0.1.0"
3239
3307
  dependencies = [
3240
3308
  "async-trait",
3309
+ "dhat",
3241
3310
  "futures",
3242
3311
  "log",
3243
3312
  "magnus",
@@ -3310,6 +3379,12 @@ dependencies = [
3310
3379
  "syn",
3311
3380
  ]
3312
3381
 
3382
+ [[package]]
3383
+ name = "thousands"
3384
+ version = "0.2.0"
3385
+ source = "registry+https://github.com/rust-lang/crates.io-index"
3386
+ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
3387
+
3313
3388
  [[package]]
3314
3389
  name = "thread_local"
3315
3390
  version = "1.1.9"
data/ext/Cargo.toml CHANGED
@@ -26,3 +26,7 @@ tokio-util = "0.7"
26
26
  tonic = { workspace = true }
27
27
  tracing = "0.1"
28
28
  url = "2.5"
29
+ dhat = { version = "0.3", optional = true }
30
+
31
+ [features]
32
+ dhat-heap = ["dhat"]
@@ -298,7 +298,6 @@ module Temporalio
298
298
  # @param request_eager_start [Boolean] Potentially reduce the latency to start this workflow by encouraging the
299
299
  # server to start it on a local worker running with this same client. This is currently experimental.
300
300
  # @param versioning_override [VersioningOverride, nil] Override the version of the workflow.
301
- # This is currently experimental.
302
301
  # @param priority [Priority] Priority of the workflow. This is currently experimental.
303
302
  # @param arg_hints [Array<Object>, nil] Overrides converter hints for arguments if any. If unset/nil and the
304
303
  # workflow definition has arg hints, those are used by default.
@@ -392,7 +391,6 @@ module Temporalio
392
391
  # @param request_eager_start [Boolean] Potentially reduce the latency to start this workflow by encouraging the
393
392
  # server to start it on a local worker running with this same client. This is currently experimental.
394
393
  # @param versioning_override [VersioningOverride, nil] Override the version of the workflow.
395
- # This is currently experimental.
396
394
  # @param priority [Priority] Priority for the workflow. This is currently experimental.
397
395
  # @param arg_hints [Array<Object>, nil] Overrides converter hints for arguments if any. If unset/nil and the
398
396
  # workflow definition has arg hints, those are used by default.
@@ -40,8 +40,6 @@ module Temporalio
40
40
  end
41
41
 
42
42
  # Specifies when a workflow might move from a worker of one Build Id to another.
43
- #
44
- # WARNING: Experimental API.
45
43
  module VersioningBehavior
46
44
  # Unspecified versioning behavior. By default, workers opting into worker versioning will
47
45
  # be required to specify a behavior.
@@ -67,6 +67,11 @@ module Temporalio
67
67
  headers[@header_key] = carrier unless carrier.empty?
68
68
  end
69
69
 
70
+ # @!visibility private
71
+ def _propagator
72
+ @propagator
73
+ end
74
+
70
75
  # @!visibility private
71
76
  def _attach_context(headers)
72
77
  context = _context_from_headers(headers)
@@ -402,6 +407,22 @@ module Temporalio
402
407
  super
403
408
  end
404
409
 
410
+ # @!visibility private
411
+ def start_nexus_operation(input)
412
+ # Nexus headers are string-to-string maps (not payload-based like activity/workflow headers)
413
+ # so we inject the tracing context directly into the headers instead of nesting under a key
414
+ span = Workflow.completed_span("StartNexusOperation:#{input.service}/#{input.operation}", kind: :client)
415
+ Temporalio::Workflow::Unsafe.durable_scheduler_disabled do
416
+ if span
417
+ @root._propagator.inject(
418
+ input.headers,
419
+ context: ::OpenTelemetry::Trace.context_with_span(span)
420
+ )
421
+ end
422
+ end
423
+ super
424
+ end
425
+
405
426
  # @!visibility private
406
427
  def _apply_span_to_headers(headers, span)
407
428
  # See WorkflowInbound#_attach_context comments for why we have to disable scheduler even for these simple
@@ -84,6 +84,18 @@ module Temporalio
84
84
  started_event_id: error.started_event_id,
85
85
  retry_state: error.retry_state
86
86
  )
87
+ when Error::NexusOperationError
88
+ failure.nexus_operation_execution_failure_info = Api::Failure::V1::NexusOperationFailureInfo.new(
89
+ endpoint: error.endpoint,
90
+ service: error.service,
91
+ operation: error.operation,
92
+ operation_token: error.operation_token || ''
93
+ )
94
+ when Error::NexusHandlerError
95
+ failure.nexus_handler_failure_info = Api::Failure::V1::NexusHandlerFailureInfo.new(
96
+ type: error.error_type.to_s,
97
+ retry_behavior: error.retry_behavior
98
+ )
87
99
  else
88
100
  failure.application_failure_info = Api::Failure::V1::ApplicationFailureInfo.new(
89
101
  type: error.class.name.to_s.split('::').last
@@ -190,6 +202,24 @@ module Temporalio
190
202
  zero_means_nil: true
191
203
  )
192
204
  )
205
+ elsif failure.nexus_operation_execution_failure_info
206
+ token = failure.nexus_operation_execution_failure_info.operation_token
207
+ Error::NexusOperationError.new(
208
+ Internal::ProtoUtils.string_or(failure.message, 'Nexus operation error'),
209
+ endpoint: failure.nexus_operation_execution_failure_info.endpoint,
210
+ service: failure.nexus_operation_execution_failure_info.service,
211
+ operation: failure.nexus_operation_execution_failure_info.operation,
212
+ operation_token: token.empty? ? nil : token
213
+ )
214
+ elsif failure.nexus_handler_failure_info
215
+ Error::NexusHandlerError.new(
216
+ Internal::ProtoUtils.string_or(failure.message, 'Nexus handler error'),
217
+ error_type: failure.nexus_handler_failure_info.type,
218
+ retry_behavior: Internal::ProtoUtils.enum_to_int(
219
+ Api::Enums::V1::NexusHandlerErrorRetryBehavior,
220
+ failure.nexus_handler_failure_info.retry_behavior
221
+ )
222
+ )
193
223
  else
194
224
  Error::Failure.new(Internal::ProtoUtils.string_or(failure.message, 'Failure error'))
195
225
  end
@@ -4,9 +4,7 @@ require 'pathname'
4
4
  require 'temporalio/internal/bridge'
5
5
 
6
6
  module Temporalio
7
- # Environment and file-based configuration for Temporal clients
8
- #
9
- # WARNING: Experimental API.
7
+ # Environment and file-based configuration for Temporal clients.
10
8
  module EnvConfig
11
9
  # This module provides utilities to load Temporal client configuration from TOML files
12
10
  # and environment variables.
@@ -233,5 +233,71 @@ module Temporalio
233
233
  @retry_state = retry_state
234
234
  end
235
235
  end
236
+
237
+ # Error raised on Nexus operation failure.
238
+ #
239
+ # WARNING: Nexus support is experimental.
240
+ class NexusOperationError < Failure
241
+ # @return [String] Nexus endpoint.
242
+ attr_reader :endpoint
243
+ # @return [String] Nexus service.
244
+ attr_reader :service
245
+ # @return [String] Nexus operation.
246
+ attr_reader :operation
247
+ # @return [String, nil] Operation token for async operations.
248
+ attr_reader :operation_token
249
+
250
+ # @!visibility private
251
+ def initialize(
252
+ message,
253
+ endpoint:,
254
+ service:,
255
+ operation:,
256
+ operation_token:
257
+ )
258
+ super(message)
259
+ @endpoint = endpoint
260
+ @service = service
261
+ @operation = operation
262
+ @operation_token = operation_token
263
+ end
264
+ end
265
+
266
+ # Error raised from a Nexus handler.
267
+ #
268
+ # WARNING: Nexus support is experimental.
269
+ class NexusHandlerError < Failure
270
+ # @return [Symbol] Error type from the handler.
271
+ attr_reader :error_type
272
+
273
+ # @return [RetryBehavior] Retry behavior for the error.
274
+ attr_reader :retry_behavior
275
+
276
+ # @!visibility private
277
+ def initialize(
278
+ message,
279
+ error_type:,
280
+ retry_behavior:
281
+ )
282
+ super(message)
283
+ @error_type = error_type.to_sym
284
+ @retry_behavior = retry_behavior
285
+ end
286
+
287
+ # Nexus handler error retry behavior.
288
+ module RetryBehavior
289
+ # Unspecified retry behavior.
290
+ UNSPECIFIED =
291
+ Api::Enums::V1::NexusHandlerErrorRetryBehavior::NEXUS_HANDLER_ERROR_RETRY_BEHAVIOR_UNSPECIFIED
292
+
293
+ # Retryable error.
294
+ RETRYABLE =
295
+ Api::Enums::V1::NexusHandlerErrorRetryBehavior::NEXUS_HANDLER_ERROR_RETRY_BEHAVIOR_RETRYABLE
296
+
297
+ # Non-retryable error.
298
+ NON_RETRYABLE =
299
+ Api::Enums::V1::NexusHandlerErrorRetryBehavior::NEXUS_HANDLER_ERROR_RETRY_BEHAVIOR_NON_RETRYABLE
300
+ end
301
+ end
236
302
  end
237
303
  end
@@ -6,6 +6,7 @@ require 'temporalio/internal/bridge/api'
6
6
  require 'temporalio/internal/proto_utils'
7
7
  require 'temporalio/internal/worker/workflow_instance'
8
8
  require 'temporalio/internal/worker/workflow_instance/external_workflow_handle'
9
+ require 'temporalio/internal/worker/workflow_instance/nexus_client'
9
10
  require 'temporalio/worker/interceptor'
10
11
  require 'temporalio/workflow'
11
12
 
@@ -32,6 +33,10 @@ module Temporalio
32
33
  @instance.continue_as_new_suggested
33
34
  end
34
35
 
36
+ def create_nexus_client(endpoint:, service:)
37
+ NexusClient.new(endpoint:, service:, outbound: @outbound)
38
+ end
39
+
35
40
  def current_details
36
41
  @instance.current_details || ''
37
42
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/workflow'
4
+ require 'temporalio/workflow/nexus_client'
5
+
6
+ module Temporalio
7
+ module Internal
8
+ module Worker
9
+ class WorkflowInstance
10
+ # Implementation of the Nexus client.
11
+ class NexusClient < Workflow::NexusClient
12
+ attr_reader :endpoint, :service
13
+
14
+ def initialize(endpoint:, service:, outbound:) # rubocop:disable Lint/MissingSuper
15
+ @endpoint = endpoint.to_s
16
+ @service = service.to_s
17
+ @outbound = outbound
18
+ end
19
+
20
+ def start_operation(operation, arg, schedule_to_close_timeout: nil, cancellation_type: nil, summary: nil,
21
+ cancellation: Workflow.cancellation, arg_hint: nil, result_hint: nil)
22
+ @outbound.start_nexus_operation(
23
+ Temporalio::Worker::Interceptor::Workflow::StartNexusOperationInput.new(
24
+ endpoint: @endpoint,
25
+ service: @service,
26
+ operation: operation.to_s,
27
+ arg:,
28
+ schedule_to_close_timeout:,
29
+ cancellation_type:,
30
+ summary:,
31
+ cancellation:,
32
+ arg_hint:,
33
+ result_hint:,
34
+ headers: {}
35
+ )
36
+ )
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/cancellation'
4
+ require 'temporalio/workflow'
5
+ require 'temporalio/workflow/nexus_operation_handle'
6
+
7
+ module Temporalio
8
+ module Internal
9
+ module Worker
10
+ class WorkflowInstance
11
+ # Implementation of the Nexus operation handle.
12
+ class NexusOperationHandle < Workflow::NexusOperationHandle
13
+ attr_reader :operation_token, :result_hint
14
+
15
+ def initialize(operation_token:, instance:, cancellation:, cancel_callback_key:, # rubocop:disable Lint/MissingSuper
16
+ result_hint:)
17
+ @operation_token = operation_token
18
+ @instance = instance
19
+ @cancellation = cancellation
20
+ @cancel_callback_key = cancel_callback_key
21
+ @result_hint = result_hint
22
+ @resolution = nil
23
+ end
24
+
25
+ def result(result_hint: nil)
26
+ # Use detached cancellation like child workflow to avoid interrupting result wait
27
+ Workflow.wait_condition(cancellation: Cancellation.new) { @resolution }
28
+
29
+ case @resolution.status
30
+ when :completed
31
+ @instance.payload_converter.from_payload(@resolution.completed, hint: result_hint || @result_hint)
32
+ when :failed
33
+ raise @instance.failure_converter.from_failure(@resolution.failed, @instance.payload_converter)
34
+ when :cancelled
35
+ raise @instance.failure_converter.from_failure(@resolution.cancelled, @instance.payload_converter)
36
+ when :timed_out
37
+ raise @instance.failure_converter.from_failure(@resolution.timed_out, @instance.payload_converter)
38
+ else
39
+ raise "Unrecognized Nexus operation result status: #{@resolution.status}"
40
+ end
41
+ end
42
+
43
+ def _resolve(resolution)
44
+ @cancellation.remove_cancel_callback(@cancel_callback_key)
45
+ @resolution = resolution
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -6,6 +6,7 @@ require 'temporalio/error'
6
6
  require 'temporalio/internal/bridge/api'
7
7
  require 'temporalio/internal/proto_utils'
8
8
  require 'temporalio/internal/worker/workflow_instance'
9
+ require 'temporalio/internal/worker/workflow_instance/nexus_operation_handle'
9
10
  require 'temporalio/worker/interceptor'
10
11
  require 'temporalio/workflow'
11
12
  require 'temporalio/workflow/child_workflow_handle'
@@ -22,6 +23,7 @@ module Temporalio
22
23
  @activity_counter = 0
23
24
  @timer_counter = 0
24
25
  @child_counter = 0
26
+ @nexus_operation_counter = 0
25
27
  @external_signal_counter = 0
26
28
  @external_cancel_counter = 0
27
29
  end
@@ -429,6 +431,72 @@ module Temporalio
429
431
  raise "Unknown resolution status: #{resolution.status}"
430
432
  end
431
433
  end
434
+
435
+ def start_nexus_operation(input)
436
+ raise Error::CanceledError, 'Nexus operation canceled before scheduled' if input.cancellation.canceled?
437
+
438
+ # Add the command
439
+ seq = (@nexus_operation_counter += 1)
440
+ @instance.add_command(
441
+ Bridge::Api::WorkflowCommands::WorkflowCommand.new(
442
+ schedule_nexus_operation: Bridge::Api::WorkflowCommands::ScheduleNexusOperation.new(
443
+ seq:,
444
+ endpoint: input.endpoint,
445
+ service: input.service,
446
+ operation: input.operation,
447
+ input: @instance.payload_converter.to_payload(input.arg, hint: input.arg_hint),
448
+ schedule_to_close_timeout: ProtoUtils.seconds_to_duration(input.schedule_to_close_timeout),
449
+ nexus_header: input.headers,
450
+ cancellation_type: input.cancellation_type
451
+ ),
452
+ user_metadata: ProtoUtils.to_user_metadata(input.summary, nil, @instance.payload_converter)
453
+ )
454
+ )
455
+
456
+ # Set as pending start
457
+ @instance.pending_nexus_operation_starts[seq] = Fiber.current
458
+
459
+ # Register cancel callback
460
+ cancel_callback_key = input.cancellation.add_cancel_callback do
461
+ # Send cancel if in start or pending
462
+ if @instance.pending_nexus_operation_starts.include?(seq) ||
463
+ @instance.pending_nexus_operations.include?(seq)
464
+ @instance.add_command(
465
+ Bridge::Api::WorkflowCommands::WorkflowCommand.new(
466
+ request_cancel_nexus_operation: Bridge::Api::WorkflowCommands::RequestCancelNexusOperation.new(
467
+ seq:
468
+ )
469
+ )
470
+ )
471
+ end
472
+ end
473
+
474
+ # Wait for start resolution
475
+ resolution = begin
476
+ Fiber.yield
477
+ ensure
478
+ # Remove pending start
479
+ @instance.pending_nexus_operation_starts.delete(seq)
480
+ end
481
+
482
+ # Handle start failure
483
+ if resolution.failed
484
+ input.cancellation.remove_cancel_callback(cancel_callback_key)
485
+ raise @instance.failure_converter.from_failure(resolution.failed, @instance.payload_converter)
486
+ end
487
+
488
+ # Create handle and add to pending operations (result will come via resolve_nexus_operation)
489
+ handle = NexusOperationHandle.new(
490
+ operation_token: resolution.operation_token,
491
+ instance: @instance,
492
+ cancellation: input.cancellation,
493
+ cancel_callback_key:,
494
+ result_hint: input.result_hint
495
+ )
496
+ @instance.pending_nexus_operations[seq] = handle
497
+
498
+ handle
499
+ end
432
500
  end
433
501
  end
434
502
  end
@@ -54,6 +54,7 @@ module Temporalio
54
54
 
55
55
  attr_reader :context, :logger, :info, :scheduler, :disable_eager_activity_execution, :pending_activities,
56
56
  :pending_timers, :pending_child_workflow_starts, :pending_child_workflows,
57
+ :pending_nexus_operation_starts, :pending_nexus_operations,
57
58
  :pending_external_signals, :pending_external_cancels, :in_progress_handlers, :payload_converter,
58
59
  :failure_converter, :cancellation, :continue_as_new_suggested, :current_deployment_version,
59
60
  :current_history_length, :current_history_size, :replaying, :random,
@@ -79,6 +80,8 @@ module Temporalio
79
80
  @pending_timers = {} # Keyed by sequence, value is fiber to resume with proto result
80
81
  @pending_child_workflow_starts = {} # Keyed by sequence, value is fiber to resume with proto result
81
82
  @pending_child_workflows = {} # Keyed by sequence, value is ChildWorkflowHandle to resolve with proto result
83
+ @pending_nexus_operation_starts = {} # Keyed by sequence, value is fiber to resume with proto result
84
+ @pending_nexus_operations = {} # Keyed by sequence, value is NexusOperationHandle to resolve with proto result
82
85
  @pending_external_signals = {} # Keyed by sequence, value is fiber to resume with proto result
83
86
  @pending_external_cancels = {} # Keyed by sequence, value is fiber to resume with proto result
84
87
  @buffered_signals = {} # Keyed by signal name, value is array of signal jobs
@@ -369,6 +372,14 @@ module Temporalio
369
372
  pending_child_workflows[job.resolve_child_workflow_execution.seq]&._resolve(
370
373
  job.resolve_child_workflow_execution.result
371
374
  )
375
+ when :resolve_nexus_operation_start
376
+ pending_nexus_operation_starts[job.resolve_nexus_operation_start.seq]&.resume(
377
+ job.resolve_nexus_operation_start
378
+ )
379
+ when :resolve_nexus_operation
380
+ pending_nexus_operations[job.resolve_nexus_operation.seq]&._resolve(
381
+ job.resolve_nexus_operation.result
382
+ )
372
383
  when :resolve_signal_external_workflow
373
384
  pending_external_signals[job.resolve_signal_external_workflow.seq]&.resume(
374
385
  job.resolve_signal_external_workflow
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'delegate'
4
4
  require 'temporalio/api'
5
+ require 'temporalio/api/operatorservice/v1/request_response'
5
6
  require 'temporalio/api/testservice/v1/request_response'
6
7
  require 'temporalio/client'
7
8
  require 'temporalio/client/connection/test_service'
@@ -264,6 +265,45 @@ module Temporalio
264
265
  Time.now
265
266
  end
266
267
 
268
+ # Create Nexus endpoint on this test environment.
269
+ #
270
+ # WARNING: Nexus support is experimental.
271
+ #
272
+ # @param name [String] Endpoint name.
273
+ # @param task_queue [String] Task queue for the endpoint.
274
+ # @return [Temporalio::Api::Nexus::V1::Endpoint] Created endpoint.
275
+ def create_nexus_endpoint(name:, task_queue:)
276
+ resp = client.connection.operator_service.create_nexus_endpoint(
277
+ Temporalio::Api::OperatorService::V1::CreateNexusEndpointRequest.new(
278
+ spec: Temporalio::Api::Nexus::V1::EndpointSpec.new(
279
+ name:,
280
+ target: Temporalio::Api::Nexus::V1::EndpointTarget.new(
281
+ worker: Temporalio::Api::Nexus::V1::EndpointTarget::Worker.new(
282
+ namespace: client.namespace,
283
+ task_queue:
284
+ )
285
+ )
286
+ )
287
+ )
288
+ )
289
+ resp.endpoint
290
+ end
291
+
292
+ # Delete Nexus endpoint on this test environment.
293
+ #
294
+ # WARNING: Nexus support is experimental.
295
+ #
296
+ # @param endpoint [Temporalio::Api::Nexus::V1::Endpoint] Endpoint to delete.
297
+ def delete_nexus_endpoint(endpoint)
298
+ client.connection.operator_service.delete_nexus_endpoint(
299
+ Temporalio::Api::OperatorService::V1::DeleteNexusEndpointRequest.new(
300
+ id: endpoint.id,
301
+ version: endpoint.version
302
+ )
303
+ )
304
+ nil
305
+ end
306
+
267
307
  # Run a block with automatic time skipping disabled. This just runs the block for environments that don't support
268
308
  # time skipping.
269
309
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Temporalio
4
- VERSION = '1.2.0'
4
+ VERSION = '1.3.0'
5
5
  end
@@ -5,8 +5,6 @@ require 'temporalio/worker_deployment_version'
5
5
  module Temporalio
6
6
  # Base class for version overrides that can be provided in start workflow options.
7
7
  # Used to control the versioning behavior of workflows started with this override.
8
- #
9
- # WARNING: Experimental API.
10
8
  class VersioningOverride
11
9
  # @!visibility private
12
10
  def _to_proto
@@ -13,8 +13,6 @@ module Temporalio
13
13
 
14
14
  # Options for configuring the Worker Versioning feature.
15
15
  #
16
- # WARNING: Deployment-based versioning is experimental and APIs may change.
17
- #
18
16
  # @!attribute version
19
17
  # @return [WorkerDeploymentVersion] The worker deployment version.
20
18
  # @!attribute use_worker_versioning
@@ -300,6 +300,23 @@ module Temporalio
300
300
  :headers
301
301
  )
302
302
 
303
+ # Input for {Outbound.start_nexus_operation}.
304
+ #
305
+ # WARNING: Nexus support is experimental.
306
+ StartNexusOperationInput = Data.define(
307
+ :endpoint,
308
+ :service,
309
+ :operation,
310
+ :arg,
311
+ :schedule_to_close_timeout,
312
+ :cancellation_type,
313
+ :summary,
314
+ :cancellation,
315
+ :arg_hint,
316
+ :result_hint,
317
+ :headers
318
+ )
319
+
303
320
  # Outbound interceptor for intercepting outbound workflow calls. This should be extended by users needing to
304
321
  # intercept workflow calls.
305
322
  class Outbound
@@ -371,6 +388,16 @@ module Temporalio
371
388
  def start_child_workflow(input)
372
389
  @next_interceptor.start_child_workflow(input)
373
390
  end
391
+
392
+ # Start Nexus operation.
393
+ #
394
+ # WARNING: Nexus support is experimental.
395
+ #
396
+ # @param input [StartNexusOperationInput] Input.
397
+ # @return [Workflow::NexusOperationHandle] Nexus operation handle.
398
+ def start_nexus_operation(input)
399
+ @next_interceptor.start_nexus_operation(input)
400
+ end
374
401
  end
375
402
  end
376
403
  end
@@ -49,7 +49,7 @@ module Temporalio
49
49
  # If not found, get a new one either by creating if not enough or find the one with the fewest.
50
50
  new_worker = if @workers.size < @max_threads
51
51
  created_worker = Worker.new(self)
52
- @workers << Worker.new(self)
52
+ @workers << created_worker
53
53
  created_worker
54
54
  else
55
55
  @workers.min_by(&:workflow_count)
@@ -425,7 +425,6 @@ module Temporalio
425
425
  # scheduler will fail. Instead of setting this to true, users are encouraged to use {Workflow::Unsafe.io_enabled}
426
426
  # with a block for narrower enabling of IO.
427
427
  # @param deployment_options [DeploymentOptions, nil] Deployment options for the worker.
428
- # WARNING: This is an experimental feature and may change in the future.
429
428
  # @param workflow_task_poller_behavior [PollerBehavior] Specify the behavior of workflow task
430
429
  # polling. Defaults to a 5-poller maximum.
431
430
  # @param activity_task_poller_behavior [PollerBehavior] Specify the behavior of activity task
@@ -9,8 +9,6 @@ module Temporalio
9
9
  )
10
10
 
11
11
  # Represents the version of a specific worker deployment.
12
- #
13
- # WARNING: Experimental API.
14
12
  class WorkerDeploymentVersion
15
13
  # Parse a version from a canonical string, which must be in the format
16
14
  # `<deployment_name>.<build_id>`. Deployment name must not have a `.` in it.
@@ -110,8 +110,6 @@ module Temporalio
110
110
 
111
111
  # Set the versioning behavior of this workflow.
112
112
  #
113
- # WARNING: This method is experimental and may change in future versions.
114
- #
115
113
  # @param behavior [VersioningBehavior] The versioning behavior.
116
114
  def workflow_versioning_behavior(behavior)
117
115
  @versioning_behavior = behavior
@@ -732,7 +730,6 @@ module Temporalio
732
730
  # @!attribute versioning_behavior
733
731
  # Dynamic equivalent of {Definition.workflow_versioning_behavior}.
734
732
  # Will override any behavior set there if set.
735
- # WARNING: Deployment-based versioning is experimental and APIs may change.
736
733
  # @return [VersioningBehavior, nil] The versioning behavior
737
734
  #
738
735
  # @return [VersioningBehavior, nil] The versioning behavior
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Temporalio
4
+ module Workflow
5
+ # Client for executing Nexus operations from workflows.
6
+ #
7
+ # This is created via {Workflow.create_nexus_client}, it is never instantiated directly.
8
+ #
9
+ # WARNING: Nexus support is experimental.
10
+ class NexusClient
11
+ # @!visibility private
12
+ def initialize
13
+ raise NotImplementedError, 'Cannot instantiate a Nexus client directly'
14
+ end
15
+
16
+ # @return [String] Endpoint name for this client.
17
+ def endpoint
18
+ raise NotImplementedError
19
+ end
20
+
21
+ # @return [String] Service name for this client.
22
+ def service
23
+ raise NotImplementedError
24
+ end
25
+
26
+ # Start a Nexus operation and return a handle.
27
+ #
28
+ # @param operation [Symbol, String] Operation name.
29
+ # @param arg [Object] Argument for the operation.
30
+ # @param schedule_to_close_timeout [Float, nil] Total timeout for the operation in seconds.
31
+ # @param cancellation_type [NexusOperationCancellationType] How the operation will react to cancellation.
32
+ # @param summary [String, nil] Optional summary for the operation (appears in UI/CLI).
33
+ # @param cancellation [Cancellation] Cancellation for the operation.
34
+ # @param arg_hint [Object, nil] Converter hint for the argument.
35
+ # @param result_hint [Object, nil] Converter hint for the result.
36
+ # @return [NexusOperationHandle] Handle to the started operation.
37
+ def start_operation(
38
+ operation,
39
+ arg,
40
+ schedule_to_close_timeout: nil,
41
+ cancellation_type: NexusOperationCancellationType::WAIT_CANCELLATION_COMPLETED,
42
+ summary: nil,
43
+ cancellation: Workflow.cancellation,
44
+ arg_hint: nil,
45
+ result_hint: nil
46
+ )
47
+ raise NotImplementedError
48
+ end
49
+
50
+ # Execute a Nexus operation and wait for the result.
51
+ #
52
+ # This is a convenience method that calls {#start_operation} and immediately waits for the result.
53
+ #
54
+ # @param operation [Symbol, String] Operation name.
55
+ # @param arg [Object] Argument for the operation.
56
+ # @param schedule_to_close_timeout [Float, nil] Total timeout for the operation in seconds.
57
+ # @param cancellation_type [NexusOperationCancellationType] How the operation will react to cancellation.
58
+ # @param summary [String, nil] Optional summary for the operation (appears in UI/CLI).
59
+ # @param cancellation [Cancellation] Cancellation for the operation.
60
+ # @param arg_hint [Object, nil] Converter hint for the argument.
61
+ # @param result_hint [Object, nil] Converter hint for the result.
62
+ # @return [Object] Result of the operation.
63
+ # @raise [Error::NexusOperationError] Operation failed.
64
+ def execute_operation(
65
+ operation,
66
+ arg,
67
+ schedule_to_close_timeout: nil,
68
+ cancellation_type: NexusOperationCancellationType::WAIT_CANCELLATION_COMPLETED,
69
+ summary: nil,
70
+ cancellation: Workflow.cancellation,
71
+ arg_hint: nil,
72
+ result_hint: nil
73
+ )
74
+ start_operation(
75
+ operation, arg, schedule_to_close_timeout:, cancellation_type:, summary:, cancellation:,
76
+ arg_hint:, result_hint:
77
+ ).result
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/internal/bridge/api'
4
+
5
+ module Temporalio
6
+ module Workflow
7
+ # How a Nexus operation should handle cancellation.
8
+ #
9
+ # WARNING: Nexus support is experimental.
10
+ module NexusOperationCancellationType
11
+ # Wait for cancellation to complete (default).
12
+ WAIT_CANCELLATION_COMPLETED = Internal::Bridge::Api::Nexus::NexusOperationCancellationType::WAIT_CANCELLATION_COMPLETED
13
+ # Abandon the operation without sending a cancellation request.
14
+ ABANDON = Internal::Bridge::Api::Nexus::NexusOperationCancellationType::ABANDON
15
+ # Send a cancellation request but do not wait for confirmation.
16
+ TRY_CANCEL = Internal::Bridge::Api::Nexus::NexusOperationCancellationType::TRY_CANCEL
17
+ # Wait for the server to confirm the cancellation request was delivered.
18
+ WAIT_CANCELLATION_REQUESTED = Internal::Bridge::Api::Nexus::NexusOperationCancellationType::WAIT_CANCELLATION_REQUESTED
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Temporalio
4
+ module Workflow
5
+ # Handle for interacting with a Nexus operation.
6
+ #
7
+ # This is created via {NexusClient#start_operation}, it is never instantiated directly.
8
+ #
9
+ # WARNING: Nexus support is experimental.
10
+ class NexusOperationHandle
11
+ # @!visibility private
12
+ def initialize
13
+ raise NotImplementedError, 'Cannot instantiate a Nexus operation handle directly'
14
+ end
15
+
16
+ # @return [String, nil] Operation token for async operations, nil for sync operations.
17
+ def operation_token
18
+ raise NotImplementedError
19
+ end
20
+
21
+ # @return [Object, nil] Hint for the result if any.
22
+ def result_hint
23
+ raise NotImplementedError
24
+ end
25
+
26
+ # Wait for the result.
27
+ #
28
+ # @param result_hint [Object, nil] Override the result hint, or if nil uses the one on the handle.
29
+ # @return [Object] Result of the Nexus operation.
30
+ #
31
+ # @raise [Error::NexusOperationError] Operation failed with +cause+ as the cause.
32
+ def result(result_hint: nil)
33
+ raise NotImplementedError
34
+ end
35
+ end
36
+ end
37
+ end
@@ -12,6 +12,9 @@ require 'temporalio/workflow/external_workflow_handle'
12
12
  require 'temporalio/workflow/future'
13
13
  require 'temporalio/workflow/handler_unfinished_policy'
14
14
  require 'temporalio/workflow/info'
15
+ require 'temporalio/workflow/nexus_client'
16
+ require 'temporalio/workflow/nexus_operation_cancellation_type'
17
+ require 'temporalio/workflow/nexus_operation_handle'
15
18
  require 'temporalio/workflow/parent_close_policy'
16
19
  require 'temporalio/workflow/update_info'
17
20
  require 'timeout'
@@ -40,6 +43,17 @@ module Temporalio
40
43
  _current.continue_as_new_suggested
41
44
  end
42
45
 
46
+ # Create a Nexus client for executing operations.
47
+ #
48
+ # WARNING: Nexus support is experimental.
49
+ #
50
+ # @param endpoint [Symbol, String] Endpoint name.
51
+ # @param service [Symbol, String] Service name.
52
+ # @return [NexusClient] Client for executing Nexus operations.
53
+ def self.create_nexus_client(endpoint:, service:)
54
+ _current.create_nexus_client(endpoint:, service:)
55
+ end
56
+
43
57
  # Get current details for this workflow that may appear in UI/CLI. Unlike static details set at start, this value
44
58
  # can be updated throughout the life of the workflow. This can be in Temporal markdown format and can span multiple
45
59
  # lines. This is currently experimental.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: temporalio
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Temporal Technologies Inc
@@ -197,6 +197,8 @@ files:
197
197
  - lib/temporalio/internal/worker/workflow_instance/handler_hash.rb
198
198
  - lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb
199
199
  - lib/temporalio/internal/worker/workflow_instance/inbound_implementation.rb
200
+ - lib/temporalio/internal/worker/workflow_instance/nexus_client.rb
201
+ - lib/temporalio/internal/worker/workflow_instance/nexus_operation_handle.rb
200
202
  - lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb
201
203
  - lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb
202
204
  - lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb
@@ -239,6 +241,9 @@ files:
239
241
  - lib/temporalio/workflow/future.rb
240
242
  - lib/temporalio/workflow/handler_unfinished_policy.rb
241
243
  - lib/temporalio/workflow/info.rb
244
+ - lib/temporalio/workflow/nexus_client.rb
245
+ - lib/temporalio/workflow/nexus_operation_cancellation_type.rb
246
+ - lib/temporalio/workflow/nexus_operation_handle.rb
242
247
  - lib/temporalio/workflow/parent_close_policy.rb
243
248
  - lib/temporalio/workflow/update_info.rb
244
249
  - lib/temporalio/workflow_history.rb