temporalio 0.4.0-arm64-darwin → 0.6.0-arm64-darwin

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 (145) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/lib/temporalio/activity/cancellation_details.rb +58 -0
  4. data/lib/temporalio/activity/context.rb +10 -1
  5. data/lib/temporalio/activity/definition.rb +41 -3
  6. data/lib/temporalio/activity/info.rb +25 -4
  7. data/lib/temporalio/activity.rb +2 -0
  8. data/lib/temporalio/api/activity/v1/message.rb +1 -1
  9. data/lib/temporalio/api/batch/v1/message.rb +7 -2
  10. data/lib/temporalio/api/cloud/account/v1/message.rb +1 -1
  11. data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +22 -2
  12. data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +2 -2
  13. data/lib/temporalio/api/cloud/connectivityrule/v1/message.rb +29 -0
  14. data/lib/temporalio/api/cloud/identity/v1/message.rb +7 -2
  15. data/lib/temporalio/api/cloud/namespace/v1/message.rb +7 -2
  16. data/lib/temporalio/api/cloud/nexus/v1/message.rb +3 -2
  17. data/lib/temporalio/api/cloud/operation/v1/message.rb +2 -2
  18. data/lib/temporalio/api/cloud/region/v1/message.rb +1 -1
  19. data/lib/temporalio/api/cloud/resource/v1/message.rb +1 -1
  20. data/lib/temporalio/api/cloud/sink/v1/message.rb +1 -1
  21. data/lib/temporalio/api/cloud/usage/v1/message.rb +1 -1
  22. data/lib/temporalio/api/command/v1/message.rb +2 -2
  23. data/lib/temporalio/api/common/v1/grpc_status.rb +1 -1
  24. data/lib/temporalio/api/common/v1/message.rb +4 -2
  25. data/lib/temporalio/api/deployment/v1/message.rb +3 -2
  26. data/lib/temporalio/api/enums/v1/batch_operation.rb +2 -2
  27. data/lib/temporalio/api/enums/v1/command_type.rb +1 -1
  28. data/lib/temporalio/api/enums/v1/common.rb +5 -2
  29. data/lib/temporalio/api/enums/v1/deployment.rb +3 -2
  30. data/lib/temporalio/api/enums/v1/event_type.rb +2 -2
  31. data/lib/temporalio/api/enums/v1/failed_cause.rb +2 -2
  32. data/lib/temporalio/api/enums/v1/namespace.rb +1 -1
  33. data/lib/temporalio/api/enums/v1/nexus.rb +1 -1
  34. data/lib/temporalio/api/enums/v1/query.rb +1 -1
  35. data/lib/temporalio/api/enums/v1/reset.rb +1 -1
  36. data/lib/temporalio/api/enums/v1/schedule.rb +1 -1
  37. data/lib/temporalio/api/enums/v1/task_queue.rb +3 -2
  38. data/lib/temporalio/api/enums/v1/update.rb +1 -1
  39. data/lib/temporalio/api/enums/v1/workflow.rb +2 -2
  40. data/lib/temporalio/api/errordetails/v1/message.rb +1 -1
  41. data/lib/temporalio/api/export/v1/message.rb +1 -1
  42. data/lib/temporalio/api/failure/v1/message.rb +3 -2
  43. data/lib/temporalio/api/filter/v1/message.rb +1 -1
  44. data/lib/temporalio/api/history/v1/message.rb +4 -2
  45. data/lib/temporalio/api/namespace/v1/message.rb +1 -1
  46. data/lib/temporalio/api/nexus/v1/message.rb +2 -2
  47. data/lib/temporalio/api/operatorservice/v1/request_response.rb +1 -1
  48. data/lib/temporalio/api/operatorservice/v1/service.rb +1 -1
  49. data/lib/temporalio/api/payload_visitor.rb +106 -1
  50. data/lib/temporalio/api/protocol/v1/message.rb +1 -1
  51. data/lib/temporalio/api/query/v1/message.rb +1 -1
  52. data/lib/temporalio/api/replication/v1/message.rb +1 -1
  53. data/lib/temporalio/api/rules/v1/message.rb +27 -0
  54. data/lib/temporalio/api/schedule/v1/message.rb +2 -2
  55. data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +1 -1
  56. data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +1 -1
  57. data/lib/temporalio/api/sdk/v1/user_metadata.rb +1 -1
  58. data/lib/temporalio/api/sdk/v1/worker_config.rb +23 -0
  59. data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +1 -1
  60. data/lib/temporalio/api/taskqueue/v1/message.rb +6 -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 +31 -0
  66. data/lib/temporalio/api/workflow/v1/message.rb +14 -2
  67. data/lib/temporalio/api/workflowservice/v1/request_response.rb +28 -2
  68. data/lib/temporalio/api/workflowservice/v1/service.rb +2 -2
  69. data/lib/temporalio/cancellation.rb +16 -12
  70. data/lib/temporalio/client/async_activity_handle.rb +12 -4
  71. data/lib/temporalio/client/connection/cloud_service.rb +135 -0
  72. data/lib/temporalio/client/connection/workflow_service.rb +150 -0
  73. data/lib/temporalio/client/connection.rb +2 -1
  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 +7 -7
  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 +24 -7
  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/3.2/temporalio_bridge.bundle +0 -0
  95. data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.bundle +0 -0
  96. data/lib/temporalio/internal/bridge/3.4/temporalio_bridge.bundle +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 +1 -1
  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 +1 -1
  107. data/lib/temporalio/internal/bridge/worker.rb +28 -4
  108. data/lib/temporalio/internal/bridge.rb +1 -1
  109. data/lib/temporalio/internal/client/implementation.rb +60 -52
  110. data/lib/temporalio/internal/proto_utils.rb +4 -4
  111. data/lib/temporalio/internal/worker/activity_worker.rb +93 -20
  112. data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +8 -6
  113. data/lib/temporalio/internal/worker/workflow_instance/context.rb +66 -24
  114. data/lib/temporalio/internal/worker/workflow_instance/details.rb +5 -2
  115. data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +2 -2
  116. data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +2 -0
  117. data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +64 -18
  118. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +28 -14
  119. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +5 -2
  120. data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +10 -4
  121. data/lib/temporalio/internal/worker/workflow_instance.rb +58 -23
  122. data/lib/temporalio/internal/worker/workflow_worker.rb +16 -6
  123. data/lib/temporalio/priority.rb +100 -0
  124. data/lib/temporalio/scoped_logger.rb +1 -1
  125. data/lib/temporalio/testing/activity_environment.rb +17 -2
  126. data/lib/temporalio/testing/workflow_environment.rb +3 -3
  127. data/lib/temporalio/version.rb +1 -1
  128. data/lib/temporalio/versioning_override.rb +56 -0
  129. data/lib/temporalio/worker/deployment_options.rb +45 -0
  130. data/lib/temporalio/worker/illegal_workflow_call_validator.rb +73 -0
  131. data/lib/temporalio/worker/interceptor.rb +13 -1
  132. data/lib/temporalio/worker/poller_behavior.rb +61 -0
  133. data/lib/temporalio/worker/thread_pool.rb +1 -1
  134. data/lib/temporalio/worker/workflow_executor/thread_pool.rb +2 -1
  135. data/lib/temporalio/worker/workflow_replayer.rb +12 -13
  136. data/lib/temporalio/worker.rb +73 -28
  137. data/lib/temporalio/worker_deployment_version.rb +67 -0
  138. data/lib/temporalio/workflow/child_workflow_handle.rb +10 -2
  139. data/lib/temporalio/workflow/definition.rb +187 -39
  140. data/lib/temporalio/workflow/external_workflow_handle.rb +3 -1
  141. data/lib/temporalio/workflow/info.rb +4 -1
  142. data/lib/temporalio/workflow.rb +134 -11
  143. data/lib/temporalio.rb +1 -0
  144. data/temporalio.gemspec +1 -0
  145. metadata +13 -2
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Temporalio
4
+ class Worker
5
+ # Custom validator for validating illegal workflow calls.
6
+ class IllegalWorkflowCallValidator
7
+ CallInfo = Data.define(
8
+ :class_name,
9
+ :method_name,
10
+ :trace_point
11
+ )
12
+
13
+ # Call info passed to the validation block for each validation.
14
+ #
15
+ # @!attribute class_name
16
+ # @return [String] Class name the method is on.
17
+ # @!attribute method_name
18
+ # @return [String] Method name being called.
19
+ # @!attribute trace_point
20
+ # @return [TracePoint] TracePoint instance for the call.
21
+ class CallInfo; end # rubocop:disable Lint/EmptyClass
22
+
23
+ # @return [Array<IllegalWorkflowCallValidator>] Set of advanced validators for Time calls.
24
+ def self.default_time_validators
25
+ @default_time_validators ||= [
26
+ # Do not consider initialize as invalid if year is present and not "true"
27
+ IllegalWorkflowCallValidator.new(method_name: :initialize) do |info|
28
+ year_val = info.trace_point.binding&.local_variable_get(:year)
29
+ raise 'can only use if passing string or explicit time info' unless year_val && year_val != true
30
+ end,
31
+ IllegalWorkflowCallValidator.new(method_name: :now) do
32
+ # When the xmlschema (aliased as iso8601) call is made, zone_offset is called which has a default parameter
33
+ # of Time.now.year. We want to prevent failing in that specific case. It is expensive to access the caller
34
+ # stack, but this is only done in the rare case they are calling this safely.
35
+ next if caller_locations&.any? { |loc| loc.label == 'zone_offset' || loc.label == 'Time.zone_offset' }
36
+
37
+ raise 'Invalid Time.now call'
38
+ end
39
+ ]
40
+ end
41
+
42
+ # @return [IllegalWorkflowCallValidator] Workflow call validator that is tailored to disallow most Mutex calls,
43
+ # but let others through for certain situations.
44
+ def self.known_safe_mutex_validator
45
+ @known_safe_mutex_validator ||= IllegalWorkflowCallValidator.new do
46
+ # Only Google Protobuf use of Mutex is known to be safe, fail unless any caller location path has protobuf
47
+ raise 'disallowed' unless caller_locations&.any? { |loc| loc.path&.include?('google/protobuf/') }
48
+ end
49
+ end
50
+
51
+ # @return [String, nil] Method name if this validator is specific to a method.
52
+ attr_reader :method_name
53
+
54
+ # @return [Proc] Block provided in constructor to invoke. See constructor for more details.
55
+ attr_reader :block
56
+
57
+ # Create a call validator.
58
+ #
59
+ # @param method_name [String, nil] Method name to check. This must be provided if the validator is in an illegal
60
+ # call array, this cannot be provided if it is a top-level class validator.
61
+ # @yield Required block that is called each time validation is needed. If the call raises, the exception message
62
+ # is used as the reason why the call is considered invalid. Return value is ignored.
63
+ # @yieldparam info [CallInfo] Information about the current call.
64
+ def initialize(method_name: nil, &block)
65
+ raise 'Block required' unless block_given?
66
+ raise TypeError, 'Method name must be Symbol' unless method_name.nil? || method_name.is_a?(Symbol)
67
+
68
+ @method_name = method_name
69
+ @block = block
70
+ end
71
+ end
72
+ end
73
+ end
@@ -22,6 +22,7 @@ module Temporalio
22
22
  ExecuteInput = Data.define(
23
23
  :proc,
24
24
  :args,
25
+ :result_hint,
25
26
  :headers
26
27
  )
