temporalio 1.10.1 → 1.11.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.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +760 -767
  3. data/Gemfile +3 -2
  4. data/ext/Cargo.toml +3 -3
  5. data/lib/temporalio/api/activity/v1/message.rb +1 -1
  6. data/lib/temporalio/api/common/v1/message.rb +2 -1
  7. data/lib/temporalio/api/schedule/v1/message.rb +1 -1
  8. data/lib/temporalio/api/taskqueue/v1/message.rb +2 -1
  9. data/lib/temporalio/api/worker/v1/message.rb +1 -1
  10. data/lib/temporalio/api/workflowservice/v1/request_response.rb +1 -1
  11. data/lib/temporalio/client/connection.rb +4 -4
  12. data/lib/temporalio/contrib/aws/s3_driver/aws_sdk_s3_storage_driver_client.rb +52 -0
  13. data/lib/temporalio/contrib/aws/s3_driver/s3_storage_driver.rb +211 -0
  14. data/lib/temporalio/contrib/aws/s3_driver/s3_storage_driver_client.rb +56 -0
  15. data/lib/temporalio/contrib/aws/s3_driver.rb +22 -0
  16. data/lib/temporalio/contrib/open_telemetry.rb +1 -1
  17. data/lib/temporalio/converters/external_storage_concurrency.rb +4 -6
  18. data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +1 -1
  19. data/lib/temporalio/converters/payload_converter/json_protobuf.rb +1 -1
  20. data/lib/temporalio/converters/storage_driver_limiter.rb +6 -8
  21. data/lib/temporalio/converters/storage_driver_retrieve_context.rb +1 -2
  22. data/lib/temporalio/converters/storage_driver_store_context.rb +1 -2
  23. data/lib/temporalio/env_config.rb +89 -8
  24. data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +2 -2
  25. data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +3 -1
  26. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +2 -1
  27. data/lib/temporalio/internal/bridge/testing.rb +4 -2
  28. data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +2 -2
  29. data/lib/temporalio/internal/worker/workflow_instance/context.rb +35 -24
  30. data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +6 -4
  31. data/lib/temporalio/internal/worker/workflow_instance/nexus_client.rb +3 -2
  32. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +52 -19
  33. data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +8 -2
  34. data/lib/temporalio/internal/worker/workflow_instance.rb +47 -26
  35. data/lib/temporalio/internal/worker/workflow_worker.rb +1 -2
  36. data/lib/temporalio/search_attributes.rb +4 -2
  37. data/lib/temporalio/testing/workflow_environment.rb +10 -2
  38. data/lib/temporalio/version.rb +1 -1
  39. data/lib/temporalio/worker/interceptor.rb +16 -8
  40. data/lib/temporalio/worker/workflow_replayer.rb +1 -1
  41. data/lib/temporalio/worker.rb +19 -12
  42. data/lib/temporalio/workflow/child_workflow_handle.rb +4 -1
  43. data/lib/temporalio/workflow/event_group.rb +157 -0
  44. data/lib/temporalio/workflow/external_workflow_handle.rb +9 -2
  45. data/lib/temporalio/workflow/future.rb +1 -1
  46. data/lib/temporalio/workflow/info.rb +4 -0
  47. data/lib/temporalio/workflow/nexus_client.rb +11 -3
  48. data/lib/temporalio/workflow.rb +114 -22
  49. data/rbi/temporalio/api/activity/v1/message.rbi +71 -2
  50. data/rbi/temporalio/api/common/v1/message.rbi +95 -0
  51. data/rbi/temporalio/api/schedule/v1/message.rbi +44 -4
  52. data/rbi/temporalio/api/taskqueue/v1/message.rbi +67 -0
  53. data/rbi/temporalio/api/worker/v1/message.rbi +22 -2
  54. data/rbi/temporalio/api/workflowservice/v1/request_response.rbi +126 -12
  55. data/rbi/temporalio/contrib/aws/s3_driver/aws_sdk_s3_storage_driver_client.rbi +8 -0
  56. data/rbi/temporalio/contrib/aws/s3_driver/s3_storage_driver.rbi +27 -0
  57. data/rbi/temporalio/contrib/aws/s3_driver/s3_storage_driver_client.rbi +17 -0
  58. data/rbi/temporalio/contrib/aws/s3_driver.rbi +5 -0
  59. data/rbi/temporalio/env_config.rbi +39 -4
  60. data/rbi/temporalio/internal/bridge/api/activity_result/activity_result.rbi +0 -19
  61. data/rbi/temporalio/internal/bridge/api/activity_task/activity_task.rbi +213 -2
  62. data/rbi/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rbi +50 -4
  63. data/rbi/temporalio/internal/bridge/testing.rbi +17 -4
  64. data/rbi/temporalio/internal/bridge/worker.rbi +4 -4
  65. data/rbi/temporalio/internal/worker/workflow_instance/context.rbi +33 -24
  66. data/rbi/temporalio/internal/worker/workflow_instance/nexus_client.rbi +4 -2
  67. data/rbi/temporalio/internal/worker/workflow_instance/outbound_implementation.rbi +17 -6
  68. data/rbi/temporalio/internal/worker/workflow_instance/scheduler.rbi +1 -1
  69. data/rbi/temporalio/internal/worker/workflow_instance.rbi +5 -2
  70. data/rbi/temporalio/testing/workflow_environment.rbi +4 -0
  71. data/rbi/temporalio/worker/interceptor.rbi +24 -0
  72. data/rbi/temporalio/worker.rbi +8 -8
  73. data/rbi/temporalio/workflow/child_workflow_handle.rbi +3 -2
  74. data/rbi/temporalio/workflow/event_group.rbi +104 -0
  75. data/rbi/temporalio/workflow/external_workflow_handle.rbi +5 -4
  76. data/rbi/temporalio/workflow/info.rbi +3 -0
  77. data/rbi/temporalio/workflow/nexus_client.rbi +8 -4
  78. data/rbi/temporalio/workflow.rbi +70 -21
  79. data/sig/temporalio/api/activity/v1/message.rbs +31 -0
  80. data/sig/temporalio/api/common/v1/message.rbs +35 -0
  81. data/sig/temporalio/api/schedule/v1/message.rbs +20 -0
  82. data/sig/temporalio/api/taskqueue/v1/message.rbs +24 -0
  83. data/sig/temporalio/api/worker/v1/message.rbs +10 -0
  84. data/sig/temporalio/api/workflowservice/v1/request_response.rbs +58 -0
  85. data/sig/temporalio/contrib/aws/s3_driver/aws_sdk_s3_storage_driver_client.rbs +13 -0
  86. data/sig/temporalio/contrib/aws/s3_driver/s3_storage_driver.rbs +68 -0
  87. data/sig/temporalio/contrib/aws/s3_driver/s3_storage_driver_client.rbs +14 -0
  88. data/sig/temporalio/contrib/aws/s3_driver.rbs +8 -0
  89. data/sig/temporalio/env_config.rbs +20 -2
  90. data/sig/temporalio/internal/bridge/api/activity_result/activity_result.rbs +6 -41
  91. data/sig/temporalio/internal/bridge/api/activity_task/activity_task.rbs +99 -0
  92. data/sig/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rbs +22 -0
  93. data/sig/temporalio/internal/bridge/testing.rbs +7 -3
  94. data/sig/temporalio/internal/bridge/worker.rbs +4 -4
  95. data/sig/temporalio/internal/worker/workflow_instance/context.rbs +19 -13
  96. data/sig/temporalio/internal/worker/workflow_instance/nexus_client.rbs +2 -1
  97. data/sig/temporalio/internal/worker/workflow_instance/outbound_implementation.rbs +8 -5
  98. data/sig/temporalio/internal/worker/workflow_instance/scheduler.rbs +1 -1
  99. data/sig/temporalio/internal/worker/workflow_instance.rbs +2 -1
  100. data/sig/temporalio/testing/workflow_environment.rbs +8 -4
  101. data/sig/temporalio/worker/interceptor.rbs +24 -8
  102. data/sig/temporalio/worker.rbs +12 -12
  103. data/sig/temporalio/workflow/child_workflow_handle.rbs +2 -1
  104. data/sig/temporalio/workflow/event_group.rbs +37 -0
  105. data/sig/temporalio/workflow/external_workflow_handle.rbs +3 -2
  106. data/sig/temporalio/workflow/info.rbs +2 -0
  107. data/sig/temporalio/workflow/nexus_client.rbs +4 -2
  108. data/sig/temporalio/workflow.rbs +27 -11
  109. metadata +16 -1
