temporalio 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/Cargo.lock +679 -437
  4. data/Cargo.toml +5 -5
  5. data/README.md +98 -34
  6. data/ext/Cargo.toml +3 -3
  7. data/lib/temporalio/activity/cancellation_details.rb +58 -0
  8. data/lib/temporalio/activity/context.rb +10 -1
  9. data/lib/temporalio/activity/definition.rb +41 -3
  10. data/lib/temporalio/activity/info.rb +25 -4
  11. data/lib/temporalio/activity.rb +2 -0
  12. data/lib/temporalio/api/activity/v1/message.rb +1 -1
  13. data/lib/temporalio/api/batch/v1/message.rb +4 -2
  14. data/lib/temporalio/api/cloud/account/v1/message.rb +1 -1
  15. data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +11 -2
  16. data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +2 -2
  17. data/lib/temporalio/api/cloud/identity/v1/message.rb +7 -2
  18. data/lib/temporalio/api/cloud/namespace/v1/message.rb +6 -2
  19. data/lib/temporalio/api/cloud/nexus/v1/message.rb +3 -2
  20. data/lib/temporalio/api/cloud/operation/v1/message.rb +1 -1
  21. data/lib/temporalio/api/cloud/region/v1/message.rb +1 -1
  22. data/lib/temporalio/api/cloud/resource/v1/message.rb +1 -1
  23. data/lib/temporalio/api/cloud/sink/v1/message.rb +1 -1
  24. data/lib/temporalio/api/cloud/usage/v1/message.rb +1 -1
  25. data/lib/temporalio/api/command/v1/message.rb +2 -2
  26. data/lib/temporalio/api/common/v1/grpc_status.rb +1 -1
  27. data/lib/temporalio/api/common/v1/message.rb +3 -2
  28. data/lib/temporalio/api/deployment/v1/message.rb +3 -2
  29. data/lib/temporalio/api/enums/v1/batch_operation.rb +1 -1
  30. data/lib/temporalio/api/enums/v1/command_type.rb +1 -1
  31. data/lib/temporalio/api/enums/v1/common.rb +5 -2
  32. data/lib/temporalio/api/enums/v1/deployment.rb +3 -2
  33. data/lib/temporalio/api/enums/v1/event_type.rb +2 -2
  34. data/lib/temporalio/api/enums/v1/failed_cause.rb +2 -2
  35. data/lib/temporalio/api/enums/v1/namespace.rb +1 -1
  36. data/lib/temporalio/api/enums/v1/nexus.rb +1 -1
  37. data/lib/temporalio/api/enums/v1/query.rb +1 -1
  38. data/lib/temporalio/api/enums/v1/reset.rb +1 -1
  39. data/lib/temporalio/api/enums/v1/schedule.rb +1 -1
  40. data/lib/temporalio/api/enums/v1/task_queue.rb +1 -1
  41. data/lib/temporalio/api/enums/v1/update.rb +1 -1
  42. data/lib/temporalio/api/enums/v1/workflow.rb +2 -2
  43. data/lib/temporalio/api/errordetails/v1/message.rb +1 -1
  44. data/lib/temporalio/api/export/v1/message.rb +1 -1
  45. data/lib/temporalio/api/failure/v1/message.rb +3 -2
  46. data/lib/temporalio/api/filter/v1/message.rb +1 -1
  47. data/lib/temporalio/api/history/v1/message.rb +4 -2
  48. data/lib/temporalio/api/namespace/v1/message.rb +1 -1
  49. data/lib/temporalio/api/nexus/v1/message.rb +2 -2
  50. data/lib/temporalio/api/operatorservice/v1/request_response.rb +1 -1
  51. data/lib/temporalio/api/operatorservice/v1/service.rb +1 -1
  52. data/lib/temporalio/api/payload_visitor.rb +87 -0
  53. data/lib/temporalio/api/protocol/v1/message.rb +1 -1
  54. data/lib/temporalio/api/query/v1/message.rb +1 -1
  55. data/lib/temporalio/api/replication/v1/message.rb +1 -1
  56. data/lib/temporalio/api/rules/v1/message.rb +27 -0
  57. data/lib/temporalio/api/schedule/v1/message.rb +2 -2
  58. data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +1 -1
  59. data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +1 -1
  60. data/lib/temporalio/api/sdk/v1/user_metadata.rb +1 -1
  61. data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +1 -1
  62. data/lib/temporalio/api/taskqueue/v1/message.rb +2 -2
  63. data/lib/temporalio/api/testservice/v1/request_response.rb +1 -1
  64. data/lib/temporalio/api/testservice/v1/service.rb +1 -1
  65. data/lib/temporalio/api/update/v1/message.rb +1 -1
  66. data/lib/temporalio/api/version/v1/message.rb +1 -1
  67. data/lib/temporalio/api/worker/v1/message.rb +30 -0
  68. data/lib/temporalio/api/workflow/v1/message.rb +14 -2
  69. data/lib/temporalio/api/workflowservice/v1/request_response.rb +19 -2
  70. data/lib/temporalio/api/workflowservice/v1/service.rb +2 -2
  71. data/lib/temporalio/client/async_activity_handle.rb +12 -4
  72. data/lib/temporalio/client/connection/cloud_service.rb +60 -0
  73. data/lib/temporalio/client/connection/workflow_service.rb +105 -0
  74. data/lib/temporalio/client/interceptor.rb +25 -7
  75. data/lib/temporalio/client/schedule.rb +10 -2
  76. data/lib/temporalio/client/with_start_workflow_operation.rb +9 -1
  77. data/lib/temporalio/client/workflow_handle.rb +50 -10
  78. data/lib/temporalio/client/workflow_update_handle.rb +9 -3
  79. data/lib/temporalio/client.rb +110 -6
  80. data/lib/temporalio/common_enums.rb +14 -0
  81. data/lib/temporalio/contrib/open_telemetry.rb +13 -9
  82. data/lib/temporalio/converters/data_converter.rb +18 -8
  83. data/lib/temporalio/converters/failure_converter.rb +6 -3
  84. data/lib/temporalio/converters/payload_converter/binary_null.rb +2 -2
  85. data/lib/temporalio/converters/payload_converter/binary_plain.rb +2 -2
  86. data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +2 -2
  87. data/lib/temporalio/converters/payload_converter/composite.rb +6 -4
  88. data/lib/temporalio/converters/payload_converter/encoding.rb +4 -2
  89. data/lib/temporalio/converters/payload_converter/json_plain.rb +2 -2
  90. data/lib/temporalio/converters/payload_converter/json_protobuf.rb +2 -2
  91. data/lib/temporalio/converters/payload_converter.rb +16 -6
  92. data/lib/temporalio/error/failure.rb +19 -1
  93. data/lib/temporalio/error.rb +1 -1
  94. data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +1 -1
  95. data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +3 -2
  96. data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +1 -1
  97. data/lib/temporalio/internal/bridge/api/common/common.rb +1 -1
  98. data/lib/temporalio/internal/bridge/api/core_interface.rb +1 -1
  99. data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +1 -1
  100. data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +3 -2
  101. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +2 -2
  102. data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +3 -2
  103. data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +1 -1
  104. data/lib/temporalio/internal/bridge/worker.rb +28 -4
  105. data/lib/temporalio/internal/bridge.rb +1 -1
  106. data/lib/temporalio/internal/client/implementation.rb +60 -52
  107. data/lib/temporalio/internal/proto_utils.rb +4 -4
  108. data/lib/temporalio/internal/worker/activity_worker.rb +93 -20
  109. data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +8 -6
  110. data/lib/temporalio/internal/worker/workflow_instance/context.rb +65 -24
  111. data/lib/temporalio/internal/worker/workflow_instance/details.rb +5 -2
  112. data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +2 -2
  113. data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +64 -18
  114. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +28 -14
  115. data/lib/temporalio/internal/worker/workflow_instance.rb +58 -23
  116. data/lib/temporalio/internal/worker/workflow_worker.rb +16 -6
  117. data/lib/temporalio/priority.rb +59 -0
  118. data/lib/temporalio/testing/activity_environment.rb +17 -2
  119. data/lib/temporalio/testing/workflow_environment.rb +3 -3
  120. data/lib/temporalio/version.rb +1 -1
  121. data/lib/temporalio/versioning_override.rb +56 -0
  122. data/lib/temporalio/worker/deployment_options.rb +45 -0
  123. data/lib/temporalio/worker/illegal_workflow_call_validator.rb +64 -0
  124. data/lib/temporalio/worker/interceptor.rb +13 -1
  125. data/lib/temporalio/worker/poller_behavior.rb +61 -0
  126. data/lib/temporalio/worker/thread_pool.rb +1 -1
  127. data/lib/temporalio/worker/workflow_executor/thread_pool.rb +2 -1
  128. data/lib/temporalio/worker/workflow_replayer.rb +12 -13
  129. data/lib/temporalio/worker.rb +63 -27
  130. data/lib/temporalio/worker_deployment_version.rb +67 -0
  131. data/lib/temporalio/workflow/child_workflow_handle.rb +10 -2
  132. data/lib/temporalio/workflow/definition.rb +183 -33
  133. data/lib/temporalio/workflow/external_workflow_handle.rb +3 -1
  134. data/lib/temporalio/workflow/info.rb +4 -1
  135. data/lib/temporalio/workflow.rb +61 -9
  136. data/lib/temporalio.rb +1 -0
  137. data/temporalio.gemspec +1 -0
  138. metadata +12 -3
