temporalio 0.3.0-x86_64-linux → 0.5.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 (152) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/Gemfile +4 -0
  4. data/Rakefile +1 -1
  5. data/lib/temporalio/activity/cancellation_details.rb +58 -0
  6. data/lib/temporalio/activity/context.rb +23 -1
  7. data/lib/temporalio/activity/definition.rb +63 -8
  8. data/lib/temporalio/activity/info.rb +28 -4
  9. data/lib/temporalio/activity.rb +2 -0
  10. data/lib/temporalio/api/activity/v1/message.rb +1 -1
  11. data/lib/temporalio/api/batch/v1/message.rb +9 -2
  12. data/lib/temporalio/api/cloud/account/v1/message.rb +1 -1
  13. data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +11 -2
  14. data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +2 -2
  15. data/lib/temporalio/api/cloud/identity/v1/message.rb +7 -2
  16. data/lib/temporalio/api/cloud/namespace/v1/message.rb +6 -2
  17. data/lib/temporalio/api/cloud/nexus/v1/message.rb +3 -2
  18. data/lib/temporalio/api/cloud/operation/v1/message.rb +1 -1
  19. data/lib/temporalio/api/cloud/region/v1/message.rb +1 -1
  20. data/lib/temporalio/api/cloud/resource/v1/message.rb +1 -1
  21. data/lib/temporalio/api/cloud/sink/v1/message.rb +1 -1
  22. data/lib/temporalio/api/cloud/usage/v1/message.rb +1 -1
  23. data/lib/temporalio/api/command/v1/message.rb +2 -2
  24. data/lib/temporalio/api/common/v1/grpc_status.rb +1 -1
  25. data/lib/temporalio/api/common/v1/message.rb +4 -2
  26. data/lib/temporalio/api/deployment/v1/message.rb +39 -0
  27. data/lib/temporalio/api/enums/v1/batch_operation.rb +2 -2
  28. data/lib/temporalio/api/enums/v1/command_type.rb +1 -1
  29. data/lib/temporalio/api/enums/v1/common.rb +5 -2
  30. data/lib/temporalio/api/enums/v1/deployment.rb +24 -0
  31. data/lib/temporalio/api/enums/v1/event_type.rb +2 -2
  32. data/lib/temporalio/api/enums/v1/failed_cause.rb +2 -2
  33. data/lib/temporalio/api/enums/v1/namespace.rb +1 -1
  34. data/lib/temporalio/api/enums/v1/nexus.rb +21 -0
  35. data/lib/temporalio/api/enums/v1/query.rb +1 -1
  36. data/lib/temporalio/api/enums/v1/reset.rb +2 -2
  37. data/lib/temporalio/api/enums/v1/schedule.rb +1 -1
  38. data/lib/temporalio/api/enums/v1/task_queue.rb +1 -1
  39. data/lib/temporalio/api/enums/v1/update.rb +1 -1
  40. data/lib/temporalio/api/enums/v1/workflow.rb +3 -2
  41. data/lib/temporalio/api/errordetails/v1/message.rb +4 -2
  42. data/lib/temporalio/api/export/v1/message.rb +1 -1
  43. data/lib/temporalio/api/failure/v1/message.rb +5 -2
  44. data/lib/temporalio/api/filter/v1/message.rb +1 -1
  45. data/lib/temporalio/api/history/v1/message.rb +6 -2
  46. data/lib/temporalio/api/namespace/v1/message.rb +1 -1
  47. data/lib/temporalio/api/nexus/v1/message.rb +3 -2
  48. data/lib/temporalio/api/operatorservice/v1/request_response.rb +1 -1
  49. data/lib/temporalio/api/operatorservice/v1/service.rb +1 -1
  50. data/lib/temporalio/api/payload_visitor.rb +162 -7
  51. data/lib/temporalio/api/protocol/v1/message.rb +1 -1
  52. data/lib/temporalio/api/query/v1/message.rb +3 -2
  53. data/lib/temporalio/api/replication/v1/message.rb +1 -1
  54. data/lib/temporalio/api/rules/v1/message.rb +27 -0
  55. data/lib/temporalio/api/schedule/v1/message.rb +2 -2
  56. data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +1 -1
  57. data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +1 -1
  58. data/lib/temporalio/api/sdk/v1/user_metadata.rb +1 -1
  59. data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +1 -1
  60. data/lib/temporalio/api/taskqueue/v1/message.rb +5 -2
  61. data/lib/temporalio/api/testservice/v1/request_response.rb +1 -1
  62. data/lib/temporalio/api/testservice/v1/service.rb +1 -1
  63. data/lib/temporalio/api/update/v1/message.rb +1 -1
  64. data/lib/temporalio/api/version/v1/message.rb +1 -1
  65. data/lib/temporalio/api/worker/v1/message.rb +30 -0
  66. data/lib/temporalio/api/workflow/v1/message.rb +22 -2
  67. data/lib/temporalio/api/workflowservice/v1/request_response.rb +58 -12
  68. data/lib/temporalio/api/workflowservice/v1/service.rb +2 -2
  69. data/lib/temporalio/api.rb +1 -0
  70. data/lib/temporalio/client/async_activity_handle.rb +12 -4
  71. data/lib/temporalio/client/connection/cloud_service.rb +60 -0
  72. data/lib/temporalio/client/connection/workflow_service.rb +343 -28
  73. data/lib/temporalio/client/interceptor.rb +64 -7
  74. data/lib/temporalio/client/schedule.rb +35 -3
  75. data/lib/temporalio/client/with_start_workflow_operation.rb +123 -0
  76. data/lib/temporalio/client/workflow_execution.rb +19 -0
  77. data/lib/temporalio/client/workflow_handle.rb +47 -7
  78. data/lib/temporalio/client/workflow_update_handle.rb +9 -3
  79. data/lib/temporalio/client.rb +231 -4
  80. data/lib/temporalio/common_enums.rb +14 -0
  81. data/lib/temporalio/contrib/open_telemetry.rb +474 -0
  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 +2 -1
  94. data/lib/temporalio/internal/bridge/3.2/temporalio_bridge.so +0 -0
  95. data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.so +0 -0
  96. data/lib/temporalio/internal/bridge/3.4/temporalio_bridge.so +0 -0
  97. data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +1 -1
  98. data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +3 -2
  99. data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +1 -1
  100. data/lib/temporalio/internal/bridge/api/common/common.rb +3 -2
  101. data/lib/temporalio/internal/bridge/api/core_interface.rb +1 -1
  102. data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +1 -1
  103. data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +3 -2
  104. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +2 -2
  105. data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +3 -2
  106. data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +3 -2
  107. data/lib/temporalio/internal/bridge/runtime.rb +3 -0
  108. data/lib/temporalio/internal/bridge/testing.rb +3 -0
  109. data/lib/temporalio/internal/bridge/worker.rb +28 -4
  110. data/lib/temporalio/internal/bridge.rb +1 -1
  111. data/lib/temporalio/internal/client/implementation.rb +281 -51
  112. data/lib/temporalio/internal/proto_utils.rb +38 -6
  113. data/lib/temporalio/internal/worker/activity_worker.rb +112 -27
  114. data/lib/temporalio/internal/worker/multi_runner.rb +2 -2
  115. data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +8 -6
  116. data/lib/temporalio/internal/worker/workflow_instance/context.rb +100 -5
  117. data/lib/temporalio/internal/worker/workflow_instance/details.rb +7 -2
  118. data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +2 -2
  119. data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +64 -18
  120. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +39 -40
  121. data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +22 -2
  122. data/lib/temporalio/internal/worker/workflow_instance.rb +134 -55
  123. data/lib/temporalio/internal/worker/workflow_worker.rb +74 -21
  124. data/lib/temporalio/priority.rb +59 -0
  125. data/lib/temporalio/runtime/metric_buffer.rb +94 -0
  126. data/lib/temporalio/runtime.rb +48 -10
  127. data/lib/temporalio/search_attributes.rb +13 -0
  128. data/lib/temporalio/testing/activity_environment.rb +59 -16
  129. data/lib/temporalio/testing/workflow_environment.rb +29 -6
  130. data/lib/temporalio/version.rb +1 -1
  131. data/lib/temporalio/versioning_override.rb +56 -0
  132. data/lib/temporalio/worker/deployment_options.rb +45 -0
  133. data/lib/temporalio/worker/illegal_workflow_call_validator.rb +64 -0
  134. data/lib/temporalio/worker/interceptor.rb +16 -1
  135. data/lib/temporalio/worker/poller_behavior.rb +61 -0
  136. data/lib/temporalio/worker/thread_pool.rb +6 -6
  137. data/lib/temporalio/worker/tuner.rb +38 -0
  138. data/lib/temporalio/worker/workflow_executor/thread_pool.rb +14 -8
  139. data/lib/temporalio/worker/workflow_executor.rb +1 -1
  140. data/lib/temporalio/worker/workflow_replayer.rb +349 -0
  141. data/lib/temporalio/worker.rb +117 -75
  142. data/lib/temporalio/worker_deployment_version.rb +67 -0
  143. data/lib/temporalio/workflow/child_workflow_handle.rb +10 -2
  144. data/lib/temporalio/workflow/definition.rb +217 -35
  145. data/lib/temporalio/workflow/external_workflow_handle.rb +3 -1
  146. data/lib/temporalio/workflow/future.rb +2 -2
  147. data/lib/temporalio/workflow/info.rb +26 -1
  148. data/lib/temporalio/workflow.rb +119 -15
  149. data/lib/temporalio/workflow_history.rb +26 -1
  150. data/lib/temporalio.rb +1 -0
  151. data/temporalio.gemspec +3 -1
  152. metadata +34 -4