27
28
 
@@ -59,7 +60,8 @@ module Temporalio
59
60
 
60
61
  # Input for {Outbound.heartbeat}.
61
62
  HeartbeatInput = Data.define(
62
- :details
63
+ :details,
64
+ :detail_hints
63
65
  )
64
66
 
65
67
  # Outbound interceptor for intercepting outbound activity calls. This should be extended by users needing to
@@ -216,6 +218,9 @@ module Temporalio
216
218
  :cancellation_type,
217
219
  :activity_id,
218
220
  :disable_eager_execution,
221
+ :priority,
222
+ :arg_hints,
223
+ :result_hint,
219
224
  :headers
220
225
  )
221
226
 
@@ -231,6 +236,8 @@ module Temporalio
231
236
  :cancellation,
232
237
  :cancellation_type,
233
238
  :activity_id,
239
+ :arg_hints,
240
+ :result_hint,
234
241
  :headers
235
242
  )
236
243
 
@@ -245,6 +252,7 @@ module Temporalio
245
252
  :signal,
246
253
  :args,
247
254
  :cancellation,
255
+ :arg_hints,
248
256
  :headers
249
257
  )
250
258
 
@@ -255,6 +263,7 @@ module Temporalio
255
263
  :signal,
256
264
  :args,
257
265
  :cancellation,