@@ -61,7 +61,8 @@ module Temporalio
61
61
  :current_deployment_version,
62
62
  :current_history_length, :current_history_size, :replaying, :random,
63
63
  :signal_handlers, :query_handlers, :update_handlers, :context_frozen, :assert_valid_local_activity,
64
- :in_query_or_validator, :random_disabled, :patch_activation_callback
64
+ :in_query_or_validator, :random_disabled, :patch_activation_callback,
65
+ :check_conditions
65
66
  attr_accessor :io_enabled, :current_details
66
67
 
67
68
  def initialize(details)
@@ -102,6 +103,7 @@ module Temporalio
102
103
  @replaying = false
103
104
  @context_frozen = false
104
105
  @in_query_or_validator = false
106
+ @check_conditions = false
105
107
  @random_disabled = false
106
108
  @workflow_failure_exception_types = details.workflow_failure_exception_types
107
109
  @signal_handlers = HandlerHash.new(
@@ -147,6 +149,8 @@ module Temporalio
147
149
  end,
148
150
  has_last_result?: !@init_job.last_completion_result.nil?,
149
151
  namespace: details.namespace,
152
+ original_execution_run_id: ProtoUtils.string_or(@init_job.original_execution_run_id) ||
153
+ details.initial_activation.run_id,
150
154
  parent: if @init_job.parent_workflow_info
151
155
  Workflow::Info::ParentInfo.new(
152
156
  namespace: @init_job.parent_workflow_info.namespace,
@@ -194,6 +198,10 @@ module Temporalio
194
198
  @current_history_size = activation.history_size_bytes
195
199
  @replaying = activation.is_replaying
196
200
  @now_timestamp = activation.timestamp
201
+ # Updates can only change state once accepted, which is recorded when that happens
202
+ @check_conditions = activation.jobs.any? do |job|
203
+ !%i[do_update query_workflow].include?(job.variant)
204
+ end
197
205
 
198
206
  # Apply jobs and run event loop
199
207
  begin
@@ -289,7 +297,7 @@ module Temporalio
289
297
  end
290
298
  end
291
299
 
292
- def patch(patch_id:, deprecated:)
300
+ def patch(patch_id:, deprecated:, event_groups: nil)
293
301
  # Use memoized result if present. If this is being deprecated, we can still use memoized result and skip the
294
302
  # command.
295
303
  patch_id = patch_id.to_s
@@ -305,7 +313,8 @@ module Temporalio
305
313
  if patched
306
314
  add_command(
307
315
  Bridge::Api::WorkflowCommands::WorkflowCommand.new(
308
- set_patch_marker: Bridge::Api::WorkflowCommands::SetPatchMarker.new(patch_id:, deprecated:)
316
+ set_patch_marker: Bridge::Api::WorkflowCommands::SetPatchMarker.new(patch_id:, deprecated:),
317
+ event_group_markers: Workflow::EventGroup._markers_for_command(event_groups)
309
318
  )
310
319
  )
311
320
  end
@@ -426,6 +435,10 @@ module Temporalio
426
435
  end
427
436
 
428
437
  def apply_signal(job)
438
+ if job.originating_event_id.nil? || job.originating_event_id <= 0
439
+ raise "Signal #{job.signal_name} has invalid originating event ID #{job.originating_event_id}"
440
+ end
441
+
429
442
  # Get signal definition, falling back to dynamic if not present and not reserved
430
443
  defn = signal_handlers[job.signal_name]
431
444
  defn = signal_handlers[nil] if !defn && !Internal::ProtoUtils.reserved_name?(job.signal_name)
@@ -436,27 +449,29 @@ module Temporalio
436
449
  end
437
450
  # Process as a top level handler so that errors are treated as if in primary workflow method
438
451
  schedule(top_level: true, handler_exec:) do
439
- # Send to interceptor if there is a definition, buffer otherwise
440
- if defn
441
- @inbound.handle_signal(
442
- Temporalio::Worker::Interceptor::Workflow::HandleSignalInput.new(
443
- signal: job.signal_name,
444
- args: begin
445
- convert_handler_args(payload_array: job.input, defn:)
446
- rescue StandardError => e
447
- # Signals argument conversion failure must not fail task
448
- @logger.error("Failed converting signal input arguments for #{job.signal_name}, dropping signal")
449
- @logger.error(e)
450
- next
451
- end,
452
- definition: defn,
453
- headers: ProtoUtils.headers_from_proto_map(job.headers, @payload_converter) || {}
452
+ Workflow::EventGroup._with_inbound_event(job.originating_event_id) do
453
+ # Send to interceptor if there is a definition, buffer otherwise
454
+ if defn
455
+ @inbound.handle_signal(
456
+ Temporalio::Worker::Interceptor::Workflow::HandleSignalInput.new(
457
+ signal: job.signal_name,
458
+ args: begin
459
+ convert_handler_args(payload_array: job.input, defn:)
460
+ rescue StandardError => e
461
+ # Signals argument conversion failure must not fail task
462
+ @logger.error("Failed converting signal input arguments for #{job.signal_name}, dropping signal")
463
+ @logger.error(e)
464
+ next
465
+ end,
466
+ definition: defn,
467
+ headers: ProtoUtils.headers_from_proto_map(job.headers, @payload_converter) || {}
468
+ )
454
469
  )
455
- )
456
- else
457
- buffered = @buffered_signals[job.signal_name]
458
- buffered = @buffered_signals[job.signal_name] = [] if buffered.nil?
459
- buffered << job
470
+ else
471
+ buffered = @buffered_signals[job.signal_name]
472
+ buffered = @buffered_signals[job.signal_name] = [] if buffered.nil?
473
+ buffered << job
474
+ end
460
475
  end
461
476
  end
462
477
  end
@@ -522,6 +537,8 @@ module Temporalio
522
537
  end
523
538
 
524
539
  def apply_update(job)
540
+ raise "Update #{job.name} has invalid update ID #{job.id.inspect}" if job.id.nil? || job.id.empty?
541
+
525
542
  # Get update definition, falling back to dynamic if not present and not reserved
526
543
  defn = update_handlers[job.name]
527
544
  defn = update_handlers[nil] if !defn && !Internal::ProtoUtils.reserved_name?(job.name)
@@ -585,9 +602,12 @@ module Temporalio
585
602
  )
586
603
  )
587
604
  accepted = true
605
+ @check_conditions = true
588
606
 
589
- # Issue update
590
- result = @inbound.handle_update(input)
607
+ # Issue update. Validators are intentionally not wrapped in the EG scope
608
+ result = Workflow::EventGroup._with_inbound_update(job.id) do
609
+ @inbound.handle_update(input)
610
+ end
591
611
 
592
612
  add_command(
593
613
  Bridge::Api::WorkflowCommands::WorkflowCommand.new(
@@ -672,7 +692,8 @@ module Temporalio
672
692
  search_attributes: err.search_attributes&._to_proto,
673
693
  retry_policy: err.retry_policy&._to_proto,
674
694
  initial_versioning_behavior: err.initial_versioning_behavior || 0
675
- )
695
+ ),
696
+ event_group_markers: err.send(:_event_group_markers) || []
676
697
  )