@@ -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'
@@ -23,6 +24,7 @@ require 'temporalio/internal/worker/workflow_instance/scheduler'
23
24
  require 'temporalio/retry_policy'
24
25
  require 'temporalio/scoped_logger'
25
26
  require 'temporalio/worker/interceptor'
27
+ require 'temporalio/worker_deployment_version'
26
28
  require 'temporalio/workflow/info'
27
29
  require 'temporalio/workflow/update_info'
28
30
  require 'timeout'
@@ -53,9 +55,10 @@ module Temporalio
53
55
  attr_reader :context, :logger, :info, :scheduler, :disable_eager_activity_execution, :pending_activities,
54
56
  :pending_timers, :pending_child_workflow_starts, :pending_child_workflows,
55
57
  :pending_external_signals, :pending_external_cancels, :in_progress_handlers, :payload_converter,
56
- :failure_converter, :cancellation, :continue_as_new_suggested, :current_history_length,
57
- :current_history_size, :replaying, :random, :signal_handlers, :query_handlers, :update_handlers,
58
- :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
61
+ attr_accessor :io_enabled, :current_details
59
62
 
60
63
  def initialize(details)
61
64
  # Initialize general state
@@ -66,6 +69,7 @@ module Temporalio
66
69
  @logger = ReplaySafeLogger.new(logger: details.logger, instance: self)