266
+ :arg_hints,
258
267
  :headers
259
268
  )
260
269
 
@@ -284,6 +293,9 @@ module Temporalio
284
293
  :cron_schedule,
285
294
  :memo,
286
295
  :search_attributes,
296
+ :priority,
297
+ :arg_hints,
298
+ :result_hint,
287
299
  :headers
288
300
  )
289
301
 
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Temporalio
4
+ class Worker
5
+ # Base class for poller behaviors that control how polling scales.
6
+ class PollerBehavior
7
+ # @!visibility private
8
+ def _to_bridge_options
9
+ raise NotImplementedError, 'Subclasses must implement this method'
10
+ end
11
+
12
+ # A poller behavior that attempts to poll as long as a slot is available, up to the
13
+ # provided maximum. Cannot be less than two for workflow tasks, or one for other tasks.
14
+ class SimpleMaximum < PollerBehavior
15
+ # @return [Integer] Maximum number of concurrent poll requests.
16
+ attr_reader :maximum
17
+
18
+ # @param maximum [Integer] Maximum number of concurrent poll requests.
19
+ def initialize(maximum)
20
+ super()
21
+ @maximum = maximum
22
+ end
23
+
24
+ # @!visibility private
25
+ def _to_bridge_options
26
+ Internal::Bridge::Worker::PollerBehaviorSimpleMaximum.new(simple_maximum: @maximum)
27
+ end
28
+ end
29
+
30
+ # A poller behavior that automatically scales the number of pollers based on feedback
31
+ # from the server. A slot must be available before beginning polling.
32
+ class Autoscaling < PollerBehavior
33
+ # @return [Integer] Minimum number of poll calls (assuming slots are available).
34
+ attr_reader :minimum
35
+ # @return [Integer] Maximum number of poll calls that will ever be open at once.
36
+ attr_reader :maximum
37
+ # @return [Integer] Number of polls attempted initially before scaling kicks in.
38
+ attr_reader :initial
39
+
40
+ # @param minimum [Integer] Minimum number of poll calls (assuming slots are available).
41
+ # @param maximum [Integer] Maximum number of poll calls that will ever be open at once.
42
+ # @param initial [Integer] Number of polls attempted initially before scaling kicks in.
43
+ def initialize(minimum: 1, maximum: 100, initial: 5)
44
+ super()
45
+ @minimum = minimum
46
+ @maximum = maximum
47
+ @initial = initial
48
+ end
49
+
50
+ # @!visibility private
51
+ def _to_bridge_options
52
+ Internal::Bridge::Worker::PollerBehaviorAutoscaling.new(
53
+ minimum: @minimum,
54
+ maximum: @maximum,
55
+ initial: @initial
56
+ )
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -125,7 +125,7 @@ module Temporalio
125
125
 
