temporalio 0.3.0-x86_64-linux → 0.4.0-x86_64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -0
  3. data/Rakefile +1 -1
  4. data/lib/temporalio/activity/context.rb +13 -0
  5. data/lib/temporalio/activity/definition.rb +22 -5
  6. data/lib/temporalio/activity/info.rb +3 -0
  7. data/lib/temporalio/api/batch/v1/message.rb +6 -1
  8. data/lib/temporalio/api/command/v1/message.rb +1 -1
  9. data/lib/temporalio/api/common/v1/message.rb +2 -1
  10. data/lib/temporalio/api/deployment/v1/message.rb +38 -0
  11. data/lib/temporalio/api/enums/v1/batch_operation.rb +1 -1
  12. data/lib/temporalio/api/enums/v1/common.rb +1 -1
  13. data/lib/temporalio/api/enums/v1/deployment.rb +23 -0
  14. data/lib/temporalio/api/enums/v1/event_type.rb +1 -1
  15. data/lib/temporalio/api/enums/v1/failed_cause.rb +1 -1
  16. data/lib/temporalio/api/enums/v1/nexus.rb +21 -0
  17. data/lib/temporalio/api/enums/v1/reset.rb +1 -1
  18. data/lib/temporalio/api/enums/v1/workflow.rb +2 -1
  19. data/lib/temporalio/api/errordetails/v1/message.rb +3 -1
  20. data/lib/temporalio/api/failure/v1/message.rb +3 -1
  21. data/lib/temporalio/api/history/v1/message.rb +3 -1
  22. data/lib/temporalio/api/nexus/v1/message.rb +2 -1
  23. data/lib/temporalio/api/payload_visitor.rb +75 -7
  24. data/lib/temporalio/api/query/v1/message.rb +2 -1
  25. data/lib/temporalio/api/taskqueue/v1/message.rb +4 -1
  26. data/lib/temporalio/api/workflow/v1/message.rb +9 -1
  27. data/lib/temporalio/api/workflowservice/v1/request_response.rb +40 -11
  28. data/lib/temporalio/api/workflowservice/v1/service.rb +1 -1
  29. data/lib/temporalio/api.rb +1 -0
  30. data/lib/temporalio/client/connection/workflow_service.rb +238 -28
  31. data/lib/temporalio/client/interceptor.rb +39 -0
  32. data/lib/temporalio/client/schedule.rb +25 -1
  33. data/lib/temporalio/client/with_start_workflow_operation.rb +115 -0
  34. data/lib/temporalio/client/workflow_execution.rb +19 -0
  35. data/lib/temporalio/client/workflow_handle.rb +3 -3
  36. data/lib/temporalio/client.rb +125 -2
  37. data/lib/temporalio/contrib/open_telemetry.rb +470 -0
  38. data/lib/temporalio/error.rb +1 -0
  39. data/lib/temporalio/internal/bridge/3.2/temporalio_bridge.so +0 -0
  40. data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.so +0 -0
  41. data/lib/temporalio/internal/bridge/3.4/temporalio_bridge.so +0 -0
  42. data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +1 -1
  43. data/lib/temporalio/internal/bridge/api/common/common.rb +2 -1
  44. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +1 -1
  45. data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +1 -1
  46. data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +2 -1
  47. data/lib/temporalio/internal/bridge/runtime.rb +3 -0
  48. data/lib/temporalio/internal/bridge/testing.rb +3 -0
  49. data/lib/temporalio/internal/client/implementation.rb +232 -10
  50. data/lib/temporalio/internal/proto_utils.rb +34 -2
  51. data/lib/temporalio/internal/worker/activity_worker.rb +20 -8
  52. data/lib/temporalio/internal/worker/multi_runner.rb +2 -2
  53. data/lib/temporalio/internal/worker/workflow_instance/context.rb +57 -3
  54. data/lib/temporalio/internal/worker/workflow_instance/details.rb +4 -2
  55. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +11 -26
  56. data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +22 -2
  57. data/lib/temporalio/internal/worker/workflow_instance.rb +76 -32
  58. data/lib/temporalio/internal/worker/workflow_worker.rb +62 -19
  59. data/lib/temporalio/runtime/metric_buffer.rb +94 -0
  60. data/lib/temporalio/runtime.rb +48 -10
  61. data/lib/temporalio/search_attributes.rb +13 -0
  62. data/lib/temporalio/testing/activity_environment.rb +42 -14
  63. data/lib/temporalio/testing/workflow_environment.rb +26 -3
  64. data/lib/temporalio/version.rb +1 -1
  65. data/lib/temporalio/worker/interceptor.rb +3 -0
  66. data/lib/temporalio/worker/thread_pool.rb +5 -5
  67. data/lib/temporalio/worker/tuner.rb +38 -0
  68. data/lib/temporalio/worker/workflow_executor/thread_pool.rb +13 -8
  69. data/lib/temporalio/worker/workflow_executor.rb +1 -1
  70. data/lib/temporalio/worker/workflow_replayer.rb +350 -0
  71. data/lib/temporalio/worker.rb +58 -52
  72. data/lib/temporalio/workflow/definition.rb +40 -8
  73. data/lib/temporalio/workflow/future.rb +2 -2
  74. data/lib/temporalio/workflow/info.rb +22 -0
  75. data/lib/temporalio/workflow.rb +60 -8
  76. data/lib/temporalio/workflow_history.rb +26 -1
  77. data/temporalio.gemspec +2 -1
  78. metadata +25 -4