@@ -56,7 +56,8 @@ module Temporalio
56
56
  raise ArgumentError, 'Activity must have schedule_to_close_timeout or start_to_close_timeout'
57
57
  end
58
58
 
59
- execute_activity_with_local_backoffs(local: false, cancellation: input.cancellation) do
59
+ execute_activity_with_local_backoffs(local: false, cancellation: input.cancellation,
60
+ result_hint: input.result_hint) do
60
61
  seq = (@activity_counter += 1)
61
62
  @instance.add_command(
62
63
  Bridge::Api::WorkflowCommands::WorkflowCommand.new(
@@ -66,14 +67,17 @@ module Temporalio
66
67
  activity_type: input.activity,
67
68
  task_queue: input.task_queue,
68
69
  headers: ProtoUtils.headers_to_proto_hash(input.headers, @instance.payload_converter),
69
- arguments: ProtoUtils.convert_to_payload_array(@instance.payload_converter, input.args),
70
+ arguments: ProtoUtils.convert_to_payload_array(
71
+ @instance.payload_converter, input.args, hints: input.arg_hints
72
+ ),
70
73
  schedule_to_close_timeout: ProtoUtils.seconds_to_duration(input.schedule_to_close_timeout),
71
74
  schedule_to_start_timeout: ProtoUtils.seconds_to_duration(input.schedule_to_start_timeout),
72
75
  start_to_close_timeout: ProtoUtils.seconds_to_duration(input.start_to_close_timeout),
73
76
  heartbeat_timeout: ProtoUtils.seconds_to_duration(input.heartbeat_timeout),
74
77
  retry_policy: input.retry_policy&._to_proto,
75
78
  cancellation_type: input.cancellation_type,
76
- do_not_eagerly_execute: input.disable_eager_execution
79
+ do_not_eagerly_execute: input.disable_eager_execution,
80
+ priority: input.priority._to_proto
77
81
  ),
78
82
  user_metadata: ProtoUtils.to_user_metadata(input.summary, nil, @instance.payload_converter)
79
83
  )