677
698
  )
678
699
  elsif @cancellation.canceled? && Error.canceled?(err)
@@ -148,7 +148,7 @@ module Temporalio
148
148
  end
149
149
 
150
150
  # Returns whether completion was submitted to the bridge
151
- # rubocop:disable Naming/PredicateMethod
151
+ # rubocop:disable-next Naming/PredicateMethod
152
152
  def handle_activation_complete(runner:, activation_completion:, encoded:, completion_complete_queue:)
153
153
  if @payload_encoding_visitor && !encoded
154
154
  if Fiber.current_scheduler
@@ -166,7 +166,6 @@ module Temporalio
166
166
  true
167
167
  end
168
168
  end
169
- # rubocop:enable Naming/PredicateMethod
170
169
 
171
170
  def on_shutdown_complete
172
171
  @state.evict_all
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'temporalio/api'
4
+ require 'time'
4
5
 
5
6
  module Temporalio
6
7
  # Collection of typed search attributes.
@@ -142,8 +143,9 @@ module Temporalio
142
143
 
143
144
  # @!visibility private
144
145
  def self._to_proto_pair(key, value)
145
- # We use a default converter, but if type is a time, we need ISO format
146
- value = value.iso8601 if key.type == IndexedValueType::TIME && value.is_a?(Time)
146
+ # We use a default converter, but if type is a time, we need ISO format with nanoseconds since the server keeps
147
+ # sub-second precision
148
+ value = value.iso8601(9) if key.type == IndexedValueType::TIME && value.is_a?(Time)
147
149
 