@@ -6,6 +6,7 @@ require 'temporalio/client'
6
6
  require 'temporalio/error'
7
7
  require 'temporalio/internal/bridge'
8
8
  require 'temporalio/internal/bridge/worker'
9
+ require 'temporalio/internal/proto_utils'
9
10
  require 'temporalio/internal/worker/activity_worker'
10
11
  require 'temporalio/internal/worker/multi_runner'
11
12
  require 'temporalio/internal/worker/workflow_instance'
@@ -51,10 +52,13 @@ module Temporalio
51
52
  :illegal_workflow_calls,
52
53
  :workflow_failure_exception_types,
53
54
  :workflow_payload_codec_thread_pool,
55
+ :unsafe_workflow_io_enabled,
54
56
  :debug_mode
55
57
  )
56
58
 
57
59
  # Options as returned from {options} for `**to_h` splat use in {initialize}. See {initialize} for details.
60
+ #
61
+ # Note, the `client` within can be replaced via client setter.
58
62
  class Options; end # rubocop:disable Lint/EmptyClass
59
63
 
60
64
  # @return [String] Memoized default build ID. This default value is built as a checksum of all of the loaded Ruby
@@ -137,7 +141,8 @@ module Temporalio
137
141
  case event
138
142
  when Internal::Worker::MultiRunner::Event::PollSuccess
139
143
  # Successful poll
140
- event.worker._on_poll_bytes(runner, event.worker_type, event.bytes)
144
+ event.worker #: Worker
145
+ ._on_poll_bytes(runner, event.worker_type, event.bytes)
141
146
  when Internal::Worker::MultiRunner::Event::PollFailure
142
147
  # Poll failure, this causes shutdown of all workers
143
148
  logger.error('Poll failure (beginning worker shutdown if not already occurring)')
@@ -274,7 +279,7 @@ module Temporalio
274
279
  end
275
280
  end
276
281
 
277
- # @return [Options] Frozen options for this client which has the same attributes as {initialize}.
282
+ # @return [Options] Options for this worker which has the same attributes as {initialize}.
278
283
  attr_reader :options
279
284
 
280
285
  # Create a new worker. At least one activity or workflow must be present.
@@ -343,6 +348,9 @@ module Temporalio
343
348
  # @param workflow_payload_codec_thread_pool [ThreadPool, nil] Thread pool to run payload codec encode/decode within.
344
349
  # This is required if a payload codec exists and the worker is not fiber based. Codecs can potentially block
345
350
  # execution which is why they need to be run in the background.
351
+ # @param unsafe_workflow_io_enabled [Boolean] If false, the default, workflow code that invokes io_wait on the fiber
352
+ # scheduler will fail. Instead of setting this to true, users are encouraged to use {Workflow::Unsafe.io_enabled}
353
+ # with a block for narrower enabling of IO.
346
354
  # @param debug_mode [Boolean] If true, deadlock detection is disabled. Deadlock detection will fail workflow tasks
347
355
  # if they block the thread for too long. This defaults to true if the `TEMPORAL_DEBUG` environment variable is
348
356
  # `true` or `1`.
@@ -374,10 +382,13 @@ module Temporalio
374
382
  illegal_workflow_calls: Worker.default_illegal_workflow_calls,
375
383
  workflow_failure_exception_types: [],
376
384
  workflow_payload_codec_thread_pool: nil,
385
+ unsafe_workflow_io_enabled: false,
377
386
  debug_mode: %w[true 1].include?(ENV['TEMPORAL_DEBUG'].to_s.downcase)
378
387
  )
379
388
  raise ArgumentError, 'Must have at least one activity or workflow' if activities.empty? && workflows.empty?
380
389
 
390
+ Internal::ProtoUtils.assert_non_reserved_name(task_queue)
391
+
381
392
  @options = Options.new(
382
393
  client:,
383
394
  task_queue:,
@@ -406,24 +417,16 @@ module Temporalio
406
417
  illegal_workflow_calls:,
407
418
  workflow_failure_exception_types:,
408
419
  workflow_payload_codec_thread_pool:,
420
+ unsafe_workflow_io_enabled:,
409
421
  debug_mode:
410
422
  ).freeze
411
423
 
412
424
  # Preload workflow definitions and some workflow settings for the bridge
413
425
  workflow_definitions = Internal::Worker::WorkflowWorker.workflow_definitions(workflows)