67
70
  @logger.scoped_values_getter = proc { scoped_logger_info }
68
71
  @runtime_metric_meter = details.metric_meter
72
+ @io_enabled = details.unsafe_workflow_io_enabled
69
73
  @scheduler = Scheduler.new(self)
70
74
  @payload_converter = details.payload_converter
71
75
  @failure_converter = details.failure_converter
@@ -87,7 +91,7 @@ module Temporalio
87
91
  @current_history_length = 0
88
92
  @current_history_size = 0
89
93
  @replaying = false
90
- @failure_exception_types = details.workflow_failure_exception_types + @definition.failure_exception_types
94
+ @workflow_failure_exception_types = details.workflow_failure_exception_types
91
95
  @signal_handlers = HandlerHash.new(
92
96
  details.definition.signals,
93
97
  Workflow::Definition::Signal
@@ -104,6 +108,12 @@ module Temporalio
104
108
  end
105
109
  @query_handlers = HandlerHash.new(details.definition.queries, Workflow::Definition::Query)
106
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
107
117
 
108
118
  # Create all things needed from initial job
109
119
  @init_job = details.initial_activation.jobs.find { |j| !j.initialize_workflow.nil? }&.initialize_workflow
@@ -115,6 +125,7 @@ module Temporalio
115
125
  continued_run_id: ProtoUtils.string_or(@init_job.continued_from_execution_run_id),
116
126
  cron_schedule: ProtoUtils.string_or(@init_job.cron_schedule),
117
127
  execution_timeout: ProtoUtils.duration_to_seconds(@init_job.workflow_execution_timeout),
128
+ headers: ProtoUtils.headers_from_proto_map(@init_job.headers, @payload_converter) || {},
118
129
  last_failure: if @init_job.continued_failure
119
130
  @failure_converter.from_failure(@init_job.continued_failure, @payload_converter)
120
131
  end,
@@ -129,10 +140,17 @@ module Temporalio
129
140
  workflow_id: @init_job.parent_workflow_info.workflow_id
130
141
  )
