temporalio 0.5.0-x86_64-darwin → 1.0.0-x86_64-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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/temporalio/activity/info.rb +5 -0
  3. data/lib/temporalio/api/batch/v1/message.rb +4 -1
  4. data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +12 -1
  5. data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +1 -1
  6. data/lib/temporalio/api/cloud/connectivityrule/v1/message.rb +29 -0
  7. data/lib/temporalio/api/cloud/identity/v1/message.rb +1 -1
  8. data/lib/temporalio/api/cloud/namespace/v1/message.rb +2 -1
  9. data/lib/temporalio/api/cloud/operation/v1/message.rb +1 -1
  10. data/lib/temporalio/api/common/v1/message.rb +2 -1
  11. data/lib/temporalio/api/enums/v1/batch_operation.rb +1 -1
  12. data/lib/temporalio/api/enums/v1/task_queue.rb +2 -1
  13. data/lib/temporalio/api/history/v1/message.rb +1 -1
  14. data/lib/temporalio/api/payload_visitor.rb +19 -1
  15. data/lib/temporalio/api/sdk/v1/worker_config.rb +23 -0
  16. data/lib/temporalio/api/taskqueue/v1/message.rb +5 -1
  17. data/lib/temporalio/api/worker/v1/message.rb +2 -1
  18. data/lib/temporalio/api/workflowservice/v1/request_response.rb +10 -1
  19. data/lib/temporalio/api/workflowservice/v1/service.rb +1 -1
  20. data/lib/temporalio/cancellation.rb +16 -12
  21. data/lib/temporalio/client/async_activity_handle.rb +1 -0
  22. data/lib/temporalio/client/connection/cloud_service.rb +75 -0
  23. data/lib/temporalio/client/connection/workflow_service.rb +45 -0
  24. data/lib/temporalio/client/connection.rb +2 -1
  25. data/lib/temporalio/contrib/open_telemetry.rb +9 -13
  26. data/lib/temporalio/converters/payload_converter/json_plain.rb +22 -5
  27. data/lib/temporalio/env_config.rb +343 -0
  28. data/lib/temporalio/error.rb +5 -1
  29. data/lib/temporalio/internal/bridge/3.2/temporalio_bridge.bundle +0 -0
  30. data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.bundle +0 -0
  31. data/lib/temporalio/internal/bridge/3.4/temporalio_bridge.bundle +0 -0
  32. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +1 -1
  33. data/lib/temporalio/internal/bridge/worker.rb +50 -0
  34. data/lib/temporalio/internal/client/implementation.rb +7 -2
  35. data/lib/temporalio/internal/worker/activity_worker.rb +1 -0
  36. data/lib/temporalio/internal/worker/workflow_instance/context.rb +7 -4
  37. data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +2 -0
  38. data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +18 -11
  39. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +6 -5
  40. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +5 -2
  41. data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +10 -4
  42. data/lib/temporalio/internal/worker/workflow_instance.rb +77 -80
  43. data/lib/temporalio/priority.rb +47 -6
  44. data/lib/temporalio/scoped_logger.rb +1 -1
  45. data/lib/temporalio/testing/activity_environment.rb +1 -0
  46. data/lib/temporalio/version.rb +1 -1
  47. data/lib/temporalio/worker/illegal_workflow_call_validator.rb +9 -0
  48. data/lib/temporalio/worker/interceptor.rb +1 -0
  49. data/lib/temporalio/worker/tuner.rb +185 -16
  50. data/lib/temporalio/worker.rb +10 -1
  51. data/lib/temporalio/workflow/definition.rb +4 -6
  52. data/lib/temporalio/workflow/info.rb +3 -0
  53. data/lib/temporalio/workflow.rb +80 -6
  54. metadata +5 -2
@@ -40,6 +40,7 @@ module Temporalio
40
40
  TunerSlotSupplierOptions = Struct.new(
41
41
  :fixed_size,
42
42
  :resource_based,
43
+ :custom,
43
44
  keyword_init: true
44
45
  )
45
46
 
@@ -103,6 +104,55 @@ module Temporalio
103
104
  # TODO(cretz): Log error on this somehow?