414
- nondeterminism_as_workflow_fail = workflow_failure_exception_types.any? do |t|
415
- t.is_a?(Class) && t >= Workflow::NondeterminismError
416
- end
417
- nondeterminism_as_workflow_fail_for_types = workflow_definitions.values.map do |defn|
418
- next unless defn.failure_exception_types.any? { |t| t.is_a?(Class) && t >= Workflow::NondeterminismError }
419
-
420
- # If they tried to do this on a dynamic workflow and haven't already set worker-level option, warn
421
- unless defn.name || nondeterminism_as_workflow_fail
422
- warn('Note, dynamic workflows cannot trap non-determinism errors, so worker-level ' \
423
- 'workflow_failure_exception_types should be set to capture that if that is the intention')
424
- end
425
- defn.name
426
- end.compact
426
+ nondeterminism_as_workflow_fail, nondeterminism_as_workflow_fail_for_types =
427
+ Internal::Worker::WorkflowWorker.bridge_workflow_failure_exception_type_options(
428
+ workflow_failure_exception_types:, workflow_definitions:
429
+ )
427
430
 
428
431
  # Create the bridge worker
429
432
  @bridge_worker = Internal::Bridge::Worker.new(
@@ -433,11 +436,7 @@ module Temporalio
433
436
  workflow: !workflows.empty?,
434
437
  namespace: client.namespace,
435
438
  task_queue:,
436
- tuner: Internal::Bridge::Worker::TunerOptions.new(
437
- workflow_slot_supplier: to_bridge_slot_supplier_options(tuner.workflow_slot_supplier),
438
- activity_slot_supplier: to_bridge_slot_supplier_options(tuner.activity_slot_supplier),
439
- local_activity_slot_supplier: to_bridge_slot_supplier_options(tuner.local_activity_slot_supplier)
440
- ),
439
+ tuner: tuner._to_bridge_options,
441
440
  build_id:,
442
441
  identity_override: identity,
443
442
  max_cached_workflows:,
@@ -476,13 +475,30 @@ module Temporalio
476
475
  bridge_worker: @bridge_worker)
477
476
  end
478
477
  unless workflows.empty?
479
- @workflow_worker = Internal::Worker::WorkflowWorker.new(worker: self,
480
- bridge_worker: @bridge_worker,
481
- workflow_definitions:)
478
+ @workflow_worker = Internal::Worker::WorkflowWorker.new(
479
+ bridge_worker: @bridge_worker,
480
+ namespace: client.namespace,
481
+ task_queue:,
482
+ workflow_definitions:,
483
+ workflow_executor:,
484
+ logger:,
485
+ data_converter: client.data_converter,
486
+ metric_meter: client.connection.options.runtime.metric_meter,
487
+ workflow_interceptors: @workflow_interceptors,
488
+ disable_eager_activity_execution:,
489
+ illegal_workflow_calls:,
490
+ workflow_failure_exception_types:,
491
+ workflow_payload_codec_thread_pool:,
492
+ unsafe_workflow_io_enabled:,
493
+ debug_mode:
494
+ )
482
495
  end
483
496
 
484
497
  # Validate worker
485
498
  @bridge_worker.validate
499
+
500
+ # Mutex needed for accessing and replacing a client
501
+ @client_mutex = Mutex.new
486
502
  end
487
503
 
488
504
  # @return [String] Task queue set on the worker options.
@@ -490,6 +506,25 @@ module Temporalio
490
506
  @options.task_queue
491
507
  end
492
508
 
509
+ # @return [Client] Client for this worker. This is the same as {Options.client} in {options}, but surrounded by a
510
+ # mutex to be safe for client replacement in {client=}.
511
+ def client
512
+ @client_mutex.synchronize { @options.client }
513
+ end
514
+
515
+ # Replace the worker's client. When this is called, the client is replaced on the internal worker which means any
516
+ # new calls will be made on the new client (but existing calls will still complete on the previous one). This is
517
+ # commonly used for providing a new client with updated authentication credentials.
518
+ #
519
+ # @param new_client [Client] New client to use for new calls.
520
+ def client=(new_client)
521
+ @client_mutex.synchronize do
522
+ @bridge_worker.replace_client(new_client.connection._core_client)
523
+ @options = @options.with(client: new_client)
524
+ new_client
525
+ end
526
+ end
527
+
493
528
  # Run this worker until cancellation or optional block completes. When the cancellation or block is complete, the
494
529
  # worker is shut down. This will return the block result if everything successful or raise an error if not.
495
530
  #
@@ -543,11 +578,6 @@ module Temporalio
543
578
  @activity_interceptors
544
579
  end
545
580
 
546
- # @!visibility private
547
- def _workflow_interceptors
548
- @workflow_interceptors
549
- end
550
-
551
581
  # @!visibility private
552
582
  def _on_poll_bytes(runner, worker_type, bytes)
553
583
  case worker_type
@@ -569,29 +599,5 @@ module Temporalio
569
599
  @workflow_worker&.on_shutdown_complete
570
600
  @workflow_worker = nil
571
601
  end