148
150
  # Convert to payload
149
151
  payload = Converters::PayloadConverter.default.to_payload(value)
@@ -52,6 +52,8 @@ module Temporalio
52
52
  # @param dev_server_extra_args [Array<String>] Any extra arguments for the CLI dev server.
53
53
  # @param dev_server_download_ttl [Float, nil] How long the automatic download should be cached for. If nil, cached
54
54
  # indefinitely.
55
+ # @param dev_server_startup_timeout [Float, nil] Seconds to wait for startup, excluding download time.
56
+ # Defaults to 5.
55
57
  #
56
58
  # @yield [environment] If a block is given, it is called with the environment and upon complete the environment is
57
59
  # shutdown.
@@ -79,6 +81,7 @@ module Temporalio
79
81
  dev_server_download_dest_dir: nil,
80
82
  dev_server_extra_args: [],
81
83
  dev_server_download_ttl: nil,
84
+ dev_server_startup_timeout: nil,
82
85
  &
83
86
  )
84
87
  # Add search attribute args
@@ -105,7 +108,8 @@ module Temporalio
105
108
  log_format: dev_server_log_format,
106
109
  log_level: dev_server_log_level,
107
110
  extra_args: dev_server_extra_args,
108
- download_ttl: dev_server_download_ttl
111
+ download_ttl: dev_server_download_ttl,
112
+ startup_timeout: dev_server_startup_timeout
109
113
  )