126
126
  private
127
127
 
128
- def locked_assign_worker(&block)
128
+ def locked_assign_worker(&block) # rubocop:disable Naming/PredicateMethod
129
129
  # keep growing if the pool is not at the minimum yet
130
130
  worker, = @ready.pop || locked_add_busy_worker
131
131
  if worker
@@ -214,7 +214,8 @@ module Temporalio
214
214
  disable_eager_activity_execution: worker_state.disable_eager_activity_execution,
215
215
  illegal_calls: worker_state.illegal_calls,
216
216
  workflow_failure_exception_types: worker_state.workflow_failure_exception_types,
217
- unsafe_workflow_io_enabled: worker_state.unsafe_workflow_io_enabled
217
+ unsafe_workflow_io_enabled: worker_state.unsafe_workflow_io_enabled,
218
+ assert_valid_local_activity: worker_state.assert_valid_local_activity
218
219
  )
219
220
  )
220
221
  end
@@ -7,6 +7,7 @@ require 'temporalio/internal/bridge/worker'
7
7
  require 'temporalio/internal/worker/multi_runner'
8
8
  require 'temporalio/internal/worker/workflow_worker'
9
9
  require 'temporalio/worker/interceptor'
10
+ require 'temporalio/worker/poller_behavior'
10
11
  require 'temporalio/worker/thread_pool'
11
12
  require 'temporalio/worker/tuner'
12
13
  require 'temporalio/worker/workflow_executor'
@@ -24,7 +25,6 @@ module Temporalio
24
25
  :data_converter,
25
26
  :workflow_executor,
26
27
  :interceptors,
27
- :build_id,
28
28
  :identity,
29
29
  :logger,
30
30
  :illegal_workflow_calls,
@@ -51,9 +51,6 @@ module Temporalio
51
51
  # @param workflow_executor [WorkflowExecutor] Workflow executor that workflow tasks run within. This must be a
52
52
  # {WorkflowExecutor::ThreadPool} currently.
53
53
  # @param interceptors [Array<Interceptor::Workflow>] Workflow interceptors.
54
- # @param build_id [String] Unique identifier for the current runtime. This is best set as a unique value
55
- # representing all code and should change only when code does. This can be something like a git commit hash. If
56
- # unset, default is hash of known Ruby code.
57
54
  # @param identity [String, nil] Override the identity for this replater.
58
55
  # @param logger [Logger] Logger to use. Defaults to stdout with warn level. Callers setting this logger are
59
56
  # responsible for closing it.
@@ -87,7 +84,6 @@ module Temporalio
87
84
  data_converter: Converters::DataConverter.default,
88
85
  workflow_executor: WorkflowExecutor::ThreadPool.default,
89
86
  interceptors: [],
90
- build_id: Worker.default_build_id,
91
87
  identity: nil,
92
88
  logger: Logger.new($stdout, level: Logger::WARN),
93
89
  illegal_workflow_calls: Worker.default_illegal_workflow_calls,
@@ -105,7 +101,6 @@ module Temporalio
105
101
  data_converter:,
106
102
  workflow_executor:,
107
103
  interceptors:,
108
- build_id:,
109
104
  identity:,
110
105
  logger:,
111
106
  illegal_workflow_calls:,
@@ -116,7 +111,9 @@ module Temporalio
116
111
  runtime:
117
112
  ).freeze
118
113
  # Preload definitions and other settings
119
- @workflow_definitions = Internal::Worker::WorkflowWorker.workflow_definitions(workflows)
114
+ @workflow_definitions = Internal::Worker::WorkflowWorker.workflow_definitions(
115
+ workflows, should_enforce_versioning_behavior: false
116
+ )
120
117
  @nondeterminism_as_workflow_fail, @nondeterminism_as_workflow_fail_for_types =
121
118
  Internal::Worker::WorkflowWorker.bridge_workflow_failure_exception_type_options(
122
119
  workflow_failure_exception_types:, workflow_definitions: @workflow_definitions
@@ -207,12 +204,13 @@ module Temporalio
207
204
  tuner: Tuner.create_fixed(
208
205
  workflow_slots: 2, activity_slots: 1, local_activity_slots: 1
209
206
  )._to_bridge_options,
210
- build_id: options.build_id,
211
207
  identity_override: options.identity,
212
208
  max_cached_workflows: 2,
213
- max_concurrent_workflow_task_polls: 1,
209
+ workflow_task_poller_behavior:
210
+ Temporalio::Worker::PollerBehavior::SimpleMaximum.new(2)._to_bridge_options,
214
211
  nonsticky_to_sticky_poll_ratio: 1.0,
215
- max_concurrent_activity_task_polls: 1,
212
+ activity_task_poller_behavior:
213
+ Temporalio::Worker::PollerBehavior::SimpleMaximum.new(1)._to_bridge_options,
216
214
  no_remote_activities: true,
217
215
  sticky_queue_schedule_to_start_timeout: 1.0,
218
216
  max_heartbeat_throttle_interval: 1.0,
@@ -220,9 +218,9 @@ module Temporalio
220
218
  max_worker_activities_per_second: nil,
221
219
  max_task_queue_activities_per_second: nil,
222
220
  graceful_shutdown_period: 0.0,
223
- use_worker_versioning: false,
224
221
  nondeterminism_as_workflow_fail:,
225
- nondeterminism_as_workflow_fail_for_types:
222
+ nondeterminism_as_workflow_fail_for_types:,
223
+ deployment_options: Worker.default_deployment_options._to_bridge_options
226
224
  )