572
-
573
- private
574
-
575
- def to_bridge_slot_supplier_options(slot_supplier)
576
- if slot_supplier.is_a?(Tuner::SlotSupplier::Fixed)
577
- Internal::Bridge::Worker::TunerSlotSupplierOptions.new(
578
- fixed_size: slot_supplier.slots,
579
- resource_based: nil
580
- )
581
- elsif slot_supplier.is_a?(Tuner::SlotSupplier::ResourceBased)
582
- Internal::Bridge::Worker::TunerSlotSupplierOptions.new(
583
- fixed_size: nil,
584
- resource_based: Internal::Bridge::Worker::TunerResourceBasedSlotSupplierOptions.new(
585
- target_mem_usage: slot_supplier.tuner_options.target_memory_usage,
586
- target_cpu_usage: slot_supplier.tuner_options.target_cpu_usage,
587
- min_slots: slot_supplier.slot_options.min_slots,
588
- max_slots: slot_supplier.slot_options.max_slots,
589
- ramp_throttle: slot_supplier.slot_options.ramp_throttle
590
- )
591
- )
592
- else
593
- raise ArgumentError, 'Tuner slot suppliers must be instances of Fixed or ResourceBased'
594
- end
595
- end
596
602
  end
597
603
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'temporalio/internal/proto_utils'
3
4
  require 'temporalio/workflow'
4
5
  require 'temporalio/workflow/handler_unfinished_policy'
5
6
 
@@ -71,7 +72,9 @@ module Temporalio
71
72
  # `attr_accessor`. If a writer is needed alongside this, use `attr_writer`.
72
73
  #
73
74
  # @param attr_names [Array<Symbol>] Attributes to expose.
74
- def workflow_query_attr_reader(*attr_names)
75
+ # @param description [String, nil] Description that may appear in CLI/UI, applied to each query handler
76
+ # implicitly created. This is currently experimental.
77
+ def workflow_query_attr_reader(*attr_names, description: nil)
75
78
  @workflow_queries ||= {}
76
79
  attr_names.each do |attr_name|
77
80
  raise 'Expected attr to be a symbol' unless attr_name.is_a?(Symbol)
@@ -83,7 +86,7 @@ module Temporalio
83
86
  end
84
87
 
85
88
  # Just run this as if done manually
86
- workflow_query
89
+ workflow_query(description:)
87
90
  define_method(attr_name) { instance_variable_get("@#{attr_name}") }
88
91
  end
89
92
  end
@@ -100,6 +103,8 @@ module Temporalio
100
103
  # values.
101
104
  #
102
105
  # @param name [String, Symbol, nil] Override the default name.
106
+ # @param description [String, nil] Description for this handler that may appear in CLI/UI. This is currently
107
+ # experimental.
103
108
  # @param dynamic [Boolean] If true, make the signal dynamic. This means it receives all other signals without
104
109
  # handlers. This cannot have a name override since it is nameless. The first parameter will be the name. Often
105
110
  # it is useful to have the second parameter be `*args` and `raw_args` be true.
@@ -109,19 +114,22 @@ module Temporalio
109
114
  # when the workflow ends. The default warns, but this can be disabled.
110
115
  def workflow_signal(
111
116
  name: nil,
117
+ description: nil,
112
118
  dynamic: false,
113
119
  raw_args: false,
114
120
  unfinished_policy: HandlerUnfinishedPolicy::WARN_AND_ABANDON
115
121
  )
116
122
  raise 'Cannot provide name if dynamic is true' if name && dynamic
117
123
 
118
- self.pending_handler_details = { type: :signal, name:, dynamic:, raw_args:, unfinished_policy: }
124
+ self.pending_handler_details = { type: :signal, name:, description:, dynamic:, raw_args:, unfinished_policy: }
119
125
  end
120
126
 
121
127
  # Mark the next method as a workflow query with a default name as the name of the method. Queries can not have
122
128
  # any side effects, meaning they should never mutate state or try to wait on anything.
123
129
  #
124
130
  # @param name [String, Symbol, nil] Override the default name.
131
+ # @param description [String, nil] Description for this handler that may appear in CLI/UI. This is currently
132
+ # experimental.
125
133
  # @param dynamic [Boolean] If true, make the query dynamic. This means it receives all other queries without
126
134
  # handlers. This cannot have a name override since it is nameless. The first parameter will be the name. Often
127
135
  # it is useful to have the second parameter be `*args` and `raw_args` be true.
@@ -129,18 +137,21 @@ module Temporalio
129
137
  # {Converters::RawValue} which is a raw payload wrapper, convertible with {Workflow.payload_converter}.
130
138
  def workflow_query(
131
139
  name: nil,
140
+ description: nil,
132
141
  dynamic: false,
133
142
  raw_args: false
134
143
  )
135
144
  raise 'Cannot provide name if dynamic is true' if name && dynamic
136
145
 
137
- self.pending_handler_details = { type: :query, name:, dynamic:, raw_args: }
146
+ self.pending_handler_details = { type: :query, name:, description:, dynamic:, raw_args: }
138
147
  end
139
148
 
140
149
  # Mark the next method as a workflow update with a default name as the name of the method. Updates can return
141
150
  # values. Separate validation methods can be provided via {workflow_update_validator}.
142
151
  #
143
152
  # @param name [String, Symbol, nil] Override the default name.
153
+ # @param description [String, nil] Description for this handler that may appear in CLI/UI. This is currently
154
+ # experimental.
144
155
  # @param dynamic [Boolean] If true, make the update dynamic. This means it receives all other updates without
