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
@@ -93,8 +93,10 @@ module Temporalio
93
93
  def handle_start_task(task_token, start)
94
94
  set_running_activity(task_token, nil)
95
95
 
96
- # Find activity definition, falling back to dynamic if present
97
- defn = @activities[start.activity_type] || @activities[nil]
96
+ # Find activity definition, falling back to dynamic if not found and not reserved name
97
+ defn = @activities[start.activity_type]
98
+ defn = @activities[nil] if !defn && !Internal::ProtoUtils.reserved_name?(start.activity_type)
99
+
98
100
  if defn.nil?
99
101
  raise Error::ApplicationError.new(
100
102
  "Activity #{start.activity_type} for workflow #{start.workflow_execution.workflow_id} " \
@@ -114,7 +116,7 @@ module Temporalio
114
116
  # Unset at the end
115
117
  Activity::Context._current_executor = nil
116
118
  end
117
- rescue Exception => e # rubocop:disable Lint/RescueException We are intending to catch everything here
119
+ rescue Exception => e # rubocop:disable Lint/RescueException -- We are intending to catch everything here
118
120
  remove_running_activity(task_token)
119
121
  @scoped_logger.warn("Failed starting activity #{start.activity_type}")
120
122
  @scoped_logger.warn(e)
@@ -201,6 +203,7 @@ module Temporalio
201
203
 
202
204
  # Run
203
205
  activity = RunningActivity.new(
206
+ worker: @worker,
204
207
  info:,
205
208
  cancellation: Cancellation.new,
206
209
  worker_shutdown_cancellation: @worker._worker_shutdown_cancellation,
@@ -210,8 +213,8 @@ module Temporalio
210
213
  )
211
214
  Activity::Context._current_executor&.set_activity_context(defn, activity)
212
215
  set_running_activity(task_token, activity)
213
- run_activity(activity, input)
214
- rescue Exception => e # rubocop:disable Lint/RescueException We are intending to catch everything here
216
+ run_activity(defn, activity, input)
217
+ rescue Exception => e # rubocop:disable Lint/RescueException -- We are intending to catch everything here
215
218
  @scoped_logger.warn("Failed starting or sending completion for activity #{start.activity_type}")
216
219
  @scoped_logger.warn(e)
217
220
  # This means that the activity couldn't start or send completion (run
@@ -236,8 +239,11 @@ module Temporalio
236
239
  remove_running_activity(task_token)
237
240
  end
238
241
 
239
- def run_activity(activity, input)
242
+ def run_activity(defn, activity, input)
240
243
  result = begin
244
+ # Create the instance. We choose to do this before interceptors so that it is available in the interceptor.
245
+ activity.instance = defn.instance.is_a?(Proc) ? defn.instance.call : defn.instance # steep:ignore
246
+
241
247
  # Build impl with interceptors
242
248
  # @type var impl: Temporalio::Worker::Interceptor::Activity::Inbound
243
249
  impl = InboundImplementation.new(self)
@@ -255,7 +261,7 @@ module Temporalio
255
261
  result: @worker.options.client.data_converter.to_payload(result)
256
262
  )
257
263
  )
258
- rescue Exception => e # rubocop:disable Lint/RescueException We are intending to catch everything here
264
+ rescue Exception => e # rubocop:disable Lint/RescueException -- We are intending to catch everything here
259
265
  if e.is_a?(Activity::CompleteAsyncError)
260
266
  # Wanting to complete async
261
267
  @scoped_logger.debug('Completing activity asynchronously')
@@ -293,9 +299,10 @@ module Temporalio
293
299
 
294
300
  class RunningActivity < Activity::Context
295
301
  attr_reader :info, :cancellation, :worker_shutdown_cancellation, :payload_converter, :logger
296
- attr_accessor :_outbound_impl, :_server_requested_cancel
302
+ attr_accessor :instance, :_outbound_impl, :_server_requested_cancel
297
303
 
