temporalio 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/Cargo.lock +679 -437
  4. data/Cargo.toml +5 -5
  5. data/README.md +98 -34
  6. data/ext/Cargo.toml +3 -3
  7. data/lib/temporalio/activity/cancellation_details.rb +58 -0
  8. data/lib/temporalio/activity/context.rb +10 -1
  9. data/lib/temporalio/activity/definition.rb +41 -3
  10. data/lib/temporalio/activity/info.rb +25 -4
  11. data/lib/temporalio/activity.rb +2 -0
  12. data/lib/temporalio/api/activity/v1/message.rb +1 -1
  13. data/lib/temporalio/api/batch/v1/message.rb +4 -2
  14. data/lib/temporalio/api/cloud/account/v1/message.rb +1 -1
  15. data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +11 -2
  16. data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +2 -2
  17. data/lib/temporalio/api/cloud/identity/v1/message.rb +7 -2
  18. data/lib/temporalio/api/cloud/namespace/v1/message.rb +6 -2
  19. data/lib/temporalio/api/cloud/nexus/v1/message.rb +3 -2
  20. data/lib/temporalio/api/cloud/operation/v1/message.rb +1 -1
  21. data/lib/temporalio/api/cloud/region/v1/message.rb +1 -1
  22. data/lib/temporalio/api/cloud/resource/v1/message.rb +1 -1
  23. data/lib/temporalio/api/cloud/sink/v1/message.rb +1 -1
  24. data/lib/temporalio/api/cloud/usage/v1/message.rb +1 -1
  25. data/lib/temporalio/api/command/v1/message.rb +2 -2
  26. data/lib/temporalio/api/common/v1/grpc_status.rb +1 -1
  27. data/lib/temporalio/api/common/v1/message.rb +3 -2
  28. data/lib/temporalio/api/deployment/v1/message.rb +3 -2
  29. data/lib/temporalio/api/enums/v1/batch_operation.rb +1 -1
  30. data/lib/temporalio/api/enums/v1/command_type.rb +1 -1
  31. data/lib/temporalio/api/enums/v1/common.rb +5 -2
  32. data/lib/temporalio/api/enums/v1/deployment.rb +3 -2
  33. data/lib/temporalio/api/enums/v1/event_type.rb +2 -2
  34. data/lib/temporalio/api/enums/v1/failed_cause.rb +2 -2
  35. data/lib/temporalio/api/enums/v1/namespace.rb +1 -1
  36. data/lib/temporalio/api/enums/v1/nexus.rb +1 -1
  37. data/lib/temporalio/api/enums/v1/query.rb +1 -1
  38. data/lib/temporalio/api/enums/v1/reset.rb +1 -1
  39. data/lib/temporalio/api/enums/v1/schedule.rb +1 -1
  40. data/lib/temporalio/api/enums/v1/task_queue.rb +1 -1
  41. data/lib/temporalio/api/enums/v1/update.rb +1 -1
  42. data/lib/temporalio/api/enums/v1/workflow.rb +2 -2
  43. data/lib/temporalio/api/errordetails/v1/message.rb +1 -1
  44. data/lib/temporalio/api/export/v1/message.rb +1 -1
  45. data/lib/temporalio/api/failure/v1/message.rb +3 -2
  46. data/lib/temporalio/api/filter/v1/message.rb +1 -1
  47. data/lib/temporalio/api/history/v1/message.rb +4 -2
  48. data/lib/temporalio/api/namespace/v1/message.rb +1 -1
  49. data/lib/temporalio/api/nexus/v1/message.rb +2 -2
  50. data/lib/temporalio/api/operatorservice/v1/request_response.rb +1 -1
  51. data/lib/temporalio/api/operatorservice/v1/service.rb +1 -1
  52. data/lib/temporalio/api/payload_visitor.rb +87 -0
  53. data/lib/temporalio/api/protocol/v1/message.rb +1 -1
  54. data/lib/temporalio/api/query/v1/message.rb +1 -1
  55. data/lib/temporalio/api/replication/v1/message.rb +1 -1
  56. data/lib/temporalio/api/rules/v1/message.rb +27 -0
  57. data/lib/temporalio/api/schedule/v1/message.rb +2 -2
  58. data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +1 -1
  59. data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +1 -1
  60. data/lib/temporalio/api/sdk/v1/user_metadata.rb +1 -1
  61. data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +1 -1
  62. data/lib/temporalio/api/taskqueue/v1/message.rb +2 -2
  63. data/lib/temporalio/api/testservice/v1/request_response.rb +1 -1
  64. data/lib/temporalio/api/testservice/v1/service.rb +1 -1
  65. data/lib/temporalio/api/update/v1/message.rb +1 -1
  66. data/lib/temporalio/api/version/v1/message.rb +1 -1
  67. data/lib/temporalio/api/worker/v1/message.rb +30 -0
  68. data/lib/temporalio/api/workflow/v1/message.rb +14 -2
  69. data/lib/temporalio/api/workflowservice/v1/request_response.rb +19 -2
  70. data/lib/temporalio/api/workflowservice/v1/service.rb +2 -2
  71. data/lib/temporalio/client/async_activity_handle.rb +12 -4
  72. data/lib/temporalio/client/connection/cloud_service.rb +60 -0
  73. data/lib/temporalio/client/connection/workflow_service.rb +105 -0
  74. data/lib/temporalio/client/interceptor.rb +25 -7
  75. data/lib/temporalio/client/schedule.rb +10 -2
  76. data/lib/temporalio/client/with_start_workflow_operation.rb +9 -1
  77. data/lib/temporalio/client/workflow_handle.rb +50 -10
  78. data/lib/temporalio/client/workflow_update_handle.rb +9 -3
  79. data/lib/temporalio/client.rb +110 -6
  80. data/lib/temporalio/common_enums.rb +14 -0
  81. data/lib/temporalio/contrib/open_telemetry.rb +13 -9
  82. data/lib/temporalio/converters/data_converter.rb +18 -8
  83. data/lib/temporalio/converters/failure_converter.rb +6 -3
  84. data/lib/temporalio/converters/payload_converter/binary_null.rb +2 -2
  85. data/lib/temporalio/converters/payload_converter/binary_plain.rb +2 -2
  86. data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +2 -2
  87. data/lib/temporalio/converters/payload_converter/composite.rb +6 -4
  88. data/lib/temporalio/converters/payload_converter/encoding.rb +4 -2
  89. data/lib/temporalio/converters/payload_converter/json_plain.rb +2 -2
  90. data/lib/temporalio/converters/payload_converter/json_protobuf.rb +2 -2
  91. data/lib/temporalio/converters/payload_converter.rb +16 -6
  92. data/lib/temporalio/error/failure.rb +19 -1
  93. data/lib/temporalio/error.rb +1 -1
  94. data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +1 -1
  95. data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +3 -2
  96. data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +1 -1
  97. data/lib/temporalio/internal/bridge/api/common/common.rb +1 -1
  98. data/lib/temporalio/internal/bridge/api/core_interface.rb +1 -1
  99. data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +1 -1
  100. data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +3 -2
  101. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +2 -2
  102. data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +3 -2
  103. data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +1 -1
  104. data/lib/temporalio/internal/bridge/worker.rb +28 -4
  105. data/lib/temporalio/internal/bridge.rb +1 -1
  106. data/lib/temporalio/internal/client/implementation.rb +60 -52
  107. data/lib/temporalio/internal/proto_utils.rb +4 -4
  108. data/lib/temporalio/internal/worker/activity_worker.rb +93 -20
  109. data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +8 -6
  110. data/lib/temporalio/internal/worker/workflow_instance/context.rb +65 -24
  111. data/lib/temporalio/internal/worker/workflow_instance/details.rb +5 -2
  112. data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +2 -2
  113. data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +64 -18
  114. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +28 -14
  115. data/lib/temporalio/internal/worker/workflow_instance.rb +58 -23
  116. data/lib/temporalio/internal/worker/workflow_worker.rb +16 -6
  117. data/lib/temporalio/priority.rb +59 -0
  118. data/lib/temporalio/testing/activity_environment.rb +17 -2
  119. data/lib/temporalio/testing/workflow_environment.rb +3 -3
  120. data/lib/temporalio/version.rb +1 -1
  121. data/lib/temporalio/versioning_override.rb +56 -0
  122. data/lib/temporalio/worker/deployment_options.rb +45 -0
  123. data/lib/temporalio/worker/illegal_workflow_call_validator.rb +64 -0
  124. data/lib/temporalio/worker/interceptor.rb +13 -1
  125. data/lib/temporalio/worker/poller_behavior.rb +61 -0
  126. data/lib/temporalio/worker/thread_pool.rb +1 -1
  127. data/lib/temporalio/worker/workflow_executor/thread_pool.rb +2 -1
  128. data/lib/temporalio/worker/workflow_replayer.rb +12 -13
  129. data/lib/temporalio/worker.rb +63 -27
  130. data/lib/temporalio/worker_deployment_version.rb +67 -0
  131. data/lib/temporalio/workflow/child_workflow_handle.rb +10 -2
  132. data/lib/temporalio/workflow/definition.rb +183 -33
  133. data/lib/temporalio/workflow/external_workflow_handle.rb +3 -1
  134. data/lib/temporalio/workflow/info.rb +4 -1
  135. data/lib/temporalio/workflow.rb +61 -9
  136. data/lib/temporalio.rb +1 -0
  137. data/temporalio.gemspec +1 -0
  138. metadata +12 -3
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/common_enums'
4
+ require 'temporalio/worker_deployment_version'
5
+
6
+ module Temporalio
7
+ class Worker
8
+ DeploymentOptions = Data.define(
9
+ :version,
10
+ :use_worker_versioning,
11
+ :default_versioning_behavior
12
+ )
13
+
14
+ # Options for configuring the Worker Versioning feature.
15
+ #
16
+ # WARNING: Deployment-based versioning is experimental and APIs may change.
17
+ #
18
+ # @!attribute version
19
+ # @return [WorkerDeploymentVersion] The worker deployment version.
20
+ # @!attribute use_worker_versioning
21
+ # @return [Boolean] Whether worker versioning is enabled.
22
+ # @!attribute default_versioning_behavior
23
+ # @return [VersioningBehavior] The default versioning behavior.
24
+ class DeploymentOptions
25
+ def initialize(
26
+ version:,
27
+ use_worker_versioning: false,
28
+ default_versioning_behavior: VersioningBehavior::UNSPECIFIED
29
+ )
30
+ super
31
+ end
32
+
33
+ # @!visibility private
34
+ def _to_bridge_options
35
+ Internal::Bridge::Worker::DeploymentOptions.new(
36
+ version: Internal::Bridge::Worker::WorkerDeploymentVersion.new(
37
+ deployment_name: version.deployment_name, build_id: version.build_id
38
+ ),
39
+ use_worker_versioning: use_worker_versioning,
40
+ default_versioning_behavior: default_versioning_behavior
41
+ )
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,64 @@
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 [String, nil] Method name if this validator is specific to a method.
43
+ attr_reader :method_name
44
+
45
+ # @return [Proc] Block provided in constructor to invoke. See constructor for more details.
46
+ attr_reader :block
47
+
48
+ # Create a call validator.
49
+ #
50
+ # @param method_name [String, nil] Method name to check. This must be provided if the validator is in an illegal
51
+ # call array, this cannot be provided if it is a top-level class validator.
52
+ # @yield Required block that is called each time validation is needed. If the call raises, the exception message
53
+ # is used as the reason why the call is considered invalid. Return value is ignored.
54
+ # @yieldparam info [CallInfo] Information about the current call.
55
+ def initialize(method_name: nil, &block)
56
+ raise 'Block required' unless block_given?
57
+ raise TypeError, 'Method name must be Symbol' unless method_name.nil? || method_name.is_a?(Symbol)
58
+
59
+ @method_name = method_name
60
+ @block = block
61
+ end
62
+ end
63
+ end
64
+ 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 = {
@@ -272,7 +285,7 @@ module Temporalio
272
285
  'Thread' => %i[abort_on_exception= exit fork handle_interrupt ignore_deadlock= kill new pass
273
286
  pending_interrupt? report_on_exception= start stop initialize join name= priority= raise run
274
287
  terminate thread_variable_set wakeup],
275
- 'Time' => %i[initialize now]
288
+ 'Time' => IllegalWorkflowCallValidator.default_time_validators
276
289
  } #: Hash[String, :all | Array[Symbol]]