145
156
  # handlers. This cannot have a name override since it is nameless. The first parameter will be the name. Often
146
157
  # it is useful to have the second parameter be `*args` and `raw_args` be true.
@@ -150,13 +161,14 @@ module Temporalio
150
161
  # when the workflow ends. The default warns, but this can be disabled.
151
162
  def workflow_update(
152
163
  name: nil,
164
+ description: nil,
153
165
  dynamic: false,
154
166
  raw_args: false,
155
167
  unfinished_policy: HandlerUnfinishedPolicy::WARN_AND_ABANDON
156
168
  )
157
169
  raise 'Cannot provide name if dynamic is true' if name && dynamic
158
170
 
159
- self.pending_handler_details = { type: :update, name:, dynamic:, raw_args:, unfinished_policy: }
171
+ self.pending_handler_details = { type: :update, name:, description:, dynamic:, raw_args:, unfinished_policy: }
160
172
  end
161
173
 
162
174
  # Mark the next method as a workflow update validator to the given update method. The validator is expected to
@@ -225,6 +237,7 @@ module Temporalio
225
237
  [Signal.new(
226
238
  name: handler[:dynamic] ? nil : (handler[:name] || method_name).to_s,
227
239
  to_invoke: method_name,
240
+ description: handler[:description],
228
241
  raw_args: handler[:raw_args],
229
242
  unfinished_policy: handler[:unfinished_policy]
230
243
  ), @workflow_signals, [@workflow_queries, @workflow_updates]]
@@ -232,12 +245,14 @@ module Temporalio
232
245
  [Query.new(
233
246
  name: handler[:dynamic] ? nil : (handler[:name] || method_name).to_s,
234
247
  to_invoke: method_name,
248
+ description: handler[:description],
235
249
  raw_args: handler[:raw_args]
236
250
  ), @workflow_queries, [@workflow_signals, @workflow_updates]]
237
251
  when :update
238
252
  [Update.new(
239
253
  name: handler[:dynamic] ? nil : (handler[:name] || method_name).to_s,
240
254
  to_invoke: method_name,
255
+ description: handler[:description],
241
256
  raw_args: handler[:raw_args],
242
257
  unfinished_policy: handler[:unfinished_policy]
243
258
  ), @workflow_updates, [@workflow_signals, @workflow_queries]]
@@ -430,6 +445,7 @@ module Temporalio
430
445
  @signals = signals.dup.freeze
431
446
  @queries = queries.dup.freeze
432
447
  @updates = updates.dup.freeze
448
+ Internal::ProtoUtils.assert_non_reserved_name(name)
433
449
  end
434
450
 
435
451
  # @return [String] Workflow name.
@@ -441,7 +457,7 @@ module Temporalio
441
457
  # A signal definition. This is usually built as a result of a {Definition.workflow_signal} method, but can be
442
458
  # manually created to set at runtime on {Workflow.signal_handlers}.
443
459
  class Signal
444
- attr_reader :name, :to_invoke, :raw_args, :unfinished_policy
460
+ attr_reader :name, :to_invoke, :description, :raw_args, :unfinished_policy
445
461
 
446
462
  # @!visibility private
447
463
  def self._name_from_parameter(signal)
@@ -460,26 +476,31 @@ module Temporalio
460
476
  #
461
477
  # @param name [String, nil] Name or nil if dynamic.
462
478
  # @param to_invoke [Symbol, Proc] Method name or proc to invoke.
479
+ # @param description [String, nil] Description for this handler that may appear in CLI/UI. This is currently
480
+ # experimental.
463
481
  # @param raw_args [Boolean] Whether the parameters should be raw values.
464
482
  # @param unfinished_policy [HandlerUnfinishedPolicy] How the workflow reacts when this handler is still running
465
483
  # on workflow completion.
466
484
  def initialize(
467
485
  name:,
468
486
  to_invoke:,
487
+ description: nil,
469
488
  raw_args: false,
470
489
  unfinished_policy: HandlerUnfinishedPolicy::WARN_AND_ABANDON
471
490
  )
472
491
  @name = name
473
492
  @to_invoke = to_invoke
493
+ @description = description
474
494
  @raw_args = raw_args
475
495
  @unfinished_policy = unfinished_policy
496
+ Internal::ProtoUtils.assert_non_reserved_name(name)
476
497
  end
477
498
  end
478
499
 
479
500
  # A query definition. This is usually built as a result of a {Definition.workflow_query} method, but can be
480
501
  # manually created to set at runtime on {Workflow.query_handlers}.
481
502
  class Query
482
- attr_reader :name, :to_invoke, :raw_args
503
+ attr_reader :name, :to_invoke, :description, :raw_args
483
504
 
484
505
  # @!visibility private
485
506
  def self._name_from_parameter(query)
@@ -498,22 +519,27 @@ module Temporalio
498
519
  #
499
520
  # @param name [String, nil] Name or nil if dynamic.
500
521
  # @param to_invoke [Symbol, Proc] Method name or proc to invoke.