298
304
  def initialize( # rubocop:disable Lint/MissingSuper
305
+ worker:,
299
306
  info:,
300
307
  cancellation:,
301
308
  worker_shutdown_cancellation:,
@@ -303,6 +310,7 @@ module Temporalio
303
310
  logger:,
304
311
  runtime_metric_meter:
305
312
  )
313
+ @worker = worker
306
314
  @info = info
307
315
  @cancellation = cancellation
308
316
  @worker_shutdown_cancellation = worker_shutdown_cancellation
@@ -331,6 +339,10 @@ module Temporalio
331
339
  }
332
340
  )
333
341
  end
342
+
343
+ def client
344
+ @worker.client
345
+ end
334
346
  end
335
347
 
336
348
  class InboundImplementation < Temporalio::Worker::Interceptor::Activity::Inbound
@@ -34,7 +34,7 @@ module Temporalio
34
34
  rescue InjectEventForTesting => e
35
35
  @queue.push(e.event)
36
36
  @queue.push(Event::BlockSuccess.new(result: e))
37
- rescue Exception => e # rubocop:disable Lint/RescueException Intentionally catch all
37
+ rescue Exception => e # rubocop:disable Lint/RescueException -- Intentionally catch all
38
38
  @queue.push(Event::BlockFailure.new(error: e))
39
39
  end
40
40
  else
@@ -43,7 +43,7 @@ module Temporalio
43
43
  rescue InjectEventForTesting => e
44
44
  @queue.push(e.event)
45
45
  @queue.push(Event::BlockSuccess.new(result: e))
46
- rescue Exception => e # rubocop:disable Lint/RescueException Intentionally catch all
46
+ rescue Exception => e # rubocop:disable Lint/RescueException -- Intentionally catch all
47
47
  @queue.push(Event::BlockFailure.new(error: e))
48
48
  end
49
49
  end
@@ -32,6 +32,16 @@ module Temporalio
32
32
  @instance.continue_as_new_suggested
33
33
  end
34
34
 
35
+ def current_details
36
+ @instance.current_details || ''
37
+ end
38
+
39
+ def current_details=(details)
40
+ raise 'Details must be a String' unless details.nil? || details.is_a?(String)
41
+
42
+ @instance.current_details = (details || '')
43
+ end
44
+
35
45
  def current_history_length
36
46
  @instance.current_history_length
37
47
  end
@@ -52,6 +62,7 @@ module Temporalio
52
62
  activity,
53
63
  *args,
54
64
  task_queue:,
65
+ summary:,
55
66
  schedule_to_close_timeout:,
56
67
  schedule_to_start_timeout:,
57
68
  start_to_close_timeout:,
@@ -62,11 +73,22 @@ module Temporalio
62
73
  activity_id:,
63
74
  disable_eager_execution:
64
75
  )