131
142
  end,
143
+ priority: Priority._from_proto(@init_job.priority),
132
144
  retry_policy: (RetryPolicy._from_proto(@init_job.retry_policy) if @init_job.retry_policy),
145
+ root: if @init_job.root_workflow
146
+ Workflow::Info::RootInfo.new(
147
+ run_id: @init_job.root_workflow.run_id,
148
+ workflow_id: @init_job.root_workflow.workflow_id
149
+ )
150
+ end,
133
151
  run_id: details.initial_activation.run_id,
134
152
  run_timeout: ProtoUtils.duration_to_seconds(@init_job.workflow_run_timeout),
135
- start_time: ProtoUtils.timestamp_to_time(details.initial_activation.timestamp) || raise,
153
+ start_time: ProtoUtils.timestamp_to_time(@init_job.start_time) || raise,
136
154
  task_queue: details.task_queue,
137
155
  task_timeout: ProtoUtils.duration_to_seconds(@init_job.workflow_task_timeout) || raise,
138
156
  workflow_id: @init_job.workflow_id,
@@ -228,6 +246,9 @@ module Temporalio
228
246
  @commands = []
229
247
  @current_activation_error = nil
230
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
+ )
231
252
  @current_history_length = activation.history_length
232
253
  @current_history_size = activation.history_size_bytes
233
254
  @replaying = activation.is_replaying
@@ -277,7 +298,9 @@ module Temporalio
277
298
  else
278
299
  Bridge::Api::WorkflowCompletion::WorkflowActivationCompletion.new(
279
300
  run_id: activation.run_id,
280
- 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
+ )
281
304
  )
282
305
  end
283
306
  ensure
@@ -289,7 +312,8 @@ module Temporalio
289
312
  # Convert workflow arguments
290
313
  @workflow_arguments = convert_args(payload_array: @init_job.arguments,
291
314
  method_name: :execute,
292
- raw_args: @definition.raw_args)
315
+ raw_args: @definition.raw_args,
316
+ arg_hints: @definition.arg_hints)
293
317
 
294
318
  # Initialize interceptors
295
319
  @inbound = @interceptors.reverse_each.reduce(InboundImplementation.new(self)) do |acc, int|
@@ -298,11 +322,24 @@ module Temporalio
298
322
  @inbound.init(OutboundImplementation.new(self))
299
323
 
300
324
  # Create the user instance
301
- if @definition.init
302
- @definition.workflow_class.new(*@workflow_arguments)
303
- else
304
- @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
305
340
  end
341
+
342
+ instance
306
343
  end
307
344
 
308
345
  def apply(job)
@@ -348,7 +385,10 @@ module Temporalio
348
385
  end
349
386
 
350
387
  def apply_signal(job)
351
- defn = signal_handlers[job.signal_name] || signal_handlers[nil]
388
+ # Get signal definition, falling back to dynamic if not present and not reserved
389
+ defn = signal_handlers[job.signal_name]
390
+ defn = signal_handlers[nil] if !defn && !Internal::ProtoUtils.reserved_name?(job.signal_name)
391
+
352
392
  handler_exec =
353
393
  if defn
354
394
  HandlerExecution.new(name: job.signal_name, update_id: nil, unfinished_policy: defn.unfinished_policy)
@@ -381,43 +421,49 @@ module Temporalio
381
421
  end
382
422
 
383
423
  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
424
+ result_hint = nil
394
425
  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