277
290
  hash.each_value(&:freeze)
278
291
  hash.freeze
@@ -296,9 +309,6 @@ module Temporalio
296
309
  # @param interceptors [Array<Interceptor::Activity, Interceptor::Workflow>] Interceptors specific to this worker.
297
310
  # Note, interceptors set on the client that include the {Interceptor::Activity} or {Interceptor::Workflow} module
298
311
  # 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
312
  # @param identity [String, nil] Override the identity for this worker. If unset, client identity is used.
303
313
  # @param logger [Logger] Logger to override client logger with. Default is the client logger.
304
314
  # @param max_cached_workflows [Integer] Number of workflows held in cache for use by sticky task queue. If set to 0,
@@ -327,19 +337,19 @@ module Temporalio
327
337
  # multiple workers on the same queue have different values set, they will thrash with the last poller winning.
328
338
  # @param graceful_shutdown_period [Float] Amount of time after shutdown is called that activities are given to
329
339
  # 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
340
  # @param disable_eager_activity_execution [Boolean] If true, disables eager activity execution. Eager activity
334
341
  # execution is an optimization on some servers that sends activities back to the same worker as the calling
335
342
  # workflow if they can run there. This should be set to true for `max_task_queue_activities_per_second` to work
336
343
  # 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
344
  # 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.