76
+ activity = case activity
77
+ when Class
78
+ Activity::Definition::Info.from_activity(activity).name&.to_s
79
+ when Symbol, String
80
+ activity.to_s
81
+ else
82
+ raise ArgumentError, 'Activity must be a definition class, or a symbol/string'
83
+ end
84
+ raise 'Cannot invoke dynamic activities' unless activity
85
+
65
86
  @outbound.execute_activity(
66
87
  Temporalio::Worker::Interceptor::Workflow::ExecuteActivityInput.new(
67
88
  activity:,
68
89
  args:,
69
90
  task_queue: task_queue || info.task_queue,
91
+ summary:,
70
92
  schedule_to_close_timeout:,
71
93
  schedule_to_start_timeout:,
72
94
  start_to_close_timeout:,
@@ -93,6 +115,16 @@ module Temporalio
93
115
  cancellation_type:,
94
116
  activity_id:
95
117
  )
118
+ activity = case activity
119
+ when Class
120
+ Activity::Definition::Info.from_activity(activity).name&.to_s
121
+ when Symbol, String
122
+ activity.to_s
123
+ else
124
+ raise ArgumentError, 'Activity must be a definition class, or a symbol/string'
125
+ end
126
+ raise 'Cannot invoke dynamic activities' unless activity
127
+
96
128
  @outbound.execute_local_activity(
97
129
  Temporalio::Worker::Interceptor::Workflow::ExecuteLocalActivityInput.new(
98
130
  activity:,
@@ -122,12 +154,26 @@ module Temporalio
122
154
  @instance.info
123
155
  end
124
156
 
157
+ def instance
158
+ @instance.instance
159
+ end
160
+
125
161
  def initialize_continue_as_new_error(error)
126
162
  @outbound.initialize_continue_as_new_error(
127
163
  Temporalio::Worker::Interceptor::Workflow::InitializeContinueAsNewErrorInput.new(error:)
128
164
  )
129
165
  end
130
166
 
167
+ def io_enabled(&)
168
+ prev = @instance.io_enabled
169
+ @instance.io_enabled = true
170
+ begin
171
+ yield
172
+ ensure
173
+ @instance.io_enabled = prev
174
+ end
175
+ end
176
+
131
177
  def logger
132
178
  @instance.logger
133
179
  end
@@ -187,6 +233,8 @@ module Temporalio
187
233
  *args,
188
234
  id:,
189
235
  task_queue:,
236
+ static_summary:,
237
+ static_details:,
190
238
  cancellation:,
191
239
  cancellation_type:,
192
240
  parent_close_policy:,
@@ -201,10 +249,12 @@ module Temporalio
201
249
  )
202
250
  @outbound.start_child_workflow(
203
251
  Temporalio::Worker::Interceptor::Workflow::StartChildWorkflowInput.new(
204
- workflow:,
252
+ workflow: Workflow::Definition._workflow_type_from_workflow_parameter(workflow),
205
253
  args:,
206
254
  id:,
207
255
  task_queue:,
256
+ static_summary:,
257
+ static_details:,
208
258
  cancellation:,
209
259
  cancellation_type:,
210
260
  parent_close_policy:,
@@ -221,6 +271,10 @@ module Temporalio
221
271
  )
222
272
  end
223
273
 
274
+ def storage
275
+ @storage ||= {}
276
+ end
277
+
224
278
  def timeout(duration, exception_class, *exception_args, summary:, &)
225
279
  raise 'Block required for timeout' unless block_given?
226
280
 
@@ -302,7 +356,7 @@ module Temporalio
302
356
  @outbound.signal_child_workflow(
303
357
  Temporalio::Worker::Interceptor::Workflow::SignalChildWorkflowInput.new(
304
358
  id:,
305
- signal:,
359
+ signal: Workflow::Definition::Signal._name_from_parameter(signal),
306
360
  args:,
307
361
  cancellation:,
308
362
  headers: {}
@@ -315,7 +369,7 @@ module Temporalio
315
369
  Temporalio::Worker::Interceptor::Workflow::SignalExternalWorkflowInput.new(
316
370
  id:,
317
371
  run_id:,
318
- signal:,
372
+ signal: Workflow::Definition::Signal._name_from_parameter(signal),
319
373
  args:,
320
374
  cancellation:,
321
375
  headers: {}
@@ -8,7 +8,7 @@ module Temporalio
8
8
  class Details
9
9
  attr_reader :namespace, :task_queue, :definition, :initial_activation, :logger, :metric_meter,
10
10
  :payload_converter, :failure_converter, :interceptors, :disable_eager_activity_execution,
11
- :illegal_calls, :workflow_failure_exception_types
11
+ :illegal_calls, :workflow_failure_exception_types, :unsafe_workflow_io_enabled
12
12
 
13
13
  def initialize(
14
14
  namespace:,
@@ -22,7 +22,8 @@ module Temporalio
22
22
  interceptors:,
23
23
  disable_eager_activity_execution:,
24
24
  illegal_calls:,
25
- workflow_failure_exception_types:
25
+ workflow_failure_exception_types:,
26
+ unsafe_workflow_io_enabled:
26
27
  )
27
28
  @namespace = namespace
28
29
  @task_queue = task_queue
@@ -36,6 +37,7 @@ module Temporalio
36
37
  @disable_eager_activity_execution = disable_eager_activity_execution
37
38
  @illegal_calls = illegal_calls
38
39
  @workflow_failure_exception_types = workflow_failure_exception_types
40
+ @unsafe_workflow_io_enabled = unsafe_workflow_io_enabled
39
41
  end
40
42
  end
41
43
  end
@@ -56,16 +56,6 @@ module Temporalio
56
56
  raise ArgumentError, 'Activity must have schedule_to_close_timeout or start_to_close_timeout'
57
57
  end
58
58
 
59
- activity_type = case input.activity
60
- when Class
61
- Activity::Definition::Info.from_activity(input.activity).name
62
- when Symbol, String
63
- input.activity.to_s
64
- else
65
- raise ArgumentError, 'Activity must be a definition class, or a symbol/string'
66
- end
67
- raise 'Cannot invoke dynamic activities' unless activity_type
68
-
69
59
  execute_activity_with_local_backoffs(local: false, cancellation: input.cancellation) do
70
60
  seq = (@activity_counter += 1)
71
61
  @instance.add_command(
@@ -73,7 +63,7 @@ module Temporalio
73
63
  schedule_activity: Bridge::Api::WorkflowCommands::ScheduleActivity.new(
74
64
  seq:,
75
65
  activity_id: input.activity_id || seq.to_s,
76
- activity_type:,
66
+ activity_type: input.activity,
77
67
  task_queue: input.task_queue,
78
68
  headers: ProtoUtils.headers_to_proto_hash(input.headers, @instance.payload_converter),
79
69
  arguments: ProtoUtils.convert_to_payload_array(@instance.payload_converter, input.args),
@@ -84,7 +74,8 @@ module Temporalio
84
74
  retry_policy: input.retry_policy&._to_proto,
85
75
  cancellation_type: input.cancellation_type,
86
76
  do_not_eagerly_execute: input.disable_eager_execution
87
- )
77
+ ),
78
+ user_metadata: ProtoUtils.to_user_metadata(input.summary, nil, @instance.payload_converter)
88
79
  )
89
80
  )
90
81
  seq
@@ -96,16 +87,6 @@ module Temporalio
96
87
  raise ArgumentError, 'Activity must have schedule_to_close_timeout or start_to_close_timeout'
97
88
  end
98
89
 
99
- activity_type = case input.activity
100
- when Class
101
- Activity::Definition::Info.from_activity(input.activity).name
102
- when Symbol, String
103
- input.activity.to_s
104
- else
105
- raise ArgumentError, 'Activity must be a definition class, or a symbol/string'
106
- end
107
- raise 'Cannot invoke dynamic activities' unless activity_type
108
-
109
90
  execute_activity_with_local_backoffs(local: true, cancellation: input.cancellation) do |do_backoff|
110
91
  seq = (@activity_counter += 1)
111
92
  @instance.add_command(
@@ -113,7 +94,7 @@ module Temporalio
113
94
  schedule_local_activity: Bridge::Api::WorkflowCommands::ScheduleLocalActivity.new(
114
95
  seq:,
115
96
  activity_id: input.activity_id || seq.to_s,
116
- activity_type:,
97
+ activity_type: input.activity,
117
98
  headers: ProtoUtils.headers_to_proto_hash(input.headers, @instance.payload_converter),
118
99
  arguments: ProtoUtils.convert_to_payload_array(@instance.payload_converter, input.args),
119
100
  schedule_to_close_timeout: ProtoUtils.seconds_to_duration(input.schedule_to_close_timeout),
@@ -232,7 +213,7 @@ module Temporalio
232
213
  seq = (@external_signal_counter += 1)
233
214
  cmd = Bridge::Api::WorkflowCommands::SignalExternalWorkflowExecution.new(
234
215
  seq:,
235
- signal_name: Workflow::Definition::Signal._name_from_parameter(signal),
216
+ signal_name: signal,
236
217
  args: ProtoUtils.convert_to_payload_array(@instance.payload_converter, args),
237
218
  headers: ProtoUtils.headers_to_proto_hash(headers, @instance.payload_converter)
238
219
  )
@@ -300,7 +281,8 @@ module Temporalio
300
281
  start_timer: Bridge::Api::WorkflowCommands::StartTimer.new(
301
282
  seq:,
302
283
  start_to_fire_timeout: ProtoUtils.seconds_to_duration(duration)
303
- )
284
+ ),
285
+ user_metadata: ProtoUtils.to_user_metadata(input.summary, nil, @instance.payload_converter)
304
286
  )
305
287
  )
306
288
  @instance.pending_timers[seq] = Fiber.current
@@ -340,7 +322,7 @@ module Temporalio
340
322
  seq:,
341
323
  namespace: @instance.info.namespace,
342
324
  workflow_id: input.id,
343
- workflow_type: Workflow::Definition._workflow_type_from_workflow_parameter(input.workflow),
325
+ workflow_type: input.workflow,
344
326
  task_queue: input.task_queue,
345
327
  input: ProtoUtils.convert_to_payload_array(@instance.payload_converter, input.args),
346
328
  workflow_execution_timeout: ProtoUtils.seconds_to_duration(input.execution_timeout),
@@ -354,6 +336,9 @@ module Temporalio
354
336
  memo: ProtoUtils.memo_to_proto_hash(input.memo, @instance.payload_converter),
355
337
  search_attributes: input.search_attributes&._to_proto_hash,
356
338
  cancellation_type: input.cancellation_type
339
+ ),
340
+ user_metadata: ProtoUtils.to_user_metadata(
341
+ input.static_summary, input.static_details, @instance.payload_converter
357
342
  )
358
343
  )
359
344
  )