110
114
  _with_core_server(
111
115
  core_server: Internal::Bridge::Testing::EphemeralServer.start_dev_server(
@@ -144,6 +148,8 @@ module Temporalio
144
148
  # @param test_server_extra_args [Array<String>] Any extra arguments for the test server.
145
149
  # @param test_server_download_ttl [Float, nil] How long the automatic download should be cached for. If nil,
146
150
  # cached indefinitely.
151
+ # @param test_server_startup_timeout [Float, nil] Seconds to wait for startup, excluding download time.
152
+ # Defaults to 5.
147
153
  #
148
154
  # @yield [environment] If a block is given, it is called with the environment and upon complete the environment is
149
155
  # shutdown.
@@ -163,6 +169,7 @@ module Temporalio
163
169
  test_server_download_dest_dir: nil,
164
170
  test_server_extra_args: [],
165
171
  test_server_download_ttl: nil,
172
+ test_server_startup_timeout: nil,
166
173
  &
167
174
  )
168
175
  server_options = Internal::Bridge::Testing::EphemeralServer::StartTestServerOptions.new(
@@ -173,7 +180,8 @@ module Temporalio
173
180
  download_dest_dir: test_server_download_dest_dir,
174
181
  port:,
175
182
  extra_args: test_server_extra_args,
176
- download_ttl: test_server_download_ttl
183
+ download_ttl: test_server_download_ttl,
184
+ startup_timeout: test_server_startup_timeout
177
185
  )
178
186
  _with_core_server(
179
187
  core_server: Internal::Bridge::Testing::EphemeralServer.start_test_server(
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Temporalio
4
- VERSION = '1.10.1'
4
+ VERSION = '1.11.0'
5
5
  end
@@ -200,7 +200,8 @@ module Temporalio
200
200
  # Input for {Outbound.cancel_external_workflow}.
201
201
  CancelExternalWorkflowInput = Data.define(
202
202
  :id,
203
- :run_id
203
+ :run_id,
204
+ :event_groups
204
205
  )
205
206
 
206
207
  # Input for {Outbound.execute_activity}.
@@ -221,7 +222,8 @@ module Temporalio
221
222
  :priority,
222
223
  :arg_hints,
223
224
  :result_hint,
224
- :headers
225
+ :headers,
226
+ :event_groups
225
227
  )
226
228
 
227
229
  # Input for {Outbound.execute_local_activity}.
@@ -239,7 +241,8 @@ module Temporalio
239
241
  :activity_id,
240
242
  :arg_hints,
241
243
  :result_hint,
242
- :headers
244
+ :headers,
245
+ :event_groups
243
246
  )
244
247
 
245
248
  # Input for {Outbound.initialize_continue_as_new_error}.
@@ -254,7 +257,8 @@ module Temporalio
254
257
  :args,
255
258
  :cancellation,
256
259
  :arg_hints,
257
- :headers
260
+ :headers,
261
+ :event_groups
258
262
  )
259
263
 
260
264
  # Input for {Outbound.signal_external_workflow}.
@@ -265,14 +269,16 @@ module Temporalio
265
269
  :args,
266
270
  :cancellation,
267
271
  :arg_hints,
268
- :headers
272
+ :headers,
273
+ :event_groups
269
274
  )
270
275
 
271
276
  # Input for {Outbound.sleep}.
272
277
  SleepInput = Data.define(
273
278
  :duration,
274
279
  :summary,
275
- :cancellation
280
+ :cancellation,
281
+ :event_groups
276
282
  )
277
283
 
278
284
  # Input for {Outbound.start_child_workflow}.
@@ -298,7 +304,8 @@ module Temporalio
298
304
  :versioning_override,
299
305
  :arg_hints,
300
306
  :result_hint,
301
- :headers
307
+ :headers,
308
+ :event_groups
302
309
  )
303
310
 
304
311
  # Input for {Outbound.start_nexus_operation}.
@@ -317,7 +324,8 @@ module Temporalio
317
324
  :cancellation,
318
325
  :arg_hint,
319
326
  :result_hint,
320
- :headers
327
+ :headers,
328
+ :event_groups
321
329
  )
322
330
 
323
331
  # Outbound interceptor for intercepting outbound workflow calls. This should be extended by users needing to
@@ -130,7 +130,7 @@ module Temporalio
130
130
  workflow_definitions: @workflow_definitions
131
131
  )
132
132
  # If there is a block, we'll go ahead and assume it's for with_replay_worker
133
- with_replay_worker(&) if block_given? # steep:ignore
133
+ with_replay_worker(&) if block_given?
134
134
  end
135
135
 
136
136
  # Replay a workflow history.
@@ -395,15 +395,15 @@ module Temporalio
395
395
  # @param logger [Logger] Logger to override client logger with. Default is the client logger.
396
396
  # @param max_cached_workflows [Integer] Number of workflows held in cache for use by sticky task queue. If set to 0,
397
397
  # workflow caching and sticky queuing are disabled.