104
105
  async_complete_activity_task(proto.to_proto, queue)
105
106
  end
107
+
108
+ class CustomSlotSupplier
109
+ def initialize(slot_supplier:, thread_pool:)
110
+ @slot_supplier = slot_supplier
111
+ @thread_pool = thread_pool
112
+ end
113
+
114
+ def reserve_slot(context, cancellation, &block)
115
+ run_user_code do
116
+ @slot_supplier.reserve_slot(context, cancellation) { |v| block.call(v) }
117
+ rescue Exception => e # rubocop:disable Lint/RescueException
118
+ block.call(e)
119
+ end
120
+ end
121
+
122
+ def try_reserve_slot(context, &block)
123
+ run_user_code do
124
+ block.call(@slot_supplier.try_reserve_slot(context))
125
+ rescue Exception => e # rubocop:disable Lint/RescueException
126
+ block.call(e)
127
+ end
128
+ end
129
+
130
+ def mark_slot_used(context, &block)
131
+ run_user_code do
132
+ block.call(@slot_supplier.mark_slot_used(context))
133
+ rescue Exception => e # rubocop:disable Lint/RescueException
134
+ block.call(e)
135
+ end
136
+ end
137
+
138
+ def release_slot(context, &block)
139
+ run_user_code do
140
+ block.call(@slot_supplier.release_slot(context))
141
+ rescue Exception => e # rubocop:disable Lint/RescueException
142
+ block.call(e)
143
+ end
144
+ end
145
+
146
+ private
147
+
148
+ def run_user_code(&)
149
+ if @thread_pool
150
+ @thread_pool.execute(&)
151
+ else
152
+ yield
153
+ end
154
+ end
155
+ end
106
156
  end
107
157
  end
108
158
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'google/protobuf/well_known_types'
4
4
  require 'securerandom'
5
+ require 'temporalio/activity'
5
6
  require 'temporalio/api'
6
7
  require 'temporalio/client/activity_id_reference'
7
8
  require 'temporalio/client/async_activity_handle'
@@ -829,9 +830,13 @@ module Temporalio
829
830
  rpc_options: Implementation.with_default_rpc_options(input.rpc_options)
830
831
  )
831
832
  end
832
- raise Error::AsyncActivityCanceledError if resp.cancel_requested
833
+ return unless resp.cancel_requested || resp.activity_paused || resp.activity_reset
833
834
 
834
- nil
835
+ raise Error::AsyncActivityCanceledError, Activity::CancellationDetails.new(
836
+ cancel_requested: resp.cancel_requested,
837
+ paused: resp.activity_paused,
838
+ reset: resp.activity_reset
839
+ )
835
840
  end
836
841
 
837
842
  def complete_async_activity(input)
@@ -185,6 +185,7 @@ module Temporalio
185
185
  payloads = codec.decode(payloads) if codec
186
186
  payloads.map { |p| Temporalio::Converters::RawValue.new(p) }
187
187
  end,
188
+ retry_policy: (RetryPolicy._from_proto(start.retry_policy) if start.retry_policy),
188
189
  schedule_to_close_timeout: Internal::ProtoUtils.duration_to_seconds(start.schedule_to_close_timeout),
189
190
  scheduled_time: Internal::ProtoUtils.timestamp_to_time(start.scheduled_time) || raise, # Never nil
190
191
  start_to_close_timeout: Internal::ProtoUtils.duration_to_seconds(start.start_to_close_timeout),
@@ -63,12 +63,13 @@ module Temporalio
63
63
  end
64
64
 
65
65
  def durable_scheduler_disabled(&)
66
- prev = Fiber.current_scheduler
67
- illegal_call_tracing_disabled { Fiber.set_scheduler(nil) }
68
- begin
66
+ prev = Fiber.scheduler
67
+ # Imply illegal call tracing disabled
68
+ illegal_call_tracing_disabled do
69
+ Fiber.set_scheduler(nil)
69
70
  yield
70
71
  ensure
71
- illegal_call_tracing_disabled { Fiber.set_scheduler(prev) }
72
+ Fiber.set_scheduler(prev)
72
73
  end
73
74
  end
74
75
 