@@ -137,8 +137,28 @@ module Temporalio
137
137
  end
138
138
 
139
139
  def io_wait(io, events, timeout)
140
- # TODO(cretz): This in a blocking fashion?
141
- raise NotImplementedError, 'TODO'
140
+ # Do not allow if IO disabled
141
+ unless @instance.io_enabled
142
+ raise Workflow::NondeterminismError,
143
+ 'Cannot perform IO from inside a workflow. If this is known to be safe, ' \
144
+ 'the code can be run in a Temporalio::Workflow::Unsafe.io_enabled block.'
145
+ end
146
+
147
+ # Use regular Ruby behavior of blocking this thread. There is no Ruby implementation of io_wait we can just
148
+ # delegate to at this time (or default scheduler or anything like that), so we had to implement this
149
+ # ourselves.
150
+ readers = events.nobits?(IO::READABLE) ? nil : [io]
151
+ writers = events.nobits?(IO::WRITABLE) ? nil : [io]
152
+ priority = events.nobits?(IO::PRIORITY) ? nil : [io]
153
+ ready = IO.select(readers, writers, priority, timeout) # steep:ignore
154
+
155
+ result = 0
156
+ unless ready.nil?
157
+ result |= IO::READABLE if ready[0]&.include?(io)
158
+ result |= IO::WRITABLE if ready[1]&.include?(io)
159
+ result |= IO::PRIORITY if ready[2]&.include?(io)
160
+ end
161
+ result
142
162
  end