398
- # @param max_concurrent_workflow_task_polls [Integer] Maximum number of concurrent poll workflow task requests we
399
- # will perform at a time on this worker's task queue.
398
+ # @param max_concurrent_workflow_task_polls [Integer, nil] Maximum number of concurrent poll workflow task requests
399
+ # we will perform at a time on this worker's task queue.
400
400
  # @param nonsticky_to_sticky_poll_ratio [Float] `max_concurrent_workflow_task_polls` * this number = the number of
401
401
  # max pollers that will be allowed for the nonsticky queue when sticky tasks are enabled. If both defaults are
402
402
  # used, the sticky queue will allow 4 max pollers while the nonsticky queue will allow one. The minimum for either
403
403
  # poller is 1, so if `max_concurrent_workflow_task_polls` is 1 and sticky queues are enabled, there will be 2
404
404
  # concurrent polls.
405
- # @param max_concurrent_activity_task_polls [Integer] Maximum number of concurrent poll activity task requests we
406
- # will perform at a time on this worker's task queue.
405
+ # @param max_concurrent_activity_task_polls [Integer, nil] Maximum number of concurrent poll activity task requests
406
+ # we will perform at a time on this worker's task queue.
407
407
  # @param no_remote_activities [Boolean] If true, this worker will only handle workflow tasks and local activities,
408
408
  # it will not poll for activity tasks.
409
409
  # @param sticky_queue_schedule_to_start_timeout [Float] How long a workflow task is allowed to sit on the sticky
@@ -450,9 +450,9 @@ module Temporalio
450
450
  # @param patch_activation_callback [Proc, nil] Experimental callback to decide whether the first non-replay call to
451
451
  # {Workflow.patched} for a patch ID should activate that patch. The callback receives a {PatchActivationInput} and
452
452
  # must return `true` to activate the patch or `false` to leave it inactive.
453
- # @param workflow_task_poller_behavior [PollerBehavior] Specify the behavior of workflow task
453
+ # @param workflow_task_poller_behavior [PollerBehavior, nil] Specify the behavior of workflow task
454
454
  # polling. Defaults to a 5-poller maximum.
455
- # @param activity_task_poller_behavior [PollerBehavior] Specify the behavior of activity task
455
+ # @param activity_task_poller_behavior [PollerBehavior, nil] Specify the behavior of activity task
456
456
  # polling. Defaults to a 5-poller maximum.
457
457
  # @param debug_mode [Boolean] If true, deadlock detection is disabled. Deadlock detection will fail workflow tasks
458
458
  # if they block the thread for too long. This defaults to true if the `TEMPORAL_DEBUG` environment variable is
@@ -473,9 +473,9 @@ module Temporalio
473
473
  identity: nil,
474
474
  logger: client.options.logger,
475
475
  max_cached_workflows: 1000,
476
- max_concurrent_workflow_task_polls: 5,
476
+ max_concurrent_workflow_task_polls: nil,
477
477
  nonsticky_to_sticky_poll_ratio: 0.2,
478
- max_concurrent_activity_task_polls: 5,
478
+ max_concurrent_activity_task_polls: nil,
479
479
  no_remote_activities: false,
480
480
  sticky_queue_schedule_to_start_timeout: 10,
481
481
  max_heartbeat_throttle_interval: 60,
@@ -491,13 +491,20 @@ module Temporalio
491
491
  unsafe_workflow_io_enabled: false,
492
492
  deployment_options: Worker.default_deployment_options,
493
493
  patch_activation_callback: nil,
494
- workflow_task_poller_behavior: PollerBehavior::SimpleMaximum.new(max_concurrent_workflow_task_polls),
495
- activity_task_poller_behavior: PollerBehavior::SimpleMaximum.new(max_concurrent_activity_task_polls),
494
+ workflow_task_poller_behavior: nil,
495
+ activity_task_poller_behavior: nil,
496
496
  debug_mode: %w[true 1].include?(ENV['TEMPORAL_DEBUG'].to_s.downcase),
497
497
  disable_payload_error_limit: false
498
498
  )
499
499
  Internal::ProtoUtils.assert_non_reserved_name(task_queue)
500
500
 