@@ -129,6 +130,7 @@ module Temporalio
129
130
  def execute_local_activity(
130
131
  activity,
131
132
  *args,
133
+ summary:,
132
134
  schedule_to_close_timeout:,
133
135
  schedule_to_start_timeout:,
134
136
  start_to_close_timeout:,
@@ -156,6 +158,7 @@ module Temporalio
156
158
  Temporalio::Worker::Interceptor::Workflow::ExecuteLocalActivityInput.new(
157
159
  activity:,
158
160
  args:,
161
+ summary:,
159
162
  schedule_to_close_timeout:,
160
163
  schedule_to_start_timeout:,
161
164
  start_to_close_timeout:,
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'delegate'
4
+
3
5
  module Temporalio
4
6
  module Internal
5
7
  module Worker
@@ -16,7 +16,7 @@ module Temporalio
16
16
  # @type var fixed_val: :all | Worker::IllegalWorkflowCallValidator | Hash[Symbol, TrueClass | Worker::IllegalWorkflowCallValidator] # rubocop:disable Layout/LineLength
17
17
  fixed_val = case val
18
18
  when Temporalio::Worker::IllegalWorkflowCallValidator
19
- if sub_val.method_name
19
+ if val.method_name
20
20
  raise ArgumentError,
21
21
  'Top level IllegalWorkflowCallValidator instances cannot have method name'
22
22
  end
@@ -84,7 +84,7 @@ module Temporalio
84
84
  when :all
85
85
  ''
86
86
  when Temporalio::Worker::IllegalWorkflowCallValidator
87
- disable do
87
+ disable_temporarily do
88
88
  vals.block.call(Temporalio::Worker::IllegalWorkflowCallValidator::CallInfo.new(
89
89
  class_name:, method_name: tp.callee_id, trace_point: tp
90
90
  ))
@@ -98,7 +98,7 @@ module Temporalio
98
98
  when true
99
99
  ''
100
100
  when Temporalio::Worker::IllegalWorkflowCallValidator
101
- disable do
101
+ disable_temporarily do
102
102
  per_method.block.call(Temporalio::Worker::IllegalWorkflowCallValidator::CallInfo.new(
103
103
  class_name:, method_name: tp.callee_id, trace_point: tp
104
104
  ))
@@ -118,8 +118,11 @@ module Temporalio
118
118
  end
119
119
 
120
120
  def enable(&block)
121
- # We've seen leaking issues in Ruby 3.2 where the TracePoint inadvertently remains enabled even for threads
122
- # that it was not started on. So we will check the thread ourselves.
121
+ # This is not reentrant and not expected to be called as such. We've seen leaking issues in Ruby 3.2 where
122
+ # the TracePoint inadvertently remains enabled even for threads that it was not started on. So we will check
123
+ # the thread ourselves. We also use the "enabled thread" concept for disabling checks too, see
124
+ # disable_temporarily for more details.
125
+
123
126
  @enabled_thread = Thread.current
124
127
  @tracepoint.enable do
125
128
  block.call
@@ -128,13 +131,17 @@ module Temporalio
128
131
  end
129
132
  end
130
133
 
131
- def disable(&block)
134
+ def disable_temporarily(&)
135
+ # An earlier version of this used @tracepoint.disable, but in some versions of Ruby, the observed behavior
136
+ # is confusingly not reentrant or at least not predictable. Therefore, instead of calling
137
+ # @tracepoint.disable, we are just unsetting the enabled thread. This means the tracer is still running, but
138
+ # no checks are performed. This is effectively a no-op if tracing was never enabled.
139
+
132
140
  previous_thread = @enabled_thread
133
- @tracepoint.disable do
134
- block.call
135
- ensure
136
- @enabled_thread = previous_thread
137
- end
141
+ @enabled_thread = nil
142
+ yield
143
+ ensure
144
+ @enabled_thread = previous_thread
138
145
  end
139
146
  end
140
147
  end
@@ -114,14 +114,15 @@ module Temporalio
114
114
  local_retry_threshold: ProtoUtils.seconds_to_duration(input.local_retry_threshold),
115
115
  attempt: do_backoff&.attempt || 0,
116
116
  original_schedule_time: do_backoff&.original_schedule_time
117
- )
117
+ ),
118
+ user_metadata: ProtoUtils.to_user_metadata(input.summary, nil, @instance.payload_converter)
118
119
  )