143
163
 
144
164
  def kernel_sleep(duration = nil)
@@ -4,6 +4,7 @@ require 'json'
4
4
  require 'temporalio'
5
5
  require 'temporalio/activity/definition'
6
6
  require 'temporalio/api'
7
+ require 'temporalio/converters/payload_converter'
7
8
  require 'temporalio/converters/raw_value'
8
9
  require 'temporalio/error'
9
10
  require 'temporalio/internal/bridge/api'
@@ -56,6 +57,7 @@ module Temporalio
56
57
  :failure_converter, :cancellation, :continue_as_new_suggested, :current_history_length,
57
58
  :current_history_size, :replaying, :random, :signal_handlers, :query_handlers, :update_handlers,
58
59
  :context_frozen
60
+ attr_accessor :io_enabled, :current_details
59
61
 
60
62
  def initialize(details)
61
63
  # Initialize general state
@@ -66,6 +68,7 @@ module Temporalio
66
68
  @logger = ReplaySafeLogger.new(logger: details.logger, instance: self)
67
69
  @logger.scoped_values_getter = proc { scoped_logger_info }
68
70
  @runtime_metric_meter = details.metric_meter
71
+ @io_enabled = details.unsafe_workflow_io_enabled
69
72
  @scheduler = Scheduler.new(self)