227
225
  )
228
226
 
@@ -245,7 +243,8 @@ module Temporalio
245
243
  workflow_payload_codec_thread_pool: options.workflow_payload_codec_thread_pool,
246
244
  unsafe_workflow_io_enabled: options.unsafe_workflow_io_enabled,
247
245
  debug_mode: options.debug_mode,
248
- on_eviction: proc { |_, remove_job| @last_workflow_remove_job = remove_job } # steep:ignore
246
+ on_eviction: proc { |_, remove_job| @last_workflow_remove_job = remove_job }, # steep:ignore
247
+ assert_valid_local_activity: ->(_) {}
249
248
  )
250
249
 
251
250
  # Create the runner
@@ -12,7 +12,10 @@ require 'temporalio/internal/worker/multi_runner'
12
12
  require 'temporalio/internal/worker/workflow_instance'
13
13
  require 'temporalio/internal/worker/workflow_worker'
14
14
  require 'temporalio/worker/activity_executor'
15
+ require 'temporalio/worker/deployment_options'
16
+ require 'temporalio/worker/illegal_workflow_call_validator'
15
17
  require 'temporalio/worker/interceptor'
18
+ require 'temporalio/worker/poller_behavior'
16
19
  require 'temporalio/worker/thread_pool'
17
20
  require 'temporalio/worker/tuner'
18
21
  require 'temporalio/worker/workflow_executor'
@@ -33,7 +36,6 @@ module Temporalio
33
36
  :activity_executors,
34
37
  :workflow_executor,
35
38
  :interceptors,
36
- :build_id,
37
39
  :identity,
38
40
  :logger,
39
41
  :max_cached_workflows,
@@ -47,12 +49,14 @@ module Temporalio
47
49
  :max_activities_per_second,
48
50
  :max_task_queue_activities_per_second,
49
51
  :graceful_shutdown_period,
50
- :use_worker_versioning,
51
52
  :disable_eager_activity_execution,
52
53
  :illegal_workflow_calls,
53
54
  :workflow_failure_exception_types,
54
55
  :workflow_payload_codec_thread_pool,
55
56
  :unsafe_workflow_io_enabled,
57
+ :deployment_options,
58
+ :workflow_task_poller_behavior,
59
+ :activity_task_poller_behavior,
56
60
  :debug_mode
57
61
  )
58
62
 
@@ -89,6 +93,14 @@ module Temporalio
89
93
  build_id
90
94
  end
91
95
 
96
+ # @return [DeploymentOptions] Default deployment options, which does not use worker versioning
97
+ # or a deployment name, and sets the build id to the one from {self.default_build_id}.
98
+ def self.default_deployment_options
99
+ @default_deployment_options ||= DeploymentOptions.new(
100
+ version: WorkerDeploymentVersion.new(deployment_name: '', build_id: Worker.default_build_id)
101
+ )
102
+ end
103
+
92
104
  # Run all workers until cancellation or optional block completes. When the cancellation or block is complete, the
93
105
  # workers are shut down. This will return the block result if everything successful or raise an error if not. See
94
106
  # {run} for details on how worker shutdown works.
@@ -232,8 +244,9 @@ module Temporalio
232
244
  end
233
245
  end
234
246
 
235
- # @return [Hash<String, [:all, Array<Symbol>]>] Default, immutable set illegal calls used for the
236
- # `illegal_workflow_calls` worker option. See the documentation of that option for more details.
247
+ # @return [Hash<String, [:all, Array<Symbol, IllegalWorkflowCallValidator>, IllegalWorkflowCallValidator]>] Default,
248
+ # immutable set illegal calls used for the `illegal_workflow_calls` worker option. See the documentation of that
249
+ # option for more details.
237
250
  def self.default_illegal_workflow_calls
238
251
  @default_illegal_workflow_calls ||= begin
239
252
  hash = {
@@ -254,7 +267,11 @@ module Temporalio
254
267
  #:write
255
268
  ],
256
269
  'Kernel' => %i[abort at_exit autoload autoload? eval exec exit fork gets load open rand readline readlines
257
- spawn srand system test trap],
270
+ sleep spawn srand system test trap],
271
+ # Loggers use mutexes in ways that can hang workflows, so users need to disable the durable scheduler to use
272
+ # them
273
+ 'Logger' => :all,
274
+ 'Monitor' => :all,
258
275
  'Net::HTTP' => :all,