119
120
  )
120
121
  seq
121
122
  end
122
123
  end
123
124
 
124
- def execute_activity_with_local_backoffs(local:, cancellation:, result_hint:, &)
125
+ def execute_activity_with_local_backoffs(local:, cancellation:, result_hint:, &block)
125
126
  # We do not even want to schedule if the cancellation is already cancelled. We choose to use canceled
126
127
  # failure instead of wrapping in activity failure which is similar to what other SDKs do, with the accepted
127
128
  # tradeoff that it makes rescue more difficult (hence the presence of Error.canceled? helper).
@@ -130,7 +131,7 @@ module Temporalio
130
131
  # This has to be done in a loop for local activity backoff
131
132
  last_local_backoff = nil
132
133
  loop do
133
- result = execute_activity_once(local:, cancellation:, last_local_backoff:, result_hint:, &)
134
+ result = execute_activity_once(local:, cancellation:, last_local_backoff:, result_hint:, &block)
134
135
  return result unless result.is_a?(Bridge::Api::ActivityResult::DoBackoff)
135
136
 
136
137
  # @type var result: untyped
@@ -142,9 +143,9 @@ module Temporalio
142
143
  end
143
144
 
144
145
  # If this doesn't raise, it returns success | DoBackoff
145
- def execute_activity_once(local:, cancellation:, last_local_backoff:, result_hint:, &)
146
+ def execute_activity_once(local:, cancellation:, last_local_backoff:, result_hint:, &block)
146
147
  # Add to pending activities (removed by the resolver)
147
- seq = yield last_local_backoff
148
+ seq = block.call(last_local_backoff)
148
149
  @instance.pending_activities[seq] = Fiber.current
149
150
 
150
151
  # Add cancellation hook
@@ -27,8 +27,11 @@ module Temporalio
27
27
  return true
28
28
  end
29
29
 
30
- # Disable illegal call tracing for the log call
31
- @instance.illegal_call_tracing_disabled { super }
30
+ # Disable scheduler since logs technically have local mutexes in them that cannot be done durably or they
31
+ # will block workflows
32
+ @instance.context.durable_scheduler_disabled do
33
+ super
34
+ end
32
35
  end
33
36
  end
34
37
  end
@@ -40,10 +40,15 @@ module Temporalio
40
40
  cond_fiber = nil
41
41
  cond_result = nil
42
42
  @wait_conditions.each do |seq, cond|
43
+ # Evaluate condition or skip if not true
43
44
  next unless (cond_result = cond.first.call)
44
45
 
45
- cond_fiber = cond[1]
46
- @wait_conditions.delete(seq)
46
+ # There have been reports of this fiber being completed already, so we make sure not to process if it
47
+ # has, but we still delete it
48
+ deleted_cond = @wait_conditions.delete(seq)
49
+ next unless deleted_cond&.last&.alive?
50
+
51
+ cond_fiber = deleted_cond.last
47
52
  break
48
53
  end
49
54
  return unless cond_fiber
@@ -106,7 +111,7 @@ module Temporalio
106
111
  # We just yield because unblock will resume this. We will just wrap in timeout if needed.
107
112
  if timeout
108
113
  begin
109
- Timeout.timeout(timeout) { Fiber.yield }
114
+ Workflow.timeout(timeout) { Fiber.yield }
110
115
  true
111
116
  rescue Timeout::Error
112
117
  false
@@ -141,7 +146,8 @@ module Temporalio
141
146
  unless @instance.io_enabled
142
147
  raise Workflow::NondeterminismError,
143
148
  'Cannot perform IO from inside a workflow. If this is known to be safe, ' \
144
- 'the code can be run in a Temporalio::Workflow::Unsafe.io_enabled block.'
149
+ 'the code can be run in a Temporalio::Workflow::Unsafe.durable_scheduler_disabled ' \
150
+ 'or Temporalio::Workflow::Unsafe.io_enabled block.'
145
151
  end