345
+ # @param illegal_workflow_calls [Hash<String,
346
+ # [:all, Array<Symbol, IllegalWorkflowCallValidator>, IllegalWorkflowCallValidator]>] Set of illegal workflow
347
+ # calls that are considered unsafe/non-deterministic and will raise if seen. The key of the hash is the fully
348
+ # qualified string class name (no leading `::`). The value can be `:all` which means any use of the class is
349
+ # illegal. The value can be an array of symbols/validators for methods on the class that cannot be used. The
350
+ # methods refer to either instance or class methods, there is no way to differentiate at this time. Symbol method
351
+ # names are the normal way to say the method cannot be used, validators are only for advanced situations. Finally,
352
+ # for advanced situations, the hash value can be a class-level validator that is not tied to a specific method.
343
353
  # @param workflow_failure_exception_types [Array<Class<Exception>>] Workflow failure exception types. This is the
344
354
  # set of exception types that, if a workflow-thrown exception extends, will cause the workflow/update to fail
345
355
  # instead of suspending the workflow via task failure. These are applied in addition to the
@@ -351,6 +361,12 @@ module Temporalio
351
361
  # @param unsafe_workflow_io_enabled [Boolean] If false, the default, workflow code that invokes io_wait on the fiber
352
362
  # scheduler will fail. Instead of setting this to true, users are encouraged to use {Workflow::Unsafe.io_enabled}