522
+ # @param description [String, nil] Description for this handler that may appear in CLI/UI. This is currently
523
+ # experimental.
501
524
  # @param raw_args [Boolean] Whether the parameters should be raw values.
502
525
  def initialize(
503
526
  name:,
504
527
  to_invoke:,
528
+ description: nil,
505
529
  raw_args: false
506
530
  )
507
531
  @name = name
508
532
  @to_invoke = to_invoke
533
+ @description = description
509
534
  @raw_args = raw_args
535
+ Internal::ProtoUtils.assert_non_reserved_name(name)
510
536
  end
511
537
  end
512
538
 
513
539
  # An update definition. This is usually built as a result of a {Definition.workflow_update} method, but can be
514
540
  # manually created to set at runtime on {Workflow.update_handlers}.
515
541
  class Update
516
- attr_reader :name, :to_invoke, :raw_args, :unfinished_policy, :validator_to_invoke
542
+ attr_reader :name, :to_invoke, :description, :raw_args, :unfinished_policy, :validator_to_invoke
517
543
 
518
544
  # @!visibility private
519
545
  def self._name_from_parameter(update)
@@ -532,6 +558,8 @@ module Temporalio
532
558
  #
533
559
  # @param name [String, nil] Name or nil if dynamic.
534
560
  # @param to_invoke [Symbol, Proc] Method name or proc to invoke.
561
+ # @param description [String, nil] Description for this handler that may appear in CLI/UI. This is currently
562
+ # experimental.
535
563
  # @param raw_args [Boolean] Whether the parameters should be raw values.
536
564
  # @param unfinished_policy [HandlerUnfinishedPolicy] How the workflow reacts when this handler is still running
537
565
  # on workflow completion.
@@ -539,15 +567,18 @@ module Temporalio
539
567
  def initialize(
540
568
  name:,
541
569
  to_invoke:,
570
+ description: nil,
542
571
  raw_args: false,
543
572
  unfinished_policy: HandlerUnfinishedPolicy::WARN_AND_ABANDON,
544
573
  validator_to_invoke: nil
545
574
  )
546
575
  @name = name
547
576
  @to_invoke = to_invoke
577
+ @description = description
548
578
  @raw_args = raw_args
549
579
  @unfinished_policy = unfinished_policy
550
580
  @validator_to_invoke = validator_to_invoke
581
+ Internal::ProtoUtils.assert_non_reserved_name(name)
551
582
  end
552
583
 
553
584
  # @!visibility private
@@ -555,6 +586,7 @@ module Temporalio
555
586
  Update.new(
556
587
  name:,
557
588
  to_invoke:,
589
+ description:,
558
590
  raw_args:,
559
591
  unfinished_policy:,
560
592
  validator_to_invoke:
@@ -8,7 +8,7 @@ module Temporalio
8
8
  # workflows.
9
9
  class Future
10
10
  # Return a future that completes when any of the given futures complete. The returned future will return the first
11
- # completed futures value or raise the first completed futures exception. To not raise the exception, see
11
+ # completed future's value or raise the first completed future's exception. To not raise the exception, see
12
12
  # {try_any_of}.
13
13
  #
14
14
  # @param futures [Array<Future<Object>>] Futures to wait for the first to complete.
@@ -16,7 +16,7 @@ module Temporalio
16
16
  def self.any_of(*futures)
17
17
  Future.new do
18
18
  Workflow.wait_condition(cancellation: nil) { futures.any?(&:done?) }
19
- # We know a future is always returned from find, the & just helps type checker
19
+ # We know a future is always returned from find, the || just helps type checker
20
20
  (futures.find(&:done?) || raise).wait
21
21
  end
22
22
  end
@@ -7,11 +7,13 @@ module Temporalio
7
7
  :continued_run_id,
8
8
  :cron_schedule,
9
9
  :execution_timeout,
10
+ :headers,
10
11
  :last_failure,
11
12
  :last_result,
12
13
  :namespace,
13
14
  :parent,
14
15
  :retry_policy,
16
+ :root,
15
17
  :run_id,
16
18
  :run_timeout,
17
19
  :start_time,
@@ -32,6 +34,8 @@ module Temporalio
32
34
  # @return [String, nil] Cron schedule if applicable.
33
35
  # @!attribute execution_timeout
34
36
  # @return [Float, nil] Execution timeout for the workflow.
37
+ # @!attribute headers
38
+ # @return [Hash<String, Api::Common::V1::Payload>] Headers.
35
39
  # @!attribute last_failure
36
40
  # @return [Exception, nil] Failure if this workflow run is a continuation of a failure.
37
41
  # @!attribute last_result
@@ -42,6 +46,9 @@ module Temporalio
42
46
  # @return [ParentInfo, nil] Parent information for the workflow if this is a child.
43
47
  # @!attribute retry_policy
44
48
  # @return [RetryPolicy, nil] Retry policy for the workflow.
49
+ # @!attribute root
50
+ # @return [RootInfo, nil] Root information for the workflow. This is nil in pre-1.27.0 server versions or if there
51
+ # is no root (i.e. the root is itself).
45
52
  # @!attribute run_id
46
53
  # @return [String] Run ID for the workflow.
47
54
  # @!attribute run_timeout
@@ -77,6 +84,21 @@ module Temporalio
77
84
  :workflow_id,
78
85
  keyword_init: true
79
86
  )