146
152
 
147
153
  # Use regular Ruby behavior of blocking this thread. There is no Ruby implementation of io_wait we can just
@@ -125,6 +125,7 @@ module Temporalio
125
125
  continued_run_id: ProtoUtils.string_or(@init_job.continued_from_execution_run_id),
126
126
  cron_schedule: ProtoUtils.string_or(@init_job.cron_schedule),
127
127
  execution_timeout: ProtoUtils.duration_to_seconds(@init_job.workflow_execution_timeout),
128
+ first_execution_run_id: @init_job.first_execution_run_id,
128
129
  headers: ProtoUtils.headers_from_proto_map(@init_job.headers, @payload_converter) || {},
129
130
  last_failure: if @init_job.continued_failure
130
131
  @failure_converter.from_failure(@init_job.continued_failure, @payload_converter)
@@ -162,86 +163,9 @@ module Temporalio
162
163
  end
163
164
 
164
165
  def activate(activation)
165
- # Run inside of scheduler
166
- run_in_scheduler { activate_internal(activation) }
167
- end
168
-
169
- def add_command(command)
170
- raise Workflow::InvalidWorkflowStateError, 'Cannot add commands in this context' if @context_frozen
171
-
172
- @commands << command
173
- end
174
-
175
- def instance
176
- @instance or raise 'Instance accessed before created'
177
- end
178
-
179
- def search_attributes
180
- # Lazy on first access
181
- @search_attributes ||= SearchAttributes._from_proto(
182
- @init_job.search_attributes, disable_mutations: true, never_nil: true
183
- ) || raise
184
- end
185
-
186
- def memo
187
- # Lazy on first access
188
- @memo ||= ExternallyImmutableHash.new(ProtoUtils.memo_from_proto(@init_job.memo, payload_converter) || {})
189
- end
190
-
191
- def now
192
- # Create each time
193
- ProtoUtils.timestamp_to_time(@now_timestamp) or raise 'Time unexpectedly not present'
194
- end
195
-
196
- def illegal_call_tracing_disabled(&)
197
- @tracer.disable(&)
198
- end
199
-
200
- def patch(patch_id:, deprecated:)
201
- # Use memoized result if present. If this is being deprecated, we can still use memoized result and skip the
202
- # command.
203
- patch_id = patch_id.to_s
204
- @patches_memoized ||= {}
205
- @patches_memoized.fetch(patch_id) do
206
- patched = !replaying || @patches_notified.include?(patch_id)
207
- @patches_memoized[patch_id] = patched
208
- if patched
209
- add_command(
210
- Bridge::Api::WorkflowCommands::WorkflowCommand.new(
211
- set_patch_marker: Bridge::Api::WorkflowCommands::SetPatchMarker.new(patch_id:, deprecated:)
212
- )
213
- )
214
- end
215
- patched
216
- end
217
- end
218
-
219
- def metric_meter
220
- @metric_meter ||= ReplaySafeMetric::Meter.new(
221
- @runtime_metric_meter.with_additional_attributes(
222
- {
223
- namespace: info.namespace,
224
- task_queue: info.task_queue,
225
- workflow_type: info.workflow_type
226
- }
227
- )
228
- )
229
- end
230
-
231
- private
232
-
233
- def run_in_scheduler(&)
166
+ # Run inside of scheduler (removed on ensure)
234
167
  Fiber.set_scheduler(@scheduler)
235
- if @tracer
236
- @tracer.enable(&)
237
- else
238
- yield
239
- end
240
- ensure
241
- Fiber.set_scheduler(nil)
242
- end
243
168
 
244
- def activate_internal(activation)
245
169
  # Reset some activation state
246
170
  @commands = []
247
171
  @current_activation_error = nil
@@ -266,8 +190,12 @@ module Temporalio
266
190
  # the first activation)
267
191
  @primary_fiber ||= schedule(top_level: true) { run_workflow }
268
192
 
269
- # Run the event loop
270
- @scheduler.run_until_all_yielded
193
+ # Run the event loop in the tracer if it exists
194
+ if @tracer
195
+ @tracer.enable { @scheduler.run_until_all_yielded }
196
+ else
197
+ @scheduler.run_until_all_yielded
198
+ end
271
199
  rescue Exception => e # rubocop:disable Lint/RescueException