353
363
  # with a block for narrower enabling of IO.
364
+ # @param deployment_options [DeploymentOptions, nil] Deployment options for the worker.
365
+ # WARNING: This is an experimental feature and may change in the future.
366
+ # @param workflow_task_poller_behavior [PollerBehavior] Specify the behavior of workflow task
367
+ # polling. Defaults to a 5-poller maximum.
368
+ # @param activity_task_poller_behavior [PollerBehavior] Specify the behavior of activity task
369
+ # polling. Defaults to a 5-poller maximum.
354
370
  # @param debug_mode [Boolean] If true, deadlock detection is disabled. Deadlock detection will fail workflow tasks
355
371
  # if they block the thread for too long. This defaults to true if the `TEMPORAL_DEBUG` environment variable is
356
372
  # `true` or `1`.
@@ -363,7 +379,6 @@ module Temporalio
363
379
  activity_executors: ActivityExecutor.defaults,
364
380
  workflow_executor: WorkflowExecutor::ThreadPool.default,
365
381
  interceptors: [],
366
- build_id: Worker.default_build_id,
367
382
  identity: nil,
368
383
  logger: client.options.logger,
369
384
  max_cached_workflows: 1000,
@@ -377,12 +392,14 @@ module Temporalio
377
392
  max_activities_per_second: nil,
378
393
  max_task_queue_activities_per_second: nil,
379
394
  graceful_shutdown_period: 0,
380
- use_worker_versioning: false,
381
395
  disable_eager_activity_execution: false,
382
396
  illegal_workflow_calls: Worker.default_illegal_workflow_calls,
383
397
  workflow_failure_exception_types: [],
384
398
  workflow_payload_codec_thread_pool: nil,
385
399
  unsafe_workflow_io_enabled: false,
400
+ deployment_options: Worker.default_deployment_options,
401
+ workflow_task_poller_behavior: PollerBehavior::SimpleMaximum.new(max_concurrent_workflow_task_polls),
402
+ activity_task_poller_behavior: PollerBehavior::SimpleMaximum.new(max_concurrent_activity_task_polls),
386
403
  debug_mode: %w[true 1].include?(ENV['TEMPORAL_DEBUG'].to_s.downcase)