@@ -87,7 +91,10 @@ module Temporalio
87
91
  raise ArgumentError, 'Activity must have schedule_to_close_timeout or start_to_close_timeout'
88
92
  end
89
93
 
90
- execute_activity_with_local_backoffs(local: true, cancellation: input.cancellation) do |do_backoff|
94
+ @instance.assert_valid_local_activity.call(input.activity)
95
+
96
+ execute_activity_with_local_backoffs(local: true, cancellation: input.cancellation,
97
+ result_hint: input.result_hint) do |do_backoff|
91
98
  seq = (@activity_counter += 1)
92
99
  @instance.add_command(
93
100
  Bridge::Api::WorkflowCommands::WorkflowCommand.new(
@@ -96,7 +103,9 @@ module Temporalio
96
103
  activity_id: input.activity_id || seq.to_s,
97
104
  activity_type: input.activity,
98
105
  headers: ProtoUtils.headers_to_proto_hash(input.headers, @instance.payload_converter),
99
- arguments: ProtoUtils.convert_to_payload_array(@instance.payload_converter, input.args),
106
+ arguments: ProtoUtils.convert_to_payload_array(
107
+ @instance.payload_converter, input.args, hints: input.arg_hints
108
+ ),
100
109
  schedule_to_close_timeout: ProtoUtils.seconds_to_duration(input.schedule_to_close_timeout),
101
110
  schedule_to_start_timeout: ProtoUtils.seconds_to_duration(input.schedule_to_start_timeout),
102
111
  start_to_close_timeout: ProtoUtils.seconds_to_duration(input.start_to_close_timeout),
@@ -112,7 +121,7 @@ module Temporalio
112
121
  end
113
122
  end
114
123
 
115
- def execute_activity_with_local_backoffs(local:, cancellation:, &)
124
+ def execute_activity_with_local_backoffs(local:, cancellation:, result_hint:, &)
116
125
  # We do not even want to schedule if the cancellation is already cancelled. We choose to use canceled
117
126
  # failure instead of wrapping in activity failure which is similar to what other SDKs do, with the accepted
118
127
  # tradeoff that it makes rescue more difficult (hence the presence of Error.canceled? helper).
@@ -121,7 +130,7 @@ module Temporalio
121
130
  # This has to be done in a loop for local activity backoff
122
131
  last_local_backoff = nil
123
132
  loop do
124
- result = execute_activity_once(local:, cancellation:, last_local_backoff:, &)
133
+ result = execute_activity_once(local:, cancellation:, last_local_backoff:, result_hint:, &)
125
134
  return result unless result.is_a?(Bridge::Api::ActivityResult::DoBackoff)
126
135
 
127
136
  # @type var result: untyped
@@ -133,7 +142,7 @@ module Temporalio
133
142
  end
134
143
 
135
144
  # If this doesn't raise, it returns success | DoBackoff
136
- def execute_activity_once(local:, cancellation:, last_local_backoff:, &)
145
+ def execute_activity_once(local:, cancellation:, last_local_backoff:, result_hint:, &)
137
146
  # Add to pending activities (removed by the resolver)
138
147
  seq = yield last_local_backoff
139
148
  @instance.pending_activities[seq] = Fiber.current
@@ -166,7 +175,7 @@ module Temporalio
166
175
 
167
176
  case resolution.status
168
177
  when :completed
169
- @instance.payload_converter.from_payload(resolution.completed.result)
178
+ @instance.payload_converter.from_payload(resolution.completed.result, hint: result_hint)
170
179
  when :failed
171
180
  raise @instance.failure_converter.from_failure(resolution.failed.failure, @instance.payload_converter)
172
181
  when :cancelled
@@ -190,6 +199,7 @@ module Temporalio
190
199
  signal: input.signal,
191
200
  args: input.args,
192
201
  cancellation: input.cancellation,
202
+ arg_hints: input.arg_hints,
193
203
  headers: input.headers
194
204
  )
195
205
  end
@@ -202,11 +212,12 @@ module Temporalio
202
212
  signal: input.signal,
203
213
  args: input.args,
204
214
  cancellation: input.cancellation,
215
+ arg_hints: input.arg_hints,
205
216
  headers: input.headers
206
217
  )
207
218
  end
208
219
 
209
- def _signal_external_workflow(id:, run_id:, child:, signal:, args:, cancellation:, headers:)
220
+ def _signal_external_workflow(id:, run_id:, child:, signal:, args:, cancellation:, arg_hints:, headers:)
210
221
  raise Error::CanceledError, 'Signal canceled before scheduled' if cancellation.canceled?
211
222
 
212
223
  # Add command