259
276
  'Pathname' => :all,
260
277
  # TODO(cretz): Investigate why clock_gettime called from Timeout thread affects this code at all. Stack trace
@@ -269,10 +286,15 @@ module Temporalio
269
286
  'Signal' => :all,
270
287
  'Socket' => :all,
271
288
  'Tempfile' => :all,
289
+ 'Timeout' => :all,
272
290
  'Thread' => %i[abort_on_exception= exit fork handle_interrupt ignore_deadlock= kill new pass
273
291
  pending_interrupt? report_on_exception= start stop initialize join name= priority= raise run
274
292
  terminate thread_variable_set wakeup],
275
- 'Time' => %i[initialize now]
293
+ 'Thread::ConditionVariable' => :all,
294
+ 'Thread::Mutex' => IllegalWorkflowCallValidator.known_safe_mutex_validator,
295
+ 'Thread::SizedQueue' => :all,
296
+ 'Thread::Queue' => :all,
297
+ 'Time' => IllegalWorkflowCallValidator.default_time_validators
276
298
  } #: Hash[String, :all | Array[Symbol]]
277
299
  hash.each_value(&:freeze)
278
300
  hash.freeze
@@ -296,9 +318,6 @@ module Temporalio
296
318
  # @param interceptors [Array<Interceptor::Activity, Interceptor::Workflow>] Interceptors specific to this worker.
297
319
  # Note, interceptors set on the client that include the {Interceptor::Activity} or {Interceptor::Workflow} module
298
320
  # are automatically included here, so no need to specify them again.
299
- # @param build_id [String] Unique identifier for the current runtime. This is best set as a unique value
300
- # representing all code and should change only when code does. This can be something like a git commit hash. If
301
- # unset, default is hash of known Ruby code.
302
321
  # @param identity [String, nil] Override the identity for this worker. If unset, client identity is used.
303
322
  # @param logger [Logger] Logger to override client logger with. Default is the client logger.
304
323
  # @param max_cached_workflows [Integer] Number of workflows held in cache for use by sticky task queue. If set to 0,
@@ -327,19 +346,19 @@ module Temporalio
327
346
  # multiple workers on the same queue have different values set, they will thrash with the last poller winning.
328
347
  # @param graceful_shutdown_period [Float] Amount of time after shutdown is called that activities are given to
329
348
  # complete before their tasks are canceled.
330
- # @param use_worker_versioning [Boolean] If true, the `build_id` argument must be specified, and this worker opts
331
- # into the worker versioning feature. This ensures it only receives workflow tasks for workflows which it claims
332
- # to be compatible with. For more information, see https://docs.temporal.io/workers#worker-versioning.
333
349
  # @param disable_eager_activity_execution [Boolean] If true, disables eager activity execution. Eager activity
334
350
  # execution is an optimization on some servers that sends activities back to the same worker as the calling
335
351
  # workflow if they can run there. This should be set to true for `max_task_queue_activities_per_second` to work
336
352
  # and in a future version of this API may be implied as such (i.e. this setting will be ignored if that setting is
337
353
  # set).
338
- # @param illegal_workflow_calls [Hash<String, [:all, Array<Symbol>]>] Set of illegal workflow calls that are
339
- # considered unsafe/non-deterministic and will raise if seen. The key of the hash is the fully qualified string
340
- # class name (no leading `::`). The value is either `:all` which means any use of the class, or an array of
341
- # symbols for methods on the class that cannot be used. The methods refer to either instance or class methods,
342
- # there is no way to differentiate at this time.
354
+ # @param illegal_workflow_calls [Hash<String,
355
+ # [:all, Array<Symbol, IllegalWorkflowCallValidator>, IllegalWorkflowCallValidator]>] Set of illegal workflow
356
+ # calls that are considered unsafe/non-deterministic and will raise if seen. The key of the hash is the fully
357
+ # qualified string class name (no leading `::`). The value can be `:all` which means any use of the class is
358
+ # illegal. The value can be an array of symbols/validators for methods on the class that cannot be used. The
359
+ # methods refer to either instance or class methods, there is no way to differentiate at this time. Symbol method
360
+ # names are the normal way to say the method cannot be used, validators are only for advanced situations. Finally,
361
+ # for advanced situations, the hash value can be a class-level validator that is not tied to a specific method.
343
362
  # @param workflow_failure_exception_types [Array<Class<Exception>>] Workflow failure exception types. This is the
344
363
  # set of exception types that, if a workflow-thrown exception extends, will cause the workflow/update to fail
345
364
  # instead of suspending the workflow via task failure. These are applied in addition to the
@@ -351,6 +370,12 @@ module Temporalio
351
370
  # @param unsafe_workflow_io_enabled [Boolean] If false, the default, workflow code that invokes io_wait on the fiber
352
371
  # scheduler will fail. Instead of setting this to true, users are encouraged to use {Workflow::Unsafe.io_enabled}
353
372
  # with a block for narrower enabling of IO.