272
200
  on_top_level_exception(e)
273
201
  end
@@ -306,8 +234,77 @@ module Temporalio
306
234
  ensure
307
235
  @commands = nil
308
236
  @current_activation_error = nil
237
+ Fiber.set_scheduler(nil)
238
+ end
239
+
240
+ def add_command(command)
241
+ raise Workflow::InvalidWorkflowStateError, 'Cannot add commands in this context' if @context_frozen
242
+
243
+ @commands << command
244
+ end
245
+
246
+ def instance
247
+ @instance or raise 'Instance accessed before created'
248
+ end
249
+
250
+ def search_attributes
251
+ # Lazy on first access
252
+ @search_attributes ||= SearchAttributes._from_proto(
253
+ @init_job.search_attributes, disable_mutations: true, never_nil: true
254
+ ) || raise
255
+ end
256
+
257
+ def memo
258
+ # Lazy on first access
259
+ @memo ||= ExternallyImmutableHash.new(ProtoUtils.memo_from_proto(@init_job.memo, payload_converter) || {})
260
+ end
261
+
262
+ def now
263
+ # Create each time
264
+ ProtoUtils.timestamp_to_time(@now_timestamp) or raise 'Time unexpectedly not present'
309
265
  end
310
266
 
267
+ def illegal_call_tracing_disabled(&)
268
+ if @tracer
269
+ @tracer.disable_temporarily(&)
270
+ else
271
+ yield
272
+ end
273
+ end
274
+
275
+ def patch(patch_id:, deprecated:)
276
+ # Use memoized result if present. If this is being deprecated, we can still use memoized result and skip the
277
+ # command.
278
+ patch_id = patch_id.to_s
279
+ @patches_memoized ||= {}
280
+ @patches_memoized.fetch(patch_id) do
281
+ patched = !replaying || @patches_notified.include?(patch_id)
282
+ @patches_memoized[patch_id] = patched
283
+ if patched
284
+ add_command(
285
+ Bridge::Api::WorkflowCommands::WorkflowCommand.new(
286
+ set_patch_marker: Bridge::Api::WorkflowCommands::SetPatchMarker.new(patch_id:, deprecated:)
287
+ )
288
+ )
289
+ end
290
+ patched
291
+ end
292
+ end
293
+
294
+ def metric_meter
295
+ @metric_meter ||= ReplaySafeMetric::Meter.new(
296
+ @runtime_metric_meter.with_additional_attributes(
297
+ {
298
+ namespace: info.namespace,
299
+ task_queue: info.task_queue,
300
+ workflow_type: info.workflow_type
301
+ }
302
+ )
303
+ )
304
+ end
305
+
306
+ private
307
+
311
308
  def create_instance
312
309
  # Convert workflow arguments