426
+ # If it's a built-in, run it without interceptors, otherwise do normal behavior
427
+ result = if job.query_type == '__stack_trace'
428
+ # Use raw value built from default converter because we don't want to use user-conversion
429
+ Converters::RawValue.new(Converters::PayloadConverter.default.to_payload(scheduler.stack_trace))
430
+ elsif job.query_type == '__temporal_workflow_metadata'
431
+ # Use raw value built from default converter because we don't want to use user-conversion
432
+ Converters::RawValue.new(Converters::PayloadConverter.default.to_payload(workflow_metadata))
433
+ else
434
+ # Get query definition, falling back to dynamic if not present and not reserved
435
+ defn = query_handlers[job.query_type]
436
+ defn = query_handlers[nil] if !defn && !Internal::ProtoUtils.reserved_name?(job.query_type)
437
+
438
+ unless defn
439
+ raise "Query handler for #{job.query_type} expected but not found, " \
440
+ "known queries: [#{query_handlers.keys.compact.sort.join(', ')}]"
441
+ end
442
+ result_hint = defn.result_hint
443
+
444
+ with_context_frozen do
445
+ @inbound.handle_query(
446
+ Temporalio::Worker::Interceptor::Workflow::HandleQueryInput.new(
447
+ id: job.query_id,
448
+ query: job.query_type,
449
+ args: begin
450
+ convert_handler_args(payload_array: job.arguments, defn:)
451
+ rescue StandardError => e
452
+ raise "Failed converting query input arguments: #{e}"
453
+ end,
454
+ definition: defn,
455
+ headers: ProtoUtils.headers_from_proto_map(job.headers, @payload_converter) || {}
456
+ )
457
+ )
458
+ end
459
+ end
399
460
 
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
461
  add_command(
416
462
  Bridge::Api::WorkflowCommands::WorkflowCommand.new(
417
463
  respond_to_query: Bridge::Api::WorkflowCommands::QueryResult.new(
418
464
  query_id: job.query_id,
419
465
  succeeded: Bridge::Api::WorkflowCommands::QuerySuccess.new(
420
- response: @payload_converter.to_payload(result)
466
+ response: @payload_converter.to_payload(result, hint: result_hint)
421
467
  )
422
468
  )
423
469
  )
@@ -435,7 +481,10 @@ module Temporalio
435
481
  end
436
482
 
437
483
  def apply_update(job)
438
- defn = update_handlers[job.name] || update_handlers[nil]
484
+ # Get update definition, falling back to dynamic if not present and not reserved
485
+ defn = update_handlers[job.name]
486
+ defn = update_handlers[nil] if !defn && !Internal::ProtoUtils.reserved_name?(job.name)
487
+
439
488
  handler_exec =
440
489
  (HandlerExecution.new(name: job.name, update_id: job.id, unfinished_policy: defn.unfinished_policy) if defn)
441
490
  schedule(handler_exec:) do
@@ -503,7 +552,7 @@ module Temporalio
503
552
  Bridge::Api::WorkflowCommands::WorkflowCommand.new(
504
553
  update_response: Bridge::Api::WorkflowCommands::UpdateResponse.new(
505
554
  protocol_instance_id: job.protocol_instance_id,
506
- completed: @payload_converter.to_payload(result)
555
+ completed: @payload_converter.to_payload(result, hint: defn.result_hint)
507
556
  )
508
557
  )
509
558
  )
@@ -527,13 +576,13 @@ module Temporalio
527
576
  result = @inbound.execute(
528
577
  Temporalio::Worker::Interceptor::Workflow::ExecuteInput.new(
529
578
  args: @workflow_arguments,
530
- headers: ProtoUtils.headers_from_proto_map(@init_job.headers, @payload_converter) || {}
579
+ headers: @info.headers
531
580
  )
532
581
  )
533
582
  add_command(
534
583
  Bridge::Api::WorkflowCommands::WorkflowCommand.new(
535
584
  complete_workflow_execution: Bridge::Api::WorkflowCommands::CompleteWorkflowExecution.new(
536
- result: @payload_converter.to_payload(result)
585
+ result: @payload_converter.to_payload(result, hint: @definition.result_hint)
537
586
  )
538
587
  )
539
588
  )
@@ -561,14 +610,19 @@ module Temporalio
561
610
  def on_top_level_exception(err)
562
611
  if err.is_a?(Workflow::ContinueAsNewError)
563
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
564
619
  add_command(
565
620
  Bridge::Api::WorkflowCommands::WorkflowCommand.new(
566
621
  continue_as_new_workflow_execution: Bridge::Api::WorkflowCommands::ContinueAsNewWorkflowExecution.new(
567
- workflow_type: if err.workflow
568
- Workflow::Definition._workflow_type_from_workflow_parameter(err.workflow)
569
- end,
622
+ workflow_type:,
570
623
  task_queue: err.task_queue,
571
- 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),
572
626
  workflow_run_timeout: ProtoUtils.seconds_to_duration(err.run_timeout),
573
627
  workflow_task_timeout: ProtoUtils.seconds_to_duration(err.task_timeout),
574
628
  memo: ProtoUtils.memo_to_proto_hash(err.memo, payload_converter),
@@ -606,9 +660,9 @@ module Temporalio
606
660
  end
607
661
 
608
662
  def failure_exception?(err)
609
- err.is_a?(Error::Failure) || err.is_a?(Timeout::Error) || @failure_exception_types.any? do |cls|
610
- err.is_a?(cls)
611
- 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) }
612
666
  end