70
73
  @payload_converter = details.payload_converter
71
74
  @failure_converter = details.failure_converter
@@ -115,6 +118,7 @@ module Temporalio
115
118
  continued_run_id: ProtoUtils.string_or(@init_job.continued_from_execution_run_id),
116
119
  cron_schedule: ProtoUtils.string_or(@init_job.cron_schedule),
117
120
  execution_timeout: ProtoUtils.duration_to_seconds(@init_job.workflow_execution_timeout),
121
+ headers: ProtoUtils.headers_from_proto_map(@init_job.headers, @payload_converter) || {},
118
122
  last_failure: if @init_job.continued_failure
119
123
  @failure_converter.from_failure(@init_job.continued_failure, @payload_converter)
120
124
  end,
@@ -130,6 +134,12 @@ module Temporalio
130
134
  )
131
135
  end,
132
136
  retry_policy: (RetryPolicy._from_proto(@init_job.retry_policy) if @init_job.retry_policy),
137
+ root: if @init_job.root_workflow
138
+ Workflow::Info::RootInfo.new(
139
+ run_id: @init_job.root_workflow.run_id,
140
+ workflow_id: @init_job.root_workflow.workflow_id
141
+ )
142
+ end,
133
143
  run_id: details.initial_activation.run_id,
134
144
  run_timeout: ProtoUtils.duration_to_seconds(@init_job.workflow_run_timeout),
135
145
  start_time: ProtoUtils.timestamp_to_time(details.initial_activation.timestamp) || raise,
@@ -348,7 +358,10 @@ module Temporalio
348
358
  end
349
359
 
350
360
  def apply_signal(job)
351
- defn = signal_handlers[job.signal_name] || signal_handlers[nil]
361
+ # Get signal definition, falling back to dynamic if not present and not reserved
362
+ defn = signal_handlers[job.signal_name]
363
+ defn = signal_handlers[nil] if !defn && !Internal::ProtoUtils.reserved_name?(job.signal_name)
364
+
352
365
  handler_exec =
353
366
  if defn
354
367
  HandlerExecution.new(name: job.signal_name, update_id: nil, unfinished_policy: defn.unfinished_policy)
@@ -381,37 +394,41 @@ module Temporalio
381
394
  end
382
395
 
383
396
  def apply_query(job)
384
- # TODO(cretz): __temporal_workflow_metadata
385
- defn = case job.query_type
386
- when '__stack_trace'
387
- Workflow::Definition::Query.new(
388
- name: '__stack_trace',
389
- to_invoke: proc { scheduler.stack_trace }
390
- )
391
- else
392
- query_handlers[job.query_type] || query_handlers[nil]
393
- end
394
397
  schedule do
395
- unless defn
396
- raise "Query handler for #{job.query_type} expected but not found, " \
397
- "known queries: [#{query_handlers.keys.compact.sort.join(', ')}]"
398
- end
398
+ # If it's a built-in, run it without interceptors, otherwise do normal behavior
399
+ result = if job.query_type == '__stack_trace'
400
+ # Use raw value built from default converter because we don't want to use user-conversion
401
+ Converters::RawValue.new(Converters::PayloadConverter.default.to_payload(scheduler.stack_trace))
402
+ elsif job.query_type == '__temporal_workflow_metadata'
403
+ # Use raw value built from default converter because we don't want to use user-conversion
404
+ Converters::RawValue.new(Converters::PayloadConverter.default.to_payload(workflow_metadata))
405
+ else
406
+ # Get query definition, falling back to dynamic if not present and not reserved
407
+ defn = query_handlers[job.query_type]
408
+ defn = query_handlers[nil] if !defn && !Internal::ProtoUtils.reserved_name?(job.query_type)
409
+
410
+ unless defn
411
+ raise "Query handler for #{job.query_type} expected but not found, " \
412
+ "known queries: [#{query_handlers.keys.compact.sort.join(', ')}]"
413
+ end
414
+
415
+ with_context_frozen do
416
+ @inbound.handle_query(
417
+ Temporalio::Worker::Interceptor::Workflow::HandleQueryInput.new(
418
+ id: job.query_id,
419
+ query: job.query_type,
420
+ args: begin
421
+ convert_handler_args(payload_array: job.arguments, defn:)
422
+ rescue StandardError => e
423
+ raise "Failed converting query input arguments: #{e}"
424
+ end,
425
+ definition: defn,
426
+ headers: ProtoUtils.headers_from_proto_map(job.headers, @payload_converter) || {}
427
+ )
428
+ )
429
+ end
430
+ end
399
431
 