387
404
  )
388
405
  raise ArgumentError, 'Must have at least one activity or workflow' if activities.empty? && workflows.empty?
@@ -398,7 +415,6 @@ module Temporalio
398
415
  activity_executors:,
399
416
  workflow_executor:,
400
417
  interceptors:,
401
- build_id:,
402
418
  identity:,
403
419
  logger:,
404
420
  max_cached_workflows:,
@@ -412,17 +428,25 @@ module Temporalio
412
428
  max_activities_per_second:,
413
429
  max_task_queue_activities_per_second:,
414
430
  graceful_shutdown_period:,
415
- use_worker_versioning:,
416
431
  disable_eager_activity_execution:,
417
432
  illegal_workflow_calls:,
418
433
  workflow_failure_exception_types:,
419
434
  workflow_payload_codec_thread_pool:,
420
435
  unsafe_workflow_io_enabled:,
436
+ deployment_options:,
437
+ workflow_task_poller_behavior:,
438
+ activity_task_poller_behavior:,
421
439
  debug_mode:
422
440
  ).freeze
423
441
 
442
+ should_enforce_versioning_behavior =
443
+ deployment_options.use_worker_versioning &&
444
+ deployment_options.default_versioning_behavior == VersioningBehavior::UNSPECIFIED
424
445
  # Preload workflow definitions and some workflow settings for the bridge
425
- workflow_definitions = Internal::Worker::WorkflowWorker.workflow_definitions(workflows)
446
+ workflow_definitions = Internal::Worker::WorkflowWorker.workflow_definitions(
447
+ workflows,
448
+ should_enforce_versioning_behavior: should_enforce_versioning_behavior
449
+ )
426
450
  nondeterminism_as_workflow_fail, nondeterminism_as_workflow_fail_for_types =
427
451
  Internal::Worker::WorkflowWorker.bridge_workflow_failure_exception_type_options(
428
452
  workflow_failure_exception_types:, workflow_definitions:
@@ -437,12 +461,11 @@ module Temporalio
437
461
  namespace: client.namespace,
438
462
  task_queue:,
439
463
  tuner: tuner._to_bridge_options,
440
- build_id:,
441
464
  identity_override: identity,
442
465
  max_cached_workflows:,
443
- max_concurrent_workflow_task_polls:,
466
+ workflow_task_poller_behavior: workflow_task_poller_behavior._to_bridge_options,
444
467
  nonsticky_to_sticky_poll_ratio:,
445
- max_concurrent_activity_task_polls:,
468
+ activity_task_poller_behavior: activity_task_poller_behavior._to_bridge_options,
446
469
  # For shutdown to work properly, we must disable remote activities
447
470
  # ourselves if there are no activities
448
471
  no_remote_activities: no_remote_activities || activities.empty?,
@@ -452,9 +475,9 @@ module Temporalio
452
475
  max_worker_activities_per_second: max_activities_per_second,
453
476
  max_task_queue_activities_per_second:,
454
477
  graceful_shutdown_period:,
455
- use_worker_versioning:,
456
478
  nondeterminism_as_workflow_fail:,
457
- nondeterminism_as_workflow_fail_for_types:
479
+ nondeterminism_as_workflow_fail_for_types:,
480
+ deployment_options: deployment_options._to_bridge_options
458
481
  )
459
482
  )
460
483
 
@@ -490,7 +513,8 @@ module Temporalio
490
513
  workflow_failure_exception_types:,
491
514
  workflow_payload_codec_thread_pool:,
492
515
  unsafe_workflow_io_enabled:,
493
- debug_mode:
516
+ debug_mode:,
517
+ assert_valid_local_activity: ->(activity) { _assert_valid_local_activity(activity) }
494
518
  )
495
519
  end
496
520
 
@@ -599,5 +623,17 @@ module Temporalio
599
623
  @workflow_worker&.on_shutdown_complete
600
624
  @workflow_worker = nil
601
625
  end
626
+
627
+ # @!visibility private
628
+ def _assert_valid_local_activity(activity)
629
+ unless @activity_worker.nil?
630
+ @activity_worker.assert_valid_activity(activity)
631
+ return
632
+ end
633
+
634
+ raise ArgumentError,
635
+ "Activity #{activity} " \
636
+ 'is not registered on this worker, no available activities.'
637
+ end
602
638
  end
603
639
  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