613
667
 
614
668
  def with_context_frozen(&)
@@ -623,11 +677,12 @@ module Temporalio
623
677
  payload_array:,
624
678
  method_name: defn.to_invoke.is_a?(Symbol) ? defn.to_invoke : nil,
625
679
  raw_args: defn.raw_args,
680
+ arg_hints: defn.arg_hints,
626
681
  ignore_first_param: defn.name.nil? # Dynamic
627
682
  )
628
683
  end
629
684
 
630
- 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)
631
686
  # Just in case it is not an array
632
687
  payload_array = payload_array.to_ary
633
688
 
@@ -667,10 +722,34 @@ module Temporalio
667
722
  if raw_args
668
723
  payload_array.map { |p| Converters::RawValue.new(p) }
669
724
  else
670
- ProtoUtils.convert_from_payload_array(@payload_converter, payload_array)
725
+ ProtoUtils.convert_from_payload_array(@payload_converter, payload_array, hints: arg_hints)
671
726
  end
672
727
  end
673
728
 
729
+ def workflow_metadata
730
+ Temporalio::Api::Sdk::V1::WorkflowMetadata.new(
731
+ definition: Temporalio::Api::Sdk::V1::WorkflowDefinition.new(
732
+ type: info.workflow_type,
733
+ query_definitions: query_handlers.values.map do |defn|
734
+ Temporalio::Api::Sdk::V1::WorkflowInteractionDefinition.new(
735
+ name: defn.name || '', description: defn.description || ''
736
+ )
737
+ end,
738
+ signal_definitions: signal_handlers.values.map do |defn|
739
+ Temporalio::Api::Sdk::V1::WorkflowInteractionDefinition.new(
740
+ name: defn.name || '', description: defn.description || ''
741
+ )
742
+ end,
743
+ update_definitions: update_handlers.values.map do |defn|
744
+ Temporalio::Api::Sdk::V1::WorkflowInteractionDefinition.new(
745
+ name: defn.name || '', description: defn.description || ''
746
+ )
747
+ end
748
+ ),
749
+ current_details: current_details || ''
750
+ )
751
+ end
752
+
674
753
  def scoped_logger_info