400
- result = with_context_frozen do
401
- @inbound.handle_query(
402
- Temporalio::Worker::Interceptor::Workflow::HandleQueryInput.new(
403
- id: job.query_id,
404
- query: job.query_type,
405
- args: begin
406
- convert_handler_args(payload_array: job.arguments, defn:)
407
- rescue StandardError => e
408
- raise "Failed converting query input arguments: #{e}"
409
- end,
410
- definition: defn,
411
- headers: ProtoUtils.headers_from_proto_map(job.headers, @payload_converter) || {}
412
- )
413
- )
414
- end
415
432
  add_command(
416
433
  Bridge::Api::WorkflowCommands::WorkflowCommand.new(
417
434
  respond_to_query: Bridge::Api::WorkflowCommands::QueryResult.new(
@@ -435,7 +452,10 @@ module Temporalio
435
452
  end
436
453
 
437
454
  def apply_update(job)
438
- defn = update_handlers[job.name] || update_handlers[nil]
455
+ # Get update definition, falling back to dynamic if not present and not reserved
456
+ defn = update_handlers[job.name]
457
+ defn = update_handlers[nil] if !defn && !Internal::ProtoUtils.reserved_name?(job.name)
458
+
439
459
  handler_exec =
440
460
  (HandlerExecution.new(name: job.name, update_id: job.id, unfinished_policy: defn.unfinished_policy) if defn)
441
461
  schedule(handler_exec:) do
@@ -527,7 +547,7 @@ module Temporalio
527
547
  result = @inbound.execute(
528
548
  Temporalio::Worker::Interceptor::Workflow::ExecuteInput.new(
529
549
  args: @workflow_arguments,
530
- headers: ProtoUtils.headers_from_proto_map(@init_job.headers, @payload_converter) || {}
550
+ headers: @info.headers
531
551
  )
532
552
  )
533
553
  add_command(
@@ -671,6 +691,30 @@ module Temporalio
671
691
  end
672
692
  end
673
693
 
694
+ def workflow_metadata
695
+ Temporalio::Api::Sdk::V1::WorkflowMetadata.new(
696
+ definition: Temporalio::Api::Sdk::V1::WorkflowDefinition.new(
697
+ type: info.workflow_type,
698
+ query_definitions: query_handlers.values.map do |defn|
699
+ Temporalio::Api::Sdk::V1::WorkflowInteractionDefinition.new(
700
+ name: defn.name || '', description: defn.description || ''
701
+ )
702
+ end,
703
+ signal_definitions: signal_handlers.values.map do |defn|
704
+ Temporalio::Api::Sdk::V1::WorkflowInteractionDefinition.new(
705
+ name: defn.name || '', description: defn.description || ''
706
+ )
707
+ end,
708
+ update_definitions: update_handlers.values.map do |defn|
709
+ Temporalio::Api::Sdk::V1::WorkflowInteractionDefinition.new(
710
+ name: defn.name || '', description: defn.description || ''
711
+ )
712
+ end
713
+ ),
714
+ current_details: current_details || ''
715
+ )
716
+ end
717
+
674
718
  def scoped_logger_info
675
719
  @scoped_logger_info ||= {
676
720
  attempt: info.attempt,