373
+ # @param deployment_options [DeploymentOptions, nil] Deployment options for the worker.
374
+ # WARNING: This is an experimental feature and may change in the future.
375
+ # @param workflow_task_poller_behavior [PollerBehavior] Specify the behavior of workflow task
376
+ # polling. Defaults to a 5-poller maximum.
377
+ # @param activity_task_poller_behavior [PollerBehavior] Specify the behavior of activity task
378
+ # polling. Defaults to a 5-poller maximum.
354
379
  # @param debug_mode [Boolean] If true, deadlock detection is disabled. Deadlock detection will fail workflow tasks
355
380
  # if they block the thread for too long. This defaults to true if the `TEMPORAL_DEBUG` environment variable is
356
381
  # `true` or `1`.
@@ -363,7 +388,6 @@ module Temporalio
363
388
  activity_executors: ActivityExecutor.defaults,
364
389
  workflow_executor: WorkflowExecutor::ThreadPool.default,
365
390
  interceptors: [],
366
- build_id: Worker.default_build_id,
367
391
  identity: nil,
368
392
  logger: client.options.logger,
369
393
  max_cached_workflows: 1000,
@@ -377,12 +401,14 @@ module Temporalio
377
401
  max_activities_per_second: nil,
378
402
  max_task_queue_activities_per_second: nil,
379
403
  graceful_shutdown_period: 0,
380
- use_worker_versioning: false,
381
404
  disable_eager_activity_execution: false,
382
405
  illegal_workflow_calls: Worker.default_illegal_workflow_calls,
383
406
  workflow_failure_exception_types: [],
384
407
  workflow_payload_codec_thread_pool: nil,
385
408
  unsafe_workflow_io_enabled: false,
409
+ deployment_options: Worker.default_deployment_options,
410
+ workflow_task_poller_behavior: PollerBehavior::SimpleMaximum.new(max_concurrent_workflow_task_polls),
411
+ activity_task_poller_behavior: PollerBehavior::SimpleMaximum.new(max_concurrent_activity_task_polls),
386
412
  debug_mode: %w[true 1].include?(ENV['TEMPORAL_DEBUG'].to_s.downcase)
387
413
  )
388
414
  raise ArgumentError, 'Must have at least one activity or workflow' if activities.empty? && workflows.empty?
@@ -398,7 +424,6 @@ module Temporalio
398
424
  activity_executors:,
399
425
  workflow_executor:,
400
426
  interceptors:,
401
- build_id:,
402
427
  identity:,
403
428
  logger:,
404
429
  max_cached_workflows:,
@@ -412,17 +437,25 @@ module Temporalio
412
437
  max_activities_per_second:,
413
438
  max_task_queue_activities_per_second:,
414
439
  graceful_shutdown_period:,
415
- use_worker_versioning:,
416
440
  disable_eager_activity_execution:,
417
441
  illegal_workflow_calls:,
418
442
  workflow_failure_exception_types:,
419
443
  workflow_payload_codec_thread_pool:,
420
444
  unsafe_workflow_io_enabled:,
445
+ deployment_options:,
446
+ workflow_task_poller_behavior:,
447
+ activity_task_poller_behavior:,
421
448
  debug_mode:
422
449
  ).freeze
423
450
 
451
+ should_enforce_versioning_behavior =
452
+ deployment_options.use_worker_versioning &&
453
+ deployment_options.default_versioning_behavior == VersioningBehavior::UNSPECIFIED
424
454
  # Preload workflow definitions and some workflow settings for the bridge
425
- workflow_definitions = Internal::Worker::WorkflowWorker.workflow_definitions(workflows)
455
+ workflow_definitions = Internal::Worker::WorkflowWorker.workflow_definitions(
456
+ workflows,
457
+ should_enforce_versioning_behavior: should_enforce_versioning_behavior
458
+ )
426
459
  nondeterminism_as_workflow_fail, nondeterminism_as_workflow_fail_for_types =
427
460
  Internal::Worker::WorkflowWorker.bridge_workflow_failure_exception_type_options(
428
461
  workflow_failure_exception_types:, workflow_definitions:
@@ -437,12 +470,11 @@ module Temporalio
437
470
  namespace: client.namespace,
438
471
  task_queue:,
439
472
  tuner: tuner._to_bridge_options,
440
- build_id:,
441
473
  identity_override: identity,
442
474
  max_cached_workflows:,
443
- max_concurrent_workflow_task_polls:,
475
+ workflow_task_poller_behavior: workflow_task_poller_behavior._to_bridge_options,
444
476
  nonsticky_to_sticky_poll_ratio:,
445
- max_concurrent_activity_task_polls:,
477
+ activity_task_poller_behavior: activity_task_poller_behavior._to_bridge_options,
446
478
  # For shutdown to work properly, we must disable remote activities
447
479
  # ourselves if there are no activities
448
480
  no_remote_activities: no_remote_activities || activities.empty?,
@@ -452,9 +484,9 @@ module Temporalio
452
484
  max_worker_activities_per_second: max_activities_per_second,
453
485
  max_task_queue_activities_per_second:,
454
486
  graceful_shutdown_period:,
455
- use_worker_versioning:,
456
487
  nondeterminism_as_workflow_fail:,
457
- nondeterminism_as_workflow_fail_for_types:
488
+ nondeterminism_as_workflow_fail_for_types:,
489
+ deployment_options: deployment_options._to_bridge_options
458
490
  )