@@ -214,7 +225,7 @@ module Temporalio
214
225
  cmd = Bridge::Api::WorkflowCommands::SignalExternalWorkflowExecution.new(
215
226
  seq:,
216
227
  signal_name: signal,
217
- args: ProtoUtils.convert_to_payload_array(@instance.payload_converter, args),
228
+ args: ProtoUtils.convert_to_payload_array(@instance.payload_converter, args, hints: arg_hints),
218
229
  headers: ProtoUtils.headers_to_proto_hash(headers, @instance.payload_converter)
219
230
  )
220
231
  if child
@@ -324,7 +335,8 @@ module Temporalio
324
335
  workflow_id: input.id,
325
336
  workflow_type: input.workflow,
326
337
  task_queue: input.task_queue,
327
- input: ProtoUtils.convert_to_payload_array(@instance.payload_converter, input.args),
338
+ input: ProtoUtils.convert_to_payload_array(@instance.payload_converter, input.args,
339
+ hints: input.arg_hints),
328
340
  workflow_execution_timeout: ProtoUtils.seconds_to_duration(input.execution_timeout),
329
341
  workflow_run_timeout: ProtoUtils.seconds_to_duration(input.run_timeout),
330
342
  workflow_task_timeout: ProtoUtils.seconds_to_duration(input.task_timeout),
@@ -335,7 +347,8 @@ module Temporalio
335
347
  headers: ProtoUtils.headers_to_proto_hash(input.headers, @instance.payload_converter),
336
348
  memo: ProtoUtils.memo_to_proto_hash(input.memo, @instance.payload_converter),
337
349
  search_attributes: input.search_attributes&._to_proto_hash,
338
- cancellation_type: input.cancellation_type
350
+ cancellation_type: input.cancellation_type,
351
+ priority: input.priority._to_proto
339
352
  ),
340
353
  user_metadata: ProtoUtils.to_user_metadata(
341
354
  input.static_summary, input.static_details, @instance.payload_converter
@@ -370,7 +383,8 @@ module Temporalio
370
383
  first_execution_run_id: resolution.succeeded.run_id,
371
384
  instance: @instance,
372
385
  cancellation: input.cancellation,
373
- cancel_callback_key:
386
+ cancel_callback_key:,
387
+ result_hint: input.result_hint
374
388
  )
375
389
  @instance.pending_child_workflows[seq] = handle
376
390
  handle
@@ -24,6 +24,7 @@ require 'temporalio/internal/worker/workflow_instance/scheduler'
24
24
  require 'temporalio/retry_policy'
25
25
  require 'temporalio/scoped_logger'
26
26
  require 'temporalio/worker/interceptor'
27
+ require 'temporalio/worker_deployment_version'
27
28
  require 'temporalio/workflow/info'
28
29
  require 'temporalio/workflow/update_info'
29
30
  require 'timeout'
@@ -54,9 +55,9 @@ module Temporalio
54
55
  attr_reader :context, :logger, :info, :scheduler, :disable_eager_activity_execution, :pending_activities,
55
56
  :pending_timers, :pending_child_workflow_starts, :pending_child_workflows,
56
57
  :pending_external_signals, :pending_external_cancels, :in_progress_handlers, :payload_converter,
57
- :failure_converter, :cancellation, :continue_as_new_suggested, :current_history_length,
58
- :current_history_size, :replaying, :random, :signal_handlers, :query_handlers, :update_handlers,
59
- :context_frozen
58
+ :failure_converter, :cancellation, :continue_as_new_suggested, :current_deployment_version,
59
+ :current_history_length, :current_history_size, :replaying, :random,
60
+ :signal_handlers, :query_handlers, :update_handlers, :context_frozen, :assert_valid_local_activity
60
61
  attr_accessor :io_enabled, :current_details
61
62
 
62
63
  def initialize(details)
@@ -90,7 +91,7 @@ module Temporalio
90
91
  @current_history_length = 0
91
92
  @current_history_size = 0
92
93
  @replaying = false
93
- @failure_exception_types = details.workflow_failure_exception_types + @definition.failure_exception_types
94
+ @workflow_failure_exception_types = details.workflow_failure_exception_types
94
95
  @signal_handlers = HandlerHash.new(
95
96
  details.definition.signals,
96
97
  Workflow::Definition::Signal
@@ -107,6 +108,12 @@ module Temporalio
107
108
  end
108
109
  @query_handlers = HandlerHash.new(details.definition.queries, Workflow::Definition::Query)
109
110
  @update_handlers = HandlerHash.new(details.definition.updates, Workflow::Definition::Update)
111
+ @definition_options = Workflow::DefinitionOptions.new(
112
+ failure_exception_types: details.definition.failure_exception_types,
113
+ versioning_behavior: details.definition.versioning_behavior
114
+ )
115
+
116
+ @assert_valid_local_activity = details.assert_valid_local_activity
110
117
 
111
118
  # Create all things needed from initial job
112
119
  @init_job = details.initial_activation.jobs.find { |j| !j.initialize_workflow.nil? }&.initialize_workflow
@@ -133,6 +140,7 @@ module Temporalio
133
140
  workflow_id: @init_job.parent_workflow_info.workflow_id
134
141
  )
135
142
  end,
143
+ priority: Priority._from_proto(@init_job.priority),
136
144
  retry_policy: (RetryPolicy._from_proto(@init_job.retry_policy) if @init_job.retry_policy),
137
145
  root: if @init_job.root_workflow