87
+
88
+ # Information about a root of a workflow.
89
+ #
90
+ # @!attribute run_id
91
+ # @return [String] Run ID for the root.
92
+ # @!attribute workflow_id
93
+ # @return [String] Workflow ID for the root.
94
+ #
95
+ # @note WARNING: This class may have required parameters added to its constructor. Users should not instantiate
96
+ # this class or it may break in incompatible ways.
97
+ RootInfo = Struct.new(
98
+ :run_id,
99
+ :workflow_id,
100
+ keyword_init: true
101
+ )
80
102
  end
81
103
  end
82
104
  end
@@ -38,6 +38,24 @@ module Temporalio
38
38
  _current.continue_as_new_suggested
39
39
  end
40
40
 
41
+ # Get current details for this workflow that may appear in UI/CLI. Unlike static details set at start, this value
42
+ # can be updated throughout the life of the workflow. This can be in Temporal markdown format and can span multiple
43
+ # lines. This is currently experimental.
44
+ #
45
+ # @return [String] Current details. Default is empty string.
46
+ def self.current_details
47
+ _current.current_details
48
+ end
49
+
50
+ # Set current details for this workflow that may appear in UI/CLI. Unlike static details set at start, this value
51
+ # can be updated throughout the life of the workflow. This can be in Temporal markdown format and can span multiple
52
+ # lines. This is currently experimental.
53
+ #
54
+ # @param details [String] Current details. Can use empty string to unset.
55
+ def self.current_details=(details)
56
+ _current.current_details = details
57
+ end
58
+
41
59
  # @return [Integer] Current number of events in history. This value is the current history event count up until the
42
60
  # current task. Note, this value may not be up to date when accessed in a query.
43
61
  def self.current_history_length
@@ -77,6 +95,8 @@ module Temporalio
77
95
  # @param activity [Class<Activity::Definition>, Symbol, String] Activity definition class or activity name.
78
96
  # @param args [Array<Object>] Arguments to the activity.
79
97
  # @param task_queue [String] Task queue to run the activity on. Defaults to the current workflow's task queue.
98
+ # @param summary [String, nil] Single-line summary for this activity that may appear in CLI/UI. This can be in
99
+ # single-line Temporal markdown format. This is currently experimental.
80
100
  # @param schedule_to_close_timeout [Float, nil] Max amount of time the activity can take from first being scheduled
81
101
  # to being completed before it times out. This is inclusive of all retries.
82
102
  # @param schedule_to_start_timeout [Float, nil] Max amount of time the activity can take to be started from first
@@ -107,6 +127,7 @@ module Temporalio
107
127
  activity,
108
128
  *args,
109
129
  task_queue: info.task_queue,
130
+ summary: nil,
110
131
  schedule_to_close_timeout: nil,
111
132
  schedule_to_start_timeout: nil,
112
133
  start_to_close_timeout: nil,
@@ -119,7 +140,7 @@ module Temporalio
119
140
  )
120
141
  _current.execute_activity(
121
142
  activity, *args,
122
- task_queue:, schedule_to_close_timeout:, schedule_to_start_timeout:, start_to_close_timeout:,
143
+ task_queue:, summary:, schedule_to_close_timeout:, schedule_to_start_timeout:, start_to_close_timeout:,
123
144
  heartbeat_timeout:, retry_policy:, cancellation:, cancellation_type:, activity_id:, disable_eager_execution:
124
145
  )
125
146
  end
@@ -130,6 +151,8 @@ module Temporalio
130
151
  *args,
131
152
  id: random.uuid,
132
153
  task_queue: info.task_queue,
154
+ static_summary: nil,
155
+ static_details: nil,
133
156
  cancellation: Workflow.cancellation,
134
157
  cancellation_type: ChildWorkflowCancellationType::WAIT_CANCELLATION_COMPLETED,
135
158
  parent_close_policy: ParentClosePolicy::TERMINATE,
@@ -144,8 +167,9 @@ module Temporalio
144
167
  )
145
168
  start_child_workflow(
146
169
  workflow, *args,
147
- id:, task_queue:, cancellation:, cancellation_type:, parent_close_policy:, execution_timeout:, run_timeout:,
148
- task_timeout:, id_reuse_policy:, retry_policy:, cron_schedule:, memo:, search_attributes:
170
+ id:, task_queue:, static_summary:, static_details:, cancellation:, cancellation_type:,
171
+ parent_close_policy:, execution_timeout:, run_timeout:, task_timeout:, id_reuse_policy:,
172
+ retry_policy:, cron_schedule:, memo:, search_attributes:
149
173
  ).result
150
174
  end
151
175
 
@@ -220,6 +244,12 @@ module Temporalio
220
244
  _current.info
221
245
  end
222
246
 