313
310
  @workflow_arguments = convert_args(payload_array: @init_job.arguments,
@@ -4,7 +4,9 @@ require 'temporalio/api'
4
4
 
5
5
  module Temporalio
6
6
  Priority = Data.define(
7
- :priority_key
7
+ :priority_key,
8
+ :fairness_key,
9
+ :fairness_weight
8
10
  )
9
11
 
10
12
  # Priority contains metadata that controls relative ordering of task processing when tasks are
@@ -29,31 +31,70 @@ module Temporalio
29
31
  #
30
32
  # The default priority is (min+max)/2. With the default max of 5 and min of 1, that comes
31
33
  # out to 3.
34
+ #
35
+ # @!attribute fairness_key
36
+ # @return [String, nil] FairnessKey is a short string that's used as a key for a fairness
37
+ # balancing mechanism. It may correspond to a tenant id, or to a fixed
38
+ # string like "high" or "low". The default is the empty string.
39
+ #
40
+ # The fairness mechanism attempts to dispatch tasks for a given key in
41
+ # proportion to its weight. For example, using a thousand distinct tenant
42
+ # ids, each with a weight of 1.0 (the default) will result in each tenant
43
+ # getting a roughly equal share of task dispatch throughput.
44
+ #
45
+ # Fairness keys are limited to 64 bytes.
46
+ #
47
+ # @!attribute fairness_weight
48
+ # @return [Float, nil] Weight for a task can come from multiple sources for
49
+ # flexibility. From highest to lowest precedence:
50
+ # 1. Weights for a small set of keys can be overridden in task queue
51
+ # configuration with an API.
52
+ # 2. It can be attached to the workflow/activity in this field.
53
+ # 3. The default fairness_weight of 1.0 will be used.
54
+ #
55
+ # Weight values are clamped to the range [0.001, 1000].
32
56
  class Priority
33
57
  # @!visibility private
34
58
  def self._from_proto(priority)
35
59
  return default if priority.nil?
36
60
 
37
- new(priority_key: priority.priority_key.zero? ? nil : priority.priority_key)
61
+ new(
62
+ priority_key: priority.priority_key.zero? ? nil : priority.priority_key,
63
+ fairness_key: priority.fairness_key.empty? ? nil : priority.fairness_key,
64
+ fairness_weight: priority.fairness_weight.zero? ? nil : priority.fairness_weight
65
+ )
38
66
  end
39
67
 
40
68
  # The default priority instance.
41
69
  #
42
70
  # @return [Priority] The default priority
43
71
  def self.default
44
- @default ||= new(priority_key: nil)
72
+ @default ||= new(priority_key: nil, fairness_key: nil, fairness_weight: nil)
73
+ end
74
+
75
+ # Initialize a new Priority instance.
76
+ #
77
+ # @param priority_key [Integer, nil] The priority key
78
+ # @param fairness_key [String, nil] The fairness key
79
+ # @param fairness_weight [Float, nil] The fairness weight
80
+ def initialize(priority_key: nil, fairness_key: nil, fairness_weight: nil)
81
+ super
45
82
  end
46
83
 
47
84
  # @!visibility private
48
85
  def _to_proto
49
- return nil if priority_key.nil?
86
+ return nil if empty?
50
87
 
51
- Temporalio::Api::Common::V1::Priority.new(priority_key: priority_key || 0)
88
+ Temporalio::Api::Common::V1::Priority.new(
89
+ priority_key: priority_key || 0,
90
+ fairness_key: fairness_key || '',
91
+ fairness_weight: fairness_weight || 0.0
92
+ )
52
93
  end
53
94
 
54
95
  # @return [Boolean] True if this priority is empty/default
55
96
  def empty?
56
- priority_key.nil?
97
+ priority_key.nil? && fairness_key.nil? && fairness_weight.nil?
57
98
  end
58
99
  end
59
100
  end
@@ -17,7 +17,7 @@ module Temporalio
17
17
 
18
18
  # @see Logger.add
19
19
  def add(severity, message = nil, progname = nil)
20
- return true if (severity || Logger::Unknown) < level
20
+ return true if (severity || Logger::UNKNOWN) < level
21
21
  return super if scoped_values_getter.nil? || @disable_scoped_values
22
22
 
23
23
  scoped_values = scoped_values_getter.call
@@ -25,6 +25,7 @@ module Temporalio
25
25
  local?: false,
26
26
  priority: Temporalio::Priority.default,
27
27
  raw_heartbeat_details: [],
28
+ retry_policy: RetryPolicy.new,
28
29
  schedule_to_close_timeout: 1.0,
29
30
  scheduled_time: Time.at(0),
30
31
  start_to_close_timeout: 1.0,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Temporalio
4
- VERSION = '0.5.0'
4
+ VERSION = '1.0.0'
5
5
  end
@@ -39,6 +39,15 @@ module Temporalio
39
39
  ]
40
40
  end
41
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
+
42
51
  # @return [String, nil] Method name if this validator is specific to a method.
43
52
  attr_reader :method_name
44
53
 
@@ -228,6 +228,7 @@ module Temporalio
228
228
  ExecuteLocalActivityInput = Data.define(
229
229
  :activity,
230
230
  :args,
231
+ :summary,
231
232
  :schedule_to_close_timeout,
232
233
  :schedule_to_start_timeout,
233
234
  :start_to_close_timeout,