138
146
  Workflow::Info::RootInfo.new(
@@ -142,7 +150,7 @@ module Temporalio
142
150
  end,
143
151
  run_id: details.initial_activation.run_id,
144
152
  run_timeout: ProtoUtils.duration_to_seconds(@init_job.workflow_run_timeout),
145
- start_time: ProtoUtils.timestamp_to_time(details.initial_activation.timestamp) || raise,
153
+ start_time: ProtoUtils.timestamp_to_time(@init_job.start_time) || raise,
146
154
  task_queue: details.task_queue,
147
155
  task_timeout: ProtoUtils.duration_to_seconds(@init_job.workflow_task_timeout) || raise,
148
156
  workflow_id: @init_job.workflow_id,
@@ -238,6 +246,9 @@ module Temporalio
238
246
  @commands = []
239
247
  @current_activation_error = nil
240
248
  @continue_as_new_suggested = activation.continue_as_new_suggested
249
+ @current_deployment_version = WorkerDeploymentVersion._from_bridge(
250
+ activation.deployment_version_for_current_task
251
+ )
241
252
  @current_history_length = activation.history_length
242
253
  @current_history_size = activation.history_size_bytes
243
254
  @replaying = activation.is_replaying
@@ -287,7 +298,9 @@ module Temporalio
287
298
  else
288
299
  Bridge::Api::WorkflowCompletion::WorkflowActivationCompletion.new(
289
300
  run_id: activation.run_id,
290
- successful: Bridge::Api::WorkflowCompletion::Success.new(commands: @commands)
301
+ successful: Bridge::Api::WorkflowCompletion::Success.new(
302
+ commands: @commands, versioning_behavior: @definition_options.versioning_behavior
303
+ )
291
304
  )
292
305
  end
293
306
  ensure
@@ -299,7 +312,8 @@ module Temporalio
299
312
  # Convert workflow arguments
300
313
  @workflow_arguments = convert_args(payload_array: @init_job.arguments,
301
314
  method_name: :execute,
302
- raw_args: @definition.raw_args)
315
+ raw_args: @definition.raw_args,
316
+ arg_hints: @definition.arg_hints)
303
317
 
304
318
  # Initialize interceptors
305
319
  @inbound = @interceptors.reverse_each.reduce(InboundImplementation.new(self)) do |acc, int|
@@ -308,11 +322,24 @@ module Temporalio
308
322
  @inbound.init(OutboundImplementation.new(self))
309
323
 
310
324
  # Create the user instance
311
- if @definition.init
312
- @definition.workflow_class.new(*@workflow_arguments)
313
- else
314
- @definition.workflow_class.new
325
+ instance = if @definition.init
326
+ @definition.workflow_class.new(*@workflow_arguments)
327
+ else
328
+ @definition.workflow_class.new
329
+ end
330
+
331
+ # Run Dynamic config getter
332
+ if @definition.dynamic_options_method
333
+ dynamic_options = instance.send(@definition.dynamic_options_method)
334
+ if dynamic_options&.versioning_behavior
335
+ @definition_options.versioning_behavior = dynamic_options.versioning_behavior
336
+ end
337
+ if dynamic_options&.failure_exception_types
338
+ @definition_options.failure_exception_types = dynamic_options.failure_exception_types
339
+ end
315
340
  end
341
+
342
+ instance
316
343
  end
317
344
 
318
345
  def apply(job)
@@ -394,6 +421,7 @@ module Temporalio
394
421
  end
395
422
 
396
423
  def apply_query(job)
424
+ result_hint = nil
397
425
  schedule do
398
426
  # If it's a built-in, run it without interceptors, otherwise do normal behavior
399
427
  result = if job.query_type == '__stack_trace'
@@ -411,6 +439,7 @@ module Temporalio
411
439
  raise "Query handler for #{job.query_type} expected but not found, " \
412
440
  "known queries: [#{query_handlers.keys.compact.sort.join(', ')}]"
413
441
  end
442
+ result_hint = defn.result_hint
414
443
 
415
444
  with_context_frozen do
416
445
  @inbound.handle_query(
@@ -434,7 +463,7 @@ module Temporalio
434
463
  respond_to_query: Bridge::Api::WorkflowCommands::QueryResult.new(
435
464
  query_id: job.query_id,
436
465
  succeeded: Bridge::Api::WorkflowCommands::QuerySuccess.new(
437
- response: @payload_converter.to_payload(result)
466
+ response: @payload_converter.to_payload(result, hint: result_hint)
438
467
  )
439
468
  )
440
469
  )
@@ -523,7 +552,7 @@ module Temporalio
523
552
  Bridge::Api::WorkflowCommands::WorkflowCommand.new(
524
553
  update_response: Bridge::Api::WorkflowCommands::UpdateResponse.new(
525
554
  protocol_instance_id: job.protocol_instance_id,
526
- completed: @payload_converter.to_payload(result)
555
+ completed: @payload_converter.to_payload(result, hint: defn.result_hint)
527
556
  )
528
557
  )
529
558
  )
@@ -553,7 +582,7 @@ module Temporalio
553
582
  add_command(
554
583
  Bridge::Api::WorkflowCommands::WorkflowCommand.new(
555
584
  complete_workflow_execution: Bridge::Api::WorkflowCommands::CompleteWorkflowExecution.new(
556
- result: @payload_converter.to_payload(result)
585
+ result: @payload_converter.to_payload(result, hint: @definition.result_hint)
557
586
  )
558
587
  )
559
588
  )
@@ -581,14 +610,19 @@ module Temporalio
581
610
  def on_top_level_exception(err)
582
611
  if err.is_a?(Workflow::ContinueAsNewError)
583
612
  @logger.debug('Workflow requested continue as new')