247
+ # @return [Definition, nil] Workflow class instance. This should always be present except in
248
+ # {Worker::Interceptor::Workflow::Inbound.init} where it will be nil.
249
+ def self.instance
250
+ _current.instance
251
+ end
252
+
223
253
  # @return [Logger] Logger for the workflow. This is a scoped logger that automatically appends workflow details to
224
254
  # every log and takes care not to log during replay.
225
255
  def self.logger
@@ -298,7 +328,7 @@ module Temporalio
298
328
  # value cannot be negative. Since Temporal timers are server-side, timer resolution may not end up as precise as
299
329
  # system timers.
300
330
  # @param summary [String, nil] A simple string identifying this timer that may be visible in UI/CLI. While it can be
301
- # normal text, it is best to treat as a timer ID.
331
+ # normal text, it is best to treat as a timer ID. This is currently experimental.
302
332
  # @param cancellation [Cancellation] Cancellation for this timer.
303
333
  # @raise [Error::CanceledError] Sleep canceled.
304
334
  def self.sleep(duration, summary: nil, cancellation: Workflow.cancellation)
@@ -311,6 +341,12 @@ module Temporalio
311
341
  # @param args [Array<Object>] Arguments to the workflow.
312
342
  # @param id [String] Unique identifier for the workflow execution. Defaults to a new UUID from {random}.
313
343
  # @param task_queue [String] Task queue to run the workflow on. Defaults to the current workflow's task queue.
344
+ # @param static_summary [String, nil] Fixed single-line summary for this workflow execution that may appear in
345
+ # CLI/UI. This can be in single-line Temporal markdown format. This is currently experimental.
346
+ # @param static_details [String, nil] Fixed details for this workflow execution that may appear in CLI/UI. This can
347
+ # be in Temporal markdown format and can be multiple lines. This is a fixed value on the workflow that cannot be
348
+ # updated. For details that can be updated, use {Workflow.current_details=} within the workflow. This is currently
349
+ # experimental.
314
350
  # @param cancellation [Cancellation] Cancellation to apply to the child workflow. How cancellation is treated is
315
351
  # based on `cancellation_type`. This defaults to the workflow's cancellation.
316
352
  # @param cancellation_type [ChildWorkflowCancellationType] How the child workflow will react to cancellation.
@@ -333,6 +369,8 @@ module Temporalio
333
369
  *args,
334
370
  id: random.uuid,
335
371
  task_queue: info.task_queue,
372
+ static_summary: nil,
373
+ static_details: nil,
336
374
  cancellation: Workflow.cancellation,
337
375
  cancellation_type: ChildWorkflowCancellationType::WAIT_CANCELLATION_COMPLETED,
338
376
  parent_close_policy: ParentClosePolicy::TERMINATE,
@@ -347,11 +385,18 @@ module Temporalio
347
385
  )
348
386
  _current.start_child_workflow(
349
387
  workflow, *args,
350
- id:, task_queue:, cancellation:, cancellation_type:, parent_close_policy:, execution_timeout:, run_timeout:,
351
- task_timeout:, id_reuse_policy:, retry_policy:, cron_schedule:, memo:, search_attributes:
388
+ id:, task_queue:, static_summary:, static_details:, cancellation:, cancellation_type:,
389
+ parent_close_policy:, execution_timeout:, run_timeout:, task_timeout:, id_reuse_policy:,
390
+ retry_policy:, cron_schedule:, memo:, search_attributes:
352
391
  )
353
392
  end
354
393
 
394
+ # @return [Hash<Object, Object>] General in-workflow storage. Most users will store state on the workflow class
395
+ # instance instead, this is only for utilities without access to the class instance.
396
+ def self.storage
397
+ _current.storage
398
+ end
399
+
355
400
  # Run the block until the timeout is reached. This is backed by {sleep}. This does not accept cancellation because
356
401
  # it is expected the block within will properly handle/bubble cancellation.
357
402
  #
@@ -361,8 +406,8 @@ module Temporalio
361
406
  # exception.
362
407
  # @param message [String] Message to use for timeout exception. Defaults to "execution expired" like
363
408
  # {::Timeout.timeout}.
364
- # @param summary [String] Timer summer for the timer created by this timeout. This is backed by {sleep} so see that
365
- # method for details.
409
+ # @param summary [String] Timer summary for the timer created by this timeout. This is backed by {sleep} so see that
410
+ # method for details. This is currently experimental.
366
411
  #
367
412
  # @yield Block to run with a timeout.
368
413
  # @return [Object] The result of the block.
@@ -458,6 +503,13 @@ module Temporalio
458
503
  def self.illegal_call_tracing_disabled(&)
459
504
  Workflow._current.illegal_call_tracing_disabled(&)
460
505
  end
506
+
507
+ # Run a block of code with IO enabled. Specifically this allows the `io_wait` call of the fiber scheduler to work.
508
+ # Users should be cautious about using this as it can often signify unsafe code. Note, this is often only
509
+ # applicable to network code as file IO and most process-based IO does not go through scheduler `io_wait`.
510
+ def self.io_enabled(&)
511
+ Workflow._current.io_enabled(&)
512
+ end
461
513
  end
462
514
 
463
515
  # Error that is raised by a workflow out of the primary workflow method to issue a continue-as-new.