675
754
  @scoped_logger_info ||= {
676
755
  attempt: info.attempt,
@@ -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,15 +29,58 @@ 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
35
41
 
36
- def initialize(worker:, bridge_worker:, workflow_definitions:)
37
- @executor = worker.options.workflow_executor
42
+ def self.bridge_workflow_failure_exception_type_options(
43
+ workflow_failure_exception_types:,
44
+ workflow_definitions:
45
+ )
46
+ as_fail = workflow_failure_exception_types.any? do |t|
47
+ t.is_a?(Class) && t >= Workflow::NondeterminismError
48
+ end
49
+ as_fail_for_types = workflow_definitions.values.map do |defn|
50
+ next unless defn.failure_exception_types.any? { |t| t.is_a?(Class) && t >= Workflow::NondeterminismError }
38
51
 
39
- payload_codec = worker.options.client.data_converter.payload_codec
40
- @workflow_payload_codec_thread_pool = worker.options.workflow_payload_codec_thread_pool
52
+ # If they tried to do this on a dynamic workflow and haven't already set worker-level option, warn
53
+ unless defn.name || as_fail
54
+ warn('Note, dynamic workflows cannot trap non-determinism errors, so worker-level ' \
55
+ 'workflow_failure_exception_types should be set to capture that if that is the intention')
56
+ end
57
+ defn.name
58
+ end.compact
59
+ [as_fail, as_fail_for_types]
60
+ end
61
+
62
+ def initialize(
63
+ bridge_worker:,
64
+ namespace:,
65
+ task_queue:,
66
+ workflow_definitions:,
67
+ workflow_executor:,
68
+ logger:,
69
+ data_converter:,
70
+ metric_meter:,
71
+ workflow_interceptors:,
72
+ disable_eager_activity_execution:,
73
+ illegal_workflow_calls:,
74
+ workflow_failure_exception_types:,
75
+ workflow_payload_codec_thread_pool:,
76
+ unsafe_workflow_io_enabled:,
77
+ debug_mode:,
78
+ assert_valid_local_activity:, on_eviction: nil
79
+ )
80
+ @executor = workflow_executor
81
+
82
+ payload_codec = data_converter.payload_codec
83
+ @workflow_payload_codec_thread_pool = workflow_payload_codec_thread_pool
41
84
  if !Fiber.current_scheduler && payload_codec && !@workflow_payload_codec_thread_pool
42
85
  raise ArgumentError, 'Must have workflow payload codec thread pool if providing codec and not using fibers'
43
86
  end
@@ -55,29 +98,32 @@ module Temporalio
55
98
  @state = State.new(
56
99
  workflow_definitions:,
57
100
  bridge_worker:,
58
- logger: worker.options.logger,
59
- metric_meter: worker.options.client.connection.options.runtime.metric_meter,
60
- data_converter: worker.options.client.data_converter,
61
- deadlock_timeout: worker.options.debug_mode ? nil : 2.0,
101
+ logger:,
102
+ metric_meter:,
103
+ data_converter:,
104
+ deadlock_timeout: debug_mode ? nil : 2.0,
62
105
  # TODO(cretz): Make this more performant for the default set?
63
106
  illegal_calls: WorkflowInstance::IllegalCallTracer.frozen_validated_illegal_calls(
64
- worker.options.illegal_workflow_calls || {}
107
+ illegal_workflow_calls || {}
65
108
  ),
66
- namespace: worker.options.client.namespace,
67
- task_queue: worker.options.task_queue,
68
- disable_eager_activity_execution: worker.options.disable_eager_activity_execution,
69
- workflow_interceptors: worker._workflow_interceptors,
70
- workflow_failure_exception_types: worker.options.workflow_failure_exception_types.map do |t|
71
- unless t.is_a?(Class) && t < Exception
109
+ namespace:,
110
+ task_queue:,
111
+ disable_eager_activity_execution:,
112
+ workflow_interceptors:,
113
+ workflow_failure_exception_types: workflow_failure_exception_types.map do |t|
114
+ unless t.is_a?(Class) && t <= Exception
72
115
  raise ArgumentError, 'All failure types must classes inheriting Exception'
73
116
  end
74
117
 
75
118
  t
76
- end.freeze
119
+ end.freeze,
120
+ unsafe_workflow_io_enabled:,
121
+ assert_valid_local_activity:
77
122
  )
123
+ @state.on_eviction = on_eviction if on_eviction
78
124
 
79
125
  # Validate worker
80
- @executor._validate_worker(worker, @state)
126
+ @executor._validate_worker(self, @state)
81
127
  end
82
128
 
83
129
  def handle_activation(runner:, activation:, decoded:)
@@ -147,12 +193,16 @@ module Temporalio
147
193
  class State
148
194
  attr_reader :workflow_definitions, :bridge_worker, :logger, :metric_meter, :data_converter, :deadlock_timeout,
149
195
  :illegal_calls, :namespace, :task_queue, :disable_eager_activity_execution,
150
- :workflow_interceptors, :workflow_failure_exception_types
196
+ :workflow_interceptors, :workflow_failure_exception_types, :unsafe_workflow_io_enabled,
197
+ :assert_valid_local_activity
198
+
199
+ attr_writer :on_eviction
151
200
 
152
201
  def initialize(
153
202
  workflow_definitions:, bridge_worker:, logger:, metric_meter:, data_converter:, deadlock_timeout:,
154
203
  illegal_calls:, namespace:, task_queue:, disable_eager_activity_execution:,
155
- workflow_interceptors:, workflow_failure_exception_types:
204
+ workflow_interceptors:, workflow_failure_exception_types:, unsafe_workflow_io_enabled:,
205
+ assert_valid_local_activity:
156
206
  )
157
207
  @workflow_definitions = workflow_definitions
158
208
  @bridge_worker = bridge_worker
@@ -166,6 +216,8 @@ module Temporalio
166
216
  @disable_eager_activity_execution = disable_eager_activity_execution
167
217
  @workflow_interceptors = workflow_interceptors
168
218
  @workflow_failure_exception_types = workflow_failure_exception_types
219
+ @unsafe_workflow_io_enabled = unsafe_workflow_io_enabled
220
+ @assert_valid_local_activity = assert_valid_local_activity
169
221
 
170
222
  @running_workflows = {}
171
223
  @running_workflows_mutex = Mutex.new
@@ -182,8 +234,9 @@ module Temporalio
182
234
  instance
183
235
  end
184
236
 
185
- def evict_running_workflow(run_id)
237
+ def evict_running_workflow(run_id, cache_remove_job)
186
238
  @running_workflows_mutex.synchronize { @running_workflows.delete(run_id) }
239
+ @on_eviction&.call(run_id, cache_remove_job)
187
240
  end
188
241
 
189
242
  def evict_all
@@ -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
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Temporalio
4
+ class Runtime
5
+ # Metric buffer for use with a runtime to capture metrics. Only one metric buffer can be associated with a runtime
6
+ # and {retrieve_updates} cannot be called before the runtime is created. Once runtime created, users should
7
+ # regularly call {retrieve_updates} to drain the buffer.
8
+ #
9
+ # @note WARNING: It is important that the buffer size is set to a high number and that {retrieve_updates} is called
10
+ # regularly to drain the buffer. If the buffer is full, metric updates will be dropped and an error will be
11
+ # logged.
12
+ class MetricBuffer
13
+ # Enumerates for the duration format.
14
+ module DurationFormat
15
+ # Durations are millisecond integers.
16
+ MILLISECONDS = :milliseconds
17
+
18
+ # Durations are second floats.
19
+ SECONDS = :seconds
20
+ end
21
+
22
+ Update = Data.define(:metric, :value, :attributes)
23
+
24
+ # Metric buffer update.
25
+ #
26
+ # @note WARNING: The constructor of this class should not be invoked by users and may change in incompatible ways
27
+ # in the future.
28
+ #
29
+ # @!attribute metric
30
+ # @return [Metric] Metric for this update. For performance reasons, this is created lazily on first use and is
31
+ # the same object each time an update on this metric exists.
32
+ # @!attribute value
33
+ # @return [Integer, Float] Metric value for this update.
34
+ # @!attribute attributes
35
+ # @return [Hash{String => String, Integer, Float, Boolean}] Attributes for this value as a frozen hash.
36
+ # For performance reasons this is sometimes the same hash if the attribute set is reused at a metric level.
37
+ class Update # rubocop:disable Lint/EmptyClass
38
+ # DEV NOTE: This class is instantiated inside Rust, be careful changing it.
39
+ end
40
+
41
+ Metric = Data.define(:name, :description, :unit, :kind)
42
+
43
+ # Metric definition present on an update.
44
+ #
45
+ # @!attribute name
46
+ # @return [String] Name of the metric.
47
+ # @!attribute description
48
+ # @return [String, nil] Description of the metric if any.
49
+ # @!attribute unit
50
+ # @return [String, nil] Unit of the metric if any.
51
+ # @!attribute kind
52
+ # @return [:counter, :histogram, :gauge] Kind of the metric.
53
+ class Metric # rubocop:disable Lint/EmptyClass
54
+ # DEV NOTE: This class is instantiated inside Rust, be careful changing it.
55
+ end
56
+
57
+ # Create a metric buffer with the given size.
58
+ #
59
+ # @note WARNING: It is important that the buffer size is set to a high number and is drained regularly. See
60
+ # {MetricBuffer} warning.
61
+ #
62
+ # @param buffer_size [Integer] Maximum size of the buffer before metrics will be dropped.
63
+ # @param duration_format [DurationFormat] How durations are represented.
64
+ def initialize(buffer_size, duration_format: DurationFormat::MILLISECONDS)
65
+ @buffer_size = buffer_size
66
+ @duration_format = duration_format
67
+ @runtime = nil
68
+ end
69
+
70
+ # Drain the buffer and return all metric updates.
71
+ #
72
+ # @note WARNING: It is important that this is called regularly. See {MetricBuffer} warning.
73
+ #
74
+ # @return [Array<Update>] Updates since last time this was called.
75
+ def retrieve_updates
76
+ raise 'Attempting to retrieve updates before runtime created' unless @runtime
77
+
78
+ @runtime._core_runtime.retrieve_buffered_metrics(@duration_format == DurationFormat::SECONDS)
79
+ end
80
+
81
+ # @!visibility private
82
+ def _buffer_size
83
+ @buffer_size
84
+ end
85
+
86
+ # @!visibility private
87
+ def _set_runtime(runtime)
88
+ raise 'Metric buffer already attached to a runtime' if @runtime
89
+
90
+ @runtime = runtime
91
+ end
92
+ end
93
+ end
94
+ end