501
+ if max_concurrent_workflow_task_polls
502
+ workflow_task_poller_behavior ||= PollerBehavior::SimpleMaximum.new(max_concurrent_workflow_task_polls)
503
+ end
504
+ if max_concurrent_activity_task_polls
505
+ activity_task_poller_behavior ||= PollerBehavior::SimpleMaximum.new(max_concurrent_activity_task_polls)
506
+ end
507
+
501
508
  @options = Options.new(
502
509
  client:,
503
510
  task_queue:,
@@ -582,9 +589,9 @@ module Temporalio
582
589
  tuner: @options.tuner._to_bridge_options,
583
590
  identity_override: @options.identity,
584
591
  max_cached_workflows: @options.max_cached_workflows,
585
- workflow_task_poller_behavior: @options.workflow_task_poller_behavior._to_bridge_options,
592
+ workflow_task_poller_behavior: @options.workflow_task_poller_behavior&._to_bridge_options,
586
593
  nonsticky_to_sticky_poll_ratio: @options.nonsticky_to_sticky_poll_ratio,
587
- activity_task_poller_behavior: @options.activity_task_poller_behavior._to_bridge_options,
594
+ activity_task_poller_behavior: @options.activity_task_poller_behavior&._to_bridge_options,
588
595
  enable_workflows: !@options.workflows.empty?,
589
596
  enable_local_activities: !@options.workflows.empty? && !@options.activities.empty?,
590
597
  enable_remote_activities: !@options.activities.empty? && !@options.no_remote_activities,
@@ -43,7 +43,10 @@ module Temporalio
43
43
  # @param cancellation [Cancellation] Cancellation for canceling the signalling.
44
44
  # @param arg_hints [Array<Object>, nil] Overrides converter hints for arguments if any. If unset/nil and the
45
45
  # signal definition has arg hints, those are used by default.
46
- def signal(signal, *args, cancellation: Workflow.cancellation, arg_hints: nil)
46
+ # @param event_groups [Array<EventGroup>, nil] Event Groups to attach to the signal command, in
47
+ # addition to any groups from enclosing {Workflow.with_event_groups} scopes.
48
+ # WARNING: This option is experimental.
49
+ def signal(signal, *args, cancellation: Workflow.cancellation, arg_hints: nil, event_groups: nil)
47
50
  raise NotImplementedError
48
51
  end
49
52
  end
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/api/sdk/v1/event_group_marker'
4
+ require 'temporalio/converters/payload_converter'
5
+
6
+ module Temporalio
7
+ module Workflow
8
+ # A discrete token associating workflow commands, and the history events they produce, with a logical group for UI
9
+ # and observability purposes.
10
+ #
11
+ # Multiple Event Groups may be attached to a single command, and a single Event Group may be attached to multiple
12
+ # commands. Instances are created with {Workflow.create_event_group}. Attach them to specific commands via the
13
+ # `event_groups:` option, or to every command produced within a block via {Workflow.with_event_groups}.
14
+ #
15
+ # WARNING: Event Groups support is experimental.
16
+ class EventGroup
17
+ # @!visibility private
18
+ Active = Struct.new(:implicit, :explicit, keyword_init: true)
19
+
20
+ # @!visibility private
21
+ EMPTY_ACTIVE = Active.new(implicit: nil, explicit: {}.freeze).freeze
22
+
23
+ # @!visibility private
24
+ STORAGE_KEY = :__temporal_event_groups
25
+
26
+ class << self
27
+ # @!visibility private
28
+ def _active
29
+ Fiber[STORAGE_KEY] || EMPTY_ACTIVE
30
+ end
31
+
32
+ # @!visibility private
33
+ def _with_active(active)
34
+ prev = Fiber[STORAGE_KEY]
35
+ Fiber[STORAGE_KEY] = active
36
+ yield
37
+ ensure
38
+ Fiber[STORAGE_KEY] = prev
39
+ end
40
+
41
+ # @!visibility private
42
+ def _with_event_groups(*groups, &)
43
+ active = _active
44
+ explicit = active.explicit.dup
45
+ groups.each do |group|
46
+ unless group.is_a?(Label)
47
+ raise TypeError, 'Event groups must be created with Temporalio::Workflow.create_event_group'
48
+ end
49
+
50
+ explicit[group.id] = group
51
+ end
52
+ _with_active(Active.new(implicit: active.implicit, explicit:), &)
53
+ end
54
+
55
+ # @!visibility private
56
+ def _with_inbound_event(event_id, &)
57
+ group = Implicit.new(
58
+ Api::Sdk::V1::EventGroupMarker.new(
59
+ inbound_event: Api::Sdk::V1::EventGroupMarker::InboundEvent.new(inbound_event_id: event_id)
60
+ )
61
+ )
62
+ _with_active(group._applied_over(_active), &)
63
+ end
64
+
65
+ # @!visibility private
66
+ def _with_inbound_update(update_id, &)
67
+ group = Implicit.new(
68
+ Api::Sdk::V1::EventGroupMarker.new(
69
+ inbound_update: Api::Sdk::V1::EventGroupMarker::InboundUpdate.new(inbound_update_id: update_id)
70
+ )
71
+ )
72
+ _with_active(group._applied_over(_active), &)
73
+ end
74
+
75
+ # @!visibility private
76
+ def _markers_for_command(directs)
77
+ active = _active
78
+ explicit = active.explicit
79
+ if directs
80
+ explicit = explicit.dup
81
+ directs.each do |group|
82
+ unless group.is_a?(Label)
83
+ raise TypeError, 'Event groups must be created with Temporalio::Workflow.create_event_group'
84
+ end
85
+
86
+ explicit[group.id] = group
87
+ end
88
+ end
89
+ groups = explicit.values
90
+ groups.unshift(active.implicit) if active.implicit
91
+ groups.map(&:_to_proto)
92
+ end
93
+ end
94
+
95
+ # @!visibility private
96
+ def initialize
97
+ raise NotImplementedError, 'Cannot instantiate EventGroup directly, use Temporalio::Workflow.create_event_group'
98
+ end
99
+
100
+ # @!visibility private
101
+ def _applied_over(_active)
102
+ raise NotImplementedError
103
+ end
104
+
105
+ # @!visibility private
106
+ def _to_proto
107
+ raise NotImplementedError
108
+ end
109
+
110
+ # An Event Group explicitly created by workflow code.
111
+ #
112
+ # @!visibility private
113
+ class Label < EventGroup
114
+ attr_reader :id, :label
115
+
116
+ def initialize(id, label) # rubocop:disable Lint/MissingSuper
117
+ @id = id
118
+ @label = label
119
+ end
120
+
121
+ # @!visibility private
122
+ def _applied_over(active)
123
+ Active.new(implicit: active.implicit, explicit: active.explicit.merge(id => self))
124
+ end
125
+
126
+ # @!visibility private
127
+ def _to_proto
128
+ marker_label = Api::Sdk::V1::EventGroupMarker::Label.new(id:)
129
+ # Deliberately the SDK's default converter, not the user-provided one.
130
+ marker_label.label = Converters::PayloadConverter.default.to_payload(label) if label
131
+ Api::Sdk::V1::EventGroupMarker.new(label: marker_label)
132
+ end
133
+ end
134
+
135
+ # An Event Group created by the SDK around an inbound signal or update.
136
+ #
137
+ # @!visibility private
138
+ class Implicit < EventGroup
139
+ def initialize(marker) # rubocop:disable Lint/MissingSuper
140
+ @marker = marker
141
+ end
142
+
143
+ # @!visibility private
144
+ def _applied_over(_active)
145
+ # Implicit groups do not inherit enclosing explicit scopes: a handler registered
146
+ # inside an explicit scope must not attribute its commands to that scope.
147
+ Active.new(implicit: self, explicit: {})
148
+ end
149
+
150
+ # @!visibility private
151
+ def _to_proto
152
+ @marker
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
@@ -30,12 +30,19 @@ module Temporalio
30
30
  # @param cancellation [Cancellation] Cancellation for canceling the signalling.
31
31
  # @param arg_hints [Array<Object>, nil] Overrides converter hints for arguments if any. If unset/nil and the
32
32
  # signal definition has arg hints, those are used by default.
33
- def signal(signal, *args, cancellation: Workflow.cancellation, arg_hints: nil)
33
+ # @param event_groups [Array<EventGroup>, nil] Event Groups to attach to the signal command, in
34
+ # addition to any groups from enclosing {Workflow.with_event_groups} scopes.
35
+ # WARNING: This option is experimental.
36
+ def signal(signal, *args, cancellation: Workflow.cancellation, arg_hints: nil, event_groups: nil)
34
37
  raise NotImplementedError
35
38
  end
36
39
 
37
40
  # Cancel the external workflow.
38
- def cancel
41
+ #
42
+ # @param event_groups [Array<EventGroup>, nil] Event Groups to attach to the cancel command, in
43
+ # addition to any groups from enclosing {Workflow.with_event_groups} scopes.
44
+ # WARNING: This option is experimental.
45
+ def cancel(event_groups: nil)
39
46
  raise NotImplementedError
40
47
  end
41
48
  end
@@ -80,7 +80,7 @@ module Temporalio
80
80
  return unless block_given?
81
81
 
82
82
  @fiber = Fiber.schedule do
83
- @result = block.call # steep:ignore
83
+ @result = block.call
84
84
  rescue Exception => e # rubocop:disable Lint/RescueException
85
85
  # These errors invalidate the whole workflow task. If stored and only raised on wait, the activation may
86
86
  # complete successfully with commands that were emitted before the error.
@@ -13,6 +13,7 @@ module Temporalio
13
13
  :last_result,
14
14
  :has_last_result?,
15
15
  :namespace,
16
+ :original_execution_run_id,
16
17
  :parent,
17
18
  :priority,
18
19
  :retry_policy,
@@ -49,6 +50,9 @@ module Temporalio
49
50
 
50
51
  # @!attribute namespace
51
52
  # @return [String] Namespace for the workflow.
53
+ # @!attribute original_execution_run_id
54
+ # @return [String] Run ID recorded on the workflow execution started event. Unlike {#run_id},
55
+ # this value is preserved across workflow resets.
52
56
  # @!attribute parent
53
57
  # @return [ParentInfo, nil] Parent information for the workflow if this is a child.
54
58
  # @!attribute priority