613
+ workflow_type, defn_arg_hints, =
614
+ if err.workflow
615
+ Workflow::Definition._workflow_type_and_hints_from_workflow_parameter(err.workflow)
616
+ else
617
+ [nil, @definition.arg_hints, nil]
618
+ end
584
619
  add_command(
585
620
  Bridge::Api::WorkflowCommands::WorkflowCommand.new(
586
621
  continue_as_new_workflow_execution: Bridge::Api::WorkflowCommands::ContinueAsNewWorkflowExecution.new(
587
- workflow_type: if err.workflow
588
- Workflow::Definition._workflow_type_from_workflow_parameter(err.workflow)
589
- end,
622
+ workflow_type:,
590
623
  task_queue: err.task_queue,
591
- arguments: ProtoUtils.convert_to_payload_array(payload_converter, err.args),
624
+ arguments: ProtoUtils.convert_to_payload_array(payload_converter, err.args,
625
+ hints: err.arg_hints || defn_arg_hints),
592
626
  workflow_run_timeout: ProtoUtils.seconds_to_duration(err.run_timeout),
593
627
  workflow_task_timeout: ProtoUtils.seconds_to_duration(err.task_timeout),
594
628
  memo: ProtoUtils.memo_to_proto_hash(err.memo, payload_converter),
@@ -626,9 +660,9 @@ module Temporalio
626
660
  end
627
661
 
628
662
  def failure_exception?(err)
629
- err.is_a?(Error::Failure) || err.is_a?(Timeout::Error) || @failure_exception_types.any? do |cls|
630
- err.is_a?(cls)
631
- end
663
+ err.is_a?(Error::Failure) || err.is_a?(Timeout::Error) ||
664
+ @workflow_failure_exception_types&.any? { |cls| err.is_a?(cls) } ||
665
+ @definition_options.failure_exception_types&.any? { |cls| err.is_a?(cls) }
632
666
  end
633
667
 
634
668
  def with_context_frozen(&)
@@ -643,11 +677,12 @@ module Temporalio
643
677
  payload_array:,
644
678
  method_name: defn.to_invoke.is_a?(Symbol) ? defn.to_invoke : nil,
645
679
  raw_args: defn.raw_args,
680
+ arg_hints: defn.arg_hints,
646
681
  ignore_first_param: defn.name.nil? # Dynamic
647
682
  )
648
683
  end
649
684
 
650
- def convert_args(payload_array:, method_name:, raw_args:, ignore_first_param: false)
685
+ def convert_args(payload_array:, method_name:, raw_args:, arg_hints:, ignore_first_param: false)
651
686
  # Just in case it is not an array
652
687
  payload_array = payload_array.to_ary
653
688
 
@@ -687,7 +722,7 @@ module Temporalio
687
722
  if raw_args
688
723
  payload_array.map { |p| Converters::RawValue.new(p) }
689
724
  else
690
- ProtoUtils.convert_from_payload_array(@payload_converter, payload_array)
725
+ ProtoUtils.convert_from_payload_array(@payload_converter, payload_array, hints: arg_hints)
691
726
  end
692
727
  end
693
728
 
@@ -13,7 +13,7 @@ module Temporalio
13
13
  module Worker
14
14
  # Worker for handling workflow activations. Most activation work is delegated to the workflow executor.
15
15
  class WorkflowWorker
16
- def self.workflow_definitions(workflows)
16
+ def self.workflow_definitions(workflows, should_enforce_versioning_behavior:)
17
17
  workflows.each_with_object({}) do |workflow, hash|
18
18
  # Load definition
19
19
  defn = begin
@@ -29,6 +29,12 @@ module Temporalio
29
29
  # Confirm name not in use
30
30
  raise ArgumentError, "Multiple workflows named #{defn.name || '<dynamic>'}" if hash.key?(defn.name)
31
31
 
32
+ # Enforce versioning behavior is set when versioning is on
33
+ if should_enforce_versioning_behavior &&
34
+ defn.versioning_behavior == VersioningBehavior::UNSPECIFIED && !defn.dynamic_options_method
35
+ raise ArgumentError, "Workflow #{defn.name} must specify a versioning behavior"
36
+ end
37
+
32
38
  hash[defn.name] = defn
33
39
  end
34
40
  end
@@ -69,7 +75,7 @@ module Temporalio
69
75
  workflow_payload_codec_thread_pool:,
70
76
  unsafe_workflow_io_enabled:,
71
77
  debug_mode:,
72
- on_eviction: nil
78
+ assert_valid_local_activity:, on_eviction: nil
73
79
  )
74
80
  @executor = workflow_executor
75
81
 
@@ -105,13 +111,14 @@ module Temporalio
105
111
  disable_eager_activity_execution:,
106
112
  workflow_interceptors:,
107
113
  workflow_failure_exception_types: workflow_failure_exception_types.map do |t|
108
- unless t.is_a?(Class) && t < Exception
114
+ unless t.is_a?(Class) && t <= Exception
109
115
  raise ArgumentError, 'All failure types must classes inheriting Exception'
110
116
  end
111
117
 
112
118
  t
113
119
  end.freeze,
114
- unsafe_workflow_io_enabled:
120
+ unsafe_workflow_io_enabled:,
121
+ assert_valid_local_activity:
115
122
  )
116
123
  @state.on_eviction = on_eviction if on_eviction
117
124
 
@@ -186,14 +193,16 @@ module Temporalio
186
193
  class State
187
194
  attr_reader :workflow_definitions, :bridge_worker, :logger, :metric_meter, :data_converter, :deadlock_timeout,