459
491
  )
460
492
 
@@ -490,7 +522,8 @@ module Temporalio
490
522
  workflow_failure_exception_types:,
491
523
  workflow_payload_codec_thread_pool:,
492
524
  unsafe_workflow_io_enabled:,
493
- debug_mode:
525
+ debug_mode:,
526
+ assert_valid_local_activity: ->(activity) { _assert_valid_local_activity(activity) }
494
527
  )
495
528
  end
496
529
 
@@ -599,5 +632,17 @@ module Temporalio
599
632
  @workflow_worker&.on_shutdown_complete
600
633
  @workflow_worker = nil
601
634
  end
635
+
636
+ # @!visibility private
637
+ def _assert_valid_local_activity(activity)
638
+ unless @activity_worker.nil?
639
+ @activity_worker.assert_valid_activity(activity)
640
+ return
641
+ end
642
+
643
+ raise ArgumentError,
644
+ "Activity #{activity} " \
645
+ 'is not registered on this worker, no available activities.'
646
+ end
602
647
  end
603
648
  end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/internal/bridge/worker'
4
+
5
+ module Temporalio
6
+ WorkerDeploymentVersion = Data.define(
7
+ :deployment_name,
8
+ :build_id
9
+ )
10
+
11
+ # Represents the version of a specific worker deployment.
12
+ #
13
+ # WARNING: Experimental API.
14
+ class WorkerDeploymentVersion
15
+ # Parse a version from a canonical string, which must be in the format
16
+ # `<deployment_name>.<build_id>`. Deployment name must not have a `.` in it.
17
+ #
18
+ # @param canonical [String] The canonical string representation of the version.
19
+ # @return [WorkerDeploymentVersion] The parsed version.
20
+ def self.from_canonical_string(canonical)
21
+ parts = canonical.split('.', 2)
22
+ if parts.length != 2
23
+ raise ArgumentError,
24
+ "Cannot parse version string: #{canonical}, must be in format <deployment_name>.<build_id>"
25
+ end
26
+ new(deployment_name: parts[0], build_id: parts[1])
27
+ end
28
+
29
+ # @!visibility private
30
+ def self._from_bridge(bridge)
31
+ return nil if bridge.nil?
32
+
33
+ new(deployment_name: bridge.deployment_name, build_id: bridge.build_id)
34
+ end
35
+
36
+ # Create WorkerDeploymentVersion.
37
+ #
38
+ # @param deployment_name [String] The name of the deployment.
39
+ # @param build_id [String] The build identifier specific to this worker build.
40
+ def initialize(deployment_name:, build_id:) # rubocop:disable Lint/UselessMethodDefinition
41
+ super
42
+ end
43
+
44
+ # Returns the canonical string representation of the version.
45
+ #
46
+ # @return [String]
47
+ def to_canonical_string
48
+ "#{deployment_name}.#{build_id}"
49
+ end
50
+
51
+ # @!visibility private
52
+ def _to_bridge_options
53
+ Internal::Bridge::Worker::WorkerDeploymentVersion.new(
54
+ deployment_name: deployment_name,
55
+ build_id: build_id
56
+ )
57
+ end
58
+
59
+ # @!visibility private
60
+ def _to_proto
61
+ Api::Deployment::V1::WorkerDeploymentVersion.new(
62
+ deployment_name: deployment_name,
63
+ build_id: build_id
64
+ )
65
+ end
66
+ end
67
+ end
@@ -21,12 +21,18 @@ module Temporalio
21
21
  raise NotImplementedError
22
22
  end
23
23
 
24
+ # @return [Object, nil] Hint for the result if any.
25
+ def result_hint
26
+ raise NotImplementedError
27
+ end
28
+
24
29
  # Wait for the result.
25
30
  #
31
+ # @param result_hint [Object, nil] Override the result hint, or if nil uses the one on the handle.
26
32
  # @return [Object] Result of the child workflow.
27
33
  #
28
34
  # @raise [Error::ChildWorkflowError] Workflow failed with +cause+ as the cause.
29
- def result
35
+ def result(result_hint: nil)
30
36
  raise NotImplementedError
31
37
  end
32
38
 
@@ -35,7 +41,9 @@ module Temporalio
35
41
  # @param signal [Workflow::Definition::Signal, Symbol, String] Signal definition or name.
36
42
  # @param args [Array<Object>] Signal args.
37
43
  # @param cancellation [Cancellation] Cancellation for canceling the signalling.
38
- def signal(signal, *args, cancellation: Workflow.cancellation)
44
+ # @param arg_hints [Array<Object>, nil] Overrides converter hints for arguments if any. If unset/nil and the
45
+ # signal definition has arg hints, those are used by default.
46
+ def signal(signal, *args, cancellation: Workflow.cancellation, arg_hints: nil)
39
47
  raise NotImplementedError
40
48
  end
41
49
  end