188
195
  :illegal_calls, :namespace, :task_queue, :disable_eager_activity_execution,
189
- :workflow_interceptors, :workflow_failure_exception_types, :unsafe_workflow_io_enabled
196
+ :workflow_interceptors, :workflow_failure_exception_types, :unsafe_workflow_io_enabled,
197
+ :assert_valid_local_activity
190
198
 
191
199
  attr_writer :on_eviction
192
200
 
193
201
  def initialize(
194
202
  workflow_definitions:, bridge_worker:, logger:, metric_meter:, data_converter:, deadlock_timeout:,
195
203
  illegal_calls:, namespace:, task_queue:, disable_eager_activity_execution:,
196
- workflow_interceptors:, workflow_failure_exception_types:, unsafe_workflow_io_enabled:
204
+ workflow_interceptors:, workflow_failure_exception_types:, unsafe_workflow_io_enabled:,
205
+ assert_valid_local_activity:
197
206
  )
198
207
  @workflow_definitions = workflow_definitions
199
208
  @bridge_worker = bridge_worker
@@ -208,6 +217,7 @@ module Temporalio
208
217
  @workflow_interceptors = workflow_interceptors
209
218
  @workflow_failure_exception_types = workflow_failure_exception_types
210
219
  @unsafe_workflow_io_enabled = unsafe_workflow_io_enabled
220
+ @assert_valid_local_activity = assert_valid_local_activity
211
221
 
212
222
  @running_workflows = {}
213
223
  @running_workflows_mutex = Mutex.new
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/api'
4
+
5
+ module Temporalio
6
+ Priority = Data.define(
7
+ :priority_key
8
+ )
9
+
10
+ # Priority contains metadata that controls relative ordering of task processing when tasks are
11
+ # backlogged in a queue. Initially, Priority will be used in activity and workflow task
12
+ # queues, which are typically where backlogs exist. Priority is (for now) attached to
13
+ # workflows and activities. Activities and child workflows inherit Priority from the workflow
14
+ # that created them, but may override fields when they are started or modified. For each field
15
+ # of a Priority on an activity/workflow, not present or equal to zero/empty string means to
16
+ # inherit the value from the calling workflow, or if there is no calling workflow, then use
17
+ # the default (documented on the field).
18
+ #
19
+ # The overall semantics of Priority are:
20
+ # 1. First, consider "priority_key": lower number goes first.
21
+ # (more will be added here later).
22
+ #
23
+ # @!attribute priority_key
24
+ # @return [Integer, nil] The priority key, which is a positive integer from 1 to n, where
25
+ # smaller integers correspond to higher priorities (tasks run sooner). In general, tasks in a
26
+ # queue should be processed in close to priority order, although small deviations are possible.
27
+ # The maximum priority value (minimum priority) is determined by server configuration, and
28
+ # defaults to 5.
29
+ #
30
+ # The default priority is (min+max)/2. With the default max of 5 and min of 1, that comes
31
+ # out to 3.
32
+ class Priority
33
+ # @!visibility private
34
+ def self._from_proto(priority)
35
+ return default if priority.nil?
36
+
37
+ new(priority_key: priority.priority_key.zero? ? nil : priority.priority_key)
38
+ end
39
+
40
+ # The default priority instance.
41
+ #
42
+ # @return [Priority] The default priority
43
+ def self.default
44
+ @default ||= new(priority_key: nil)
45
+ end
46
+
47
+ # @!visibility private
48
+ def _to_proto
49
+ return nil if priority_key.nil?
50
+
51
+ Temporalio::Api::Common::V1::Priority.new(priority_key: priority_key || 0)
52
+ end
53
+
54
+ # @return [Boolean] True if this priority is empty/default
55
+ def empty?
56
+ priority_key.nil?
57
+ end
58
+ end
59
+ end
@@ -21,9 +21,10 @@ module Temporalio
21
21
  activity_type: 'unknown',
22
22
  attempt: 1,
23
23
  current_attempt_scheduled_time: Time.at(0),
24
- heartbeat_details: [],
25
24
  heartbeat_timeout: nil,
26
25
  local?: false,
26
+ priority: Temporalio::Priority.default,
27
+ raw_heartbeat_details: [],
27
28
  schedule_to_close_timeout: 1.0,
28
29
  scheduled_time: Time.at(0),
29
30
  start_to_close_timeout: 1.0,
@@ -42,8 +43,10 @@ module Temporalio
42
43
  # @param info [Activity::Info] Value for {Activity::Context#info}. Users should not try to instantiate this
43
44
  # themselves, but rather use `with` on {default_info}.
44
45
  # @param on_heartbeat [Proc(Array), nil] Proc that is called with all heartbeat details when
45
- # {Activity::Context#heartbeat} is called.
46
+ # {Activity::Context#heartbeat} is called. Should return a value
46
47
  # @param cancellation [Cancellation] Value for {Activity::Context#cancellation}.
48
+ # @param on_cancellation_details [Proc, nil] Proc that is called when {Activity::Context#cancellation_details} is
49
+ # called. Defaults to a proc that returns an instance if canceled with `cancel_requested` as true.
47
50
  # @param worker_shutdown_cancellation [Cancellation] Value for {Activity::Context#worker_shutdown_cancellation}.
48
51
  # @param payload_converter [Converters::PayloadConverter] Value for {Activity::Context#payload_converter}.
49
52
  # @param logger [Logger] Value for {Activity::Context#logger}.
@@ -55,6 +58,7 @@ module Temporalio
55
58
  info: ActivityEnvironment.default_info,
56
59
  on_heartbeat: nil,
57
60
  cancellation: Cancellation.new,
61
+ on_cancellation_details: nil,
58
62
  worker_shutdown_cancellation: Cancellation.new,
59
63
  payload_converter: Converters::PayloadConverter.default,
60
64
  logger: Logger.new(nil),
@@ -65,6 +69,9 @@ module Temporalio
65
69
  @info = info
66
70
  @on_heartbeat = on_heartbeat
67
71
  @cancellation = cancellation
72
+ @on_cancellation_details = on_cancellation_details || proc do
73
+ @_cancellation_details ||= Activity::CancellationDetails.new if @cancellation.canceled?
74
+ end
68
75
  @worker_shutdown_cancellation = worker_shutdown_cancellation
69
76
  @payload_converter = payload_converter
70
77
  @logger = logger
@@ -92,6 +99,7 @@ module Temporalio
92
99
  defn.instance.is_a?(Proc) ? defn.instance.call : defn.instance,
93
100
  on_heartbeat: @on_heartbeat,
94
101
  cancellation: @cancellation,
102
+ on_cancellation_details: @on_cancellation_details,
95
103
  worker_shutdown_cancellation: @worker_shutdown_cancellation,
96
104
  payload_converter: @payload_converter,
97
105
  logger: @logger,
@@ -121,6 +129,7 @@ module Temporalio
121
129
  instance:,
122
130
  on_heartbeat:,
123
131
  cancellation:,
132
+ on_cancellation_details:,
124
133
  worker_shutdown_cancellation:,
125
134
  payload_converter:,
126
135
  logger:,
@@ -131,6 +140,7 @@ module Temporalio
131
140
  @instance = instance
132
141
  @on_heartbeat = on_heartbeat
133
142
  @cancellation = cancellation
143
+ @on_cancellation_details = on_cancellation_details
134
144
  @worker_shutdown_cancellation = worker_shutdown_cancellation
135
145
  @payload_converter = payload_converter
136
146
  @logger = logger
@@ -152,6 +162,11 @@ module Temporalio
152
162
  def client
153
163
  @client or raise 'No client configured in this test environment'
154
164
  end
165
+
166
+ # @!visibility private
167
+ def cancellation_details
168
+ @on_cancellation_details.call
169
+ end
155
170
  end
156
171
 
157
172
  private_constant :Context
@@ -322,7 +322,7 @@ module Temporalio
322
322
  raise 'Block required' unless block_given?
323
323
  return super unless supports_time_skipping?
324
324
 
325
- already_disabled = @auto_time_skipping
325
+ already_disabled = !@auto_time_skipping
326
326
  @auto_time_skipping = false
327
327
  begin
328
328
  yield
@@ -394,8 +394,8 @@ module Temporalio
394
394
  end
395
395
 
396
396
  # @!visibility private
397
- def result(follow_runs: true, rpc_options: nil)
398
- @env.time_skipping_unlocked { super(follow_runs:, rpc_options:) }
397
+ def result(follow_runs: true, result_hint: nil, rpc_options: nil)
398
+ @env.time_skipping_unlocked { super(follow_runs:, result_hint:, rpc_options:) }
399
399
  end
400
400
  end
401
401
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Temporalio
4
- VERSION = '0.4.0'
4
+ VERSION = '0.5.0'
5
5
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/worker_deployment_version'
4
+
5
+ module Temporalio
6
+ # Base class for version overrides that can be provided in start workflow options.
7
+ # Used to control the versioning behavior of workflows started with this override.
8
+ #
9
+ # WARNING: Experimental API.
10
+ class VersioningOverride
11
+ # @!visibility private
12
+ def _to_proto
13
+ raise NotImplementedError, 'Subclasses must implement this method'
14
+ end
15
+
16
+ # Represents a versioning override to pin a workflow to a specific version
17
+ class Pinned < VersioningOverride
18
+ # The worker deployment version to pin to
19
+ # @return [WorkerDeploymentVersion]
20
+ attr_reader :version
21
+
22
+ # Create a new pinned versioning override
23
+ #
24
+ # @param version [WorkerDeploymentVersion] The worker deployment version to pin to
25
+ def initialize(version)
26
+ @version = version
27
+ super()
28
+ end
29
+
30
+ # TODO: Remove deprecated field setting once removed from server
31
+
32
+ # @!visibility private
33
+ def _to_proto
34
+ Api::Workflow::V1::VersioningOverride.new(
35
+ behavior: Api::Enums::V1::VersioningBehavior::VERSIONING_BEHAVIOR_PINNED,
36
+ pinned_version: @version.to_canonical_string,
37
+ pinned: Api::Workflow::V1::VersioningOverride::PinnedOverride.new(
38
+ behavior: Api::Workflow::V1::VersioningOverride::PinnedOverrideBehavior::PINNED_OVERRIDE_BEHAVIOR_PINNED,
39
+ version: @version._to_proto
40
+ )
41
+ )
42
+ end
43
+ end
44
+
45
+ # Represents a versioning override to auto-upgrade a workflow
46
+ class AutoUpgrade < VersioningOverride
47
+ # @!visibility private
48
+ def _to_proto
49
+ Api::Workflow::V1::VersioningOverride.new(
50
+ behavior: Api::Enums::V1::VersioningBehavior::VERSIONING_BEHAVIOR_AUTO_UPGRADE,
51
+ auto_upgrade: true
52
+ )
53
+ end
54
+ end
55
+ end
56
+ end