clara-temporalio 0.4.3-x86_64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +2 -0
  3. data/Gemfile +27 -0
  4. data/Rakefile +101 -0
  5. data/lib/temporalio/activity/cancellation_details.rb +58 -0
  6. data/lib/temporalio/activity/complete_async_error.rb +11 -0
  7. data/lib/temporalio/activity/context.rb +131 -0
  8. data/lib/temporalio/activity/definition.rb +197 -0
  9. data/lib/temporalio/activity/info.rb +70 -0
  10. data/lib/temporalio/activity.rb +14 -0
  11. data/lib/temporalio/api/activity/v1/message.rb +25 -0
  12. data/lib/temporalio/api/batch/v1/message.rb +38 -0
  13. data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
  14. data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +135 -0
  15. data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +25 -0
  16. data/lib/temporalio/api/cloud/cloudservice.rb +3 -0
  17. data/lib/temporalio/api/cloud/identity/v1/message.rb +46 -0
  18. data/lib/temporalio/api/cloud/namespace/v1/message.rb +46 -0
  19. data/lib/temporalio/api/cloud/nexus/v1/message.rb +32 -0
  20. data/lib/temporalio/api/cloud/operation/v1/message.rb +28 -0
  21. data/lib/temporalio/api/cloud/region/v1/message.rb +24 -0
  22. data/lib/temporalio/api/cloud/resource/v1/message.rb +23 -0
  23. data/lib/temporalio/api/cloud/sink/v1/message.rb +24 -0
  24. data/lib/temporalio/api/cloud/usage/v1/message.rb +31 -0
  25. data/lib/temporalio/api/command/v1/message.rb +46 -0
  26. data/lib/temporalio/api/common/v1/grpc_status.rb +23 -0
  27. data/lib/temporalio/api/common/v1/message.rb +49 -0
  28. data/lib/temporalio/api/deployment/v1/message.rb +39 -0
  29. data/lib/temporalio/api/enums/v1/batch_operation.rb +22 -0
  30. data/lib/temporalio/api/enums/v1/command_type.rb +21 -0
  31. data/lib/temporalio/api/enums/v1/common.rb +28 -0
  32. data/lib/temporalio/api/enums/v1/deployment.rb +23 -0
  33. data/lib/temporalio/api/enums/v1/event_type.rb +21 -0
  34. data/lib/temporalio/api/enums/v1/failed_cause.rb +26 -0
  35. data/lib/temporalio/api/enums/v1/namespace.rb +23 -0
  36. data/lib/temporalio/api/enums/v1/nexus.rb +21 -0
  37. data/lib/temporalio/api/enums/v1/query.rb +22 -0
  38. data/lib/temporalio/api/enums/v1/reset.rb +23 -0
  39. data/lib/temporalio/api/enums/v1/schedule.rb +21 -0
  40. data/lib/temporalio/api/enums/v1/task_queue.rb +25 -0
  41. data/lib/temporalio/api/enums/v1/update.rb +22 -0
  42. data/lib/temporalio/api/enums/v1/workflow.rb +31 -0
  43. data/lib/temporalio/api/errordetails/v1/message.rb +44 -0
  44. data/lib/temporalio/api/export/v1/message.rb +24 -0
  45. data/lib/temporalio/api/failure/v1/message.rb +38 -0
  46. data/lib/temporalio/api/filter/v1/message.rb +27 -0
  47. data/lib/temporalio/api/history/v1/message.rb +94 -0
  48. data/lib/temporalio/api/namespace/v1/message.rb +31 -0
  49. data/lib/temporalio/api/nexus/v1/message.rb +41 -0
  50. data/lib/temporalio/api/operatorservice/v1/request_response.rb +49 -0
  51. data/lib/temporalio/api/operatorservice/v1/service.rb +23 -0
  52. data/lib/temporalio/api/operatorservice.rb +3 -0
  53. data/lib/temporalio/api/payload_visitor.rb +1668 -0
  54. data/lib/temporalio/api/protocol/v1/message.rb +23 -0
  55. data/lib/temporalio/api/query/v1/message.rb +28 -0
  56. data/lib/temporalio/api/replication/v1/message.rb +26 -0
  57. data/lib/temporalio/api/rules/v1/message.rb +27 -0
  58. data/lib/temporalio/api/schedule/v1/message.rb +43 -0
  59. data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +25 -0
  60. data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +21 -0
  61. data/lib/temporalio/api/sdk/v1/user_metadata.rb +23 -0
  62. data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +23 -0
  63. data/lib/temporalio/api/taskqueue/v1/message.rb +48 -0
  64. data/lib/temporalio/api/testservice/v1/request_response.rb +31 -0
  65. data/lib/temporalio/api/testservice/v1/service.rb +23 -0
  66. data/lib/temporalio/api/update/v1/message.rb +33 -0
  67. data/lib/temporalio/api/version/v1/message.rb +26 -0
  68. data/lib/temporalio/api/workflow/v1/message.rb +63 -0
  69. data/lib/temporalio/api/workflowservice/v1/request_response.rb +244 -0
  70. data/lib/temporalio/api/workflowservice/v1/service.rb +23 -0
  71. data/lib/temporalio/api/workflowservice.rb +3 -0
  72. data/lib/temporalio/api.rb +15 -0
  73. data/lib/temporalio/cancellation.rb +170 -0
  74. data/lib/temporalio/client/activity_id_reference.rb +32 -0
  75. data/lib/temporalio/client/async_activity_handle.rb +85 -0
  76. data/lib/temporalio/client/connection/cloud_service.rb +786 -0
  77. data/lib/temporalio/client/connection/operator_service.rb +201 -0
  78. data/lib/temporalio/client/connection/service.rb +42 -0
  79. data/lib/temporalio/client/connection/test_service.rb +111 -0
  80. data/lib/temporalio/client/connection/workflow_service.rb +1326 -0
  81. data/lib/temporalio/client/connection.rb +316 -0
  82. data/lib/temporalio/client/interceptor.rb +457 -0
  83. data/lib/temporalio/client/schedule.rb +991 -0
  84. data/lib/temporalio/client/schedule_handle.rb +126 -0
  85. data/lib/temporalio/client/with_start_workflow_operation.rb +115 -0
  86. data/lib/temporalio/client/workflow_execution.rb +119 -0
  87. data/lib/temporalio/client/workflow_execution_count.rb +36 -0
  88. data/lib/temporalio/client/workflow_execution_status.rb +18 -0
  89. data/lib/temporalio/client/workflow_handle.rb +389 -0
  90. data/lib/temporalio/client/workflow_query_reject_condition.rb +14 -0
  91. data/lib/temporalio/client/workflow_update_handle.rb +65 -0
  92. data/lib/temporalio/client/workflow_update_wait_stage.rb +17 -0
  93. data/lib/temporalio/client.rb +625 -0
  94. data/lib/temporalio/common_enums.rb +55 -0
  95. data/lib/temporalio/contrib/open_telemetry.rb +469 -0
  96. data/lib/temporalio/converters/data_converter.rb +99 -0
  97. data/lib/temporalio/converters/failure_converter.rb +205 -0
  98. data/lib/temporalio/converters/payload_codec.rb +26 -0
  99. data/lib/temporalio/converters/payload_converter/binary_null.rb +34 -0
  100. data/lib/temporalio/converters/payload_converter/binary_plain.rb +35 -0
  101. data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +42 -0
  102. data/lib/temporalio/converters/payload_converter/composite.rb +66 -0
  103. data/lib/temporalio/converters/payload_converter/encoding.rb +35 -0
  104. data/lib/temporalio/converters/payload_converter/json_plain.rb +44 -0
  105. data/lib/temporalio/converters/payload_converter/json_protobuf.rb +41 -0
  106. data/lib/temporalio/converters/payload_converter.rb +71 -0
  107. data/lib/temporalio/converters/raw_value.rb +20 -0
  108. data/lib/temporalio/converters.rb +9 -0
  109. data/lib/temporalio/error/failure.rb +237 -0
  110. data/lib/temporalio/error.rb +156 -0
  111. data/lib/temporalio/internal/bridge/3.2/temporalio_bridge.so +0 -0
  112. data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.so +0 -0
  113. data/lib/temporalio/internal/bridge/3.4/temporalio_bridge.so +0 -0
  114. data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +34 -0
  115. data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +32 -0
  116. data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +33 -0
  117. data/lib/temporalio/internal/bridge/api/common/common.rb +27 -0
  118. data/lib/temporalio/internal/bridge/api/core_interface.rb +40 -0
  119. data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +27 -0
  120. data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +34 -0
  121. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +56 -0
  122. data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +58 -0
  123. data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +31 -0
  124. data/lib/temporalio/internal/bridge/api.rb +3 -0
  125. data/lib/temporalio/internal/bridge/client.rb +95 -0
  126. data/lib/temporalio/internal/bridge/runtime.rb +56 -0
  127. data/lib/temporalio/internal/bridge/testing.rb +69 -0
  128. data/lib/temporalio/internal/bridge/worker.rb +109 -0
  129. data/lib/temporalio/internal/bridge.rb +36 -0
  130. data/lib/temporalio/internal/client/implementation.rb +926 -0
  131. data/lib/temporalio/internal/metric.rb +122 -0
  132. data/lib/temporalio/internal/proto_utils.rb +165 -0
  133. data/lib/temporalio/internal/worker/activity_worker.rb +448 -0
  134. data/lib/temporalio/internal/worker/multi_runner.rb +213 -0
  135. data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
  136. data/lib/temporalio/internal/worker/workflow_instance/context.rb +391 -0
  137. data/lib/temporalio/internal/worker/workflow_instance/details.rb +49 -0
  138. data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +32 -0
  139. data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +22 -0
  140. data/lib/temporalio/internal/worker/workflow_instance/handler_execution.rb +25 -0
  141. data/lib/temporalio/internal/worker/workflow_instance/handler_hash.rb +41 -0
  142. data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +97 -0
  143. data/lib/temporalio/internal/worker/workflow_instance/inbound_implementation.rb +62 -0
  144. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +404 -0
  145. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +37 -0
  146. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb +40 -0
  147. data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +183 -0
  148. data/lib/temporalio/internal/worker/workflow_instance.rb +800 -0
  149. data/lib/temporalio/internal/worker/workflow_worker.rb +249 -0
  150. data/lib/temporalio/internal.rb +7 -0
  151. data/lib/temporalio/metric.rb +109 -0
  152. data/lib/temporalio/priority.rb +59 -0
  153. data/lib/temporalio/retry_policy.rb +74 -0
  154. data/lib/temporalio/runtime/metric_buffer.rb +94 -0
  155. data/lib/temporalio/runtime.rb +352 -0
  156. data/lib/temporalio/scoped_logger.rb +96 -0
  157. data/lib/temporalio/search_attributes.rb +356 -0
  158. data/lib/temporalio/testing/activity_environment.rb +175 -0
  159. data/lib/temporalio/testing/workflow_environment.rb +406 -0
  160. data/lib/temporalio/testing.rb +10 -0
  161. data/lib/temporalio/version.rb +5 -0
  162. data/lib/temporalio/versioning_override.rb +55 -0
  163. data/lib/temporalio/worker/activity_executor/fiber.rb +49 -0
  164. data/lib/temporalio/worker/activity_executor/thread_pool.rb +46 -0
  165. data/lib/temporalio/worker/activity_executor.rb +55 -0
  166. data/lib/temporalio/worker/deployment_options.rb +45 -0
  167. data/lib/temporalio/worker/interceptor.rb +367 -0
  168. data/lib/temporalio/worker/poller_behavior.rb +61 -0
  169. data/lib/temporalio/worker/thread_pool.rb +237 -0
  170. data/lib/temporalio/worker/tuner.rb +189 -0
  171. data/lib/temporalio/worker/workflow_executor/thread_pool.rb +236 -0
  172. data/lib/temporalio/worker/workflow_executor.rb +26 -0
  173. data/lib/temporalio/worker/workflow_replayer.rb +349 -0
  174. data/lib/temporalio/worker.rb +633 -0
  175. data/lib/temporalio/worker_deployment_version.rb +67 -0
  176. data/lib/temporalio/workflow/activity_cancellation_type.rb +20 -0
  177. data/lib/temporalio/workflow/child_workflow_cancellation_type.rb +21 -0
  178. data/lib/temporalio/workflow/child_workflow_handle.rb +43 -0
  179. data/lib/temporalio/workflow/definition.rb +680 -0
  180. data/lib/temporalio/workflow/external_workflow_handle.rb +41 -0
  181. data/lib/temporalio/workflow/future.rb +151 -0
  182. data/lib/temporalio/workflow/handler_unfinished_policy.rb +13 -0
  183. data/lib/temporalio/workflow/info.rb +107 -0
  184. data/lib/temporalio/workflow/parent_close_policy.rb +19 -0
  185. data/lib/temporalio/workflow/update_info.rb +20 -0
  186. data/lib/temporalio/workflow.rb +594 -0
  187. data/lib/temporalio/workflow_history.rb +47 -0
  188. data/lib/temporalio.rb +12 -0
  189. data/temporalio.gemspec +31 -0
  190. metadata +267 -0
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/workflow'
4
+
5
+ module Temporalio
6
+ module Internal
7
+ module Worker
8
+ class WorkflowInstance
9
+ # Class that installs {::TracePoint} to disallow illegal calls.
10
+ class IllegalCallTracer
11
+ def self.frozen_validated_illegal_calls(illegal_calls)
12
+ illegal_calls.to_h do |key, val|
13
+ raise TypeError, 'Invalid illegal call map, top-level key must be a String' unless key.is_a?(String)
14
+
15
+ # @type var fixed_val: :all | Hash[Symbol, bool]
16
+ fixed_val = case val
17
+ when Array
18
+ val.to_h do |sub_val|
19
+ unless sub_val.is_a?(Symbol)
20
+ raise TypeError,
21
+ 'Invalid illegal call map, each value must be a Symbol'
22
+ end
23
+
24
+ [sub_val, true]
25
+ end.freeze
26
+ when :all
27
+ :all
28
+ else
29
+ raise TypeError, 'Invalid illegal call map, top-level value must be an Array or :all'
30
+ end
31
+
32
+ [key.frozen? ? key : key.dup.freeze, fixed_val]
33
+ end.freeze
34
+ end
35
+
36
+ # Illegal calls are Hash[String, :all | Hash[Symbol, Bool]]
37
+ def initialize(illegal_calls)
38
+ @tracepoint = TracePoint.new(:call, :c_call) do |tp|
39
+ # Manual check for proper thread since we have seen issues in Ruby 3.2 where it leaks
40
+ next unless Thread.current == @enabled_thread
41
+
42
+ cls = tp.defined_class
43
+ next unless cls.is_a?(Module)
44
+
45
+ # Extract the class name from the defined class. This is more difficult than it seems because you have to
46
+ # resolve the attached object of the singleton class. But in older Ruby (at least <= 3.1), the singleton
47
+ # class of things like `Date` does not have `attached_object` so you have to fall back in these rare cases
48
+ # to parsing the string output. Reaching the string parsing component is rare, so this should not have
49
+ # significant performance impact.
50
+ cls_name = if cls.singleton_class?
51
+ if cls.respond_to?(:attached_object)
52
+ cls = cls.attached_object # steep:ignore
53
+ next unless cls.is_a?(Module)
54
+
55
+ cls.name.to_s
56
+ else
57
+ cls.to_s.delete_prefix('#<Class:').delete_suffix('>')
58
+ end
59
+ else
60
+ cls.name.to_s
61
+ end
62
+
63
+ # Check if the call is considered illegal
64
+ vals = illegal_calls[cls_name]
65
+ if vals == :all || vals&.[](tp.callee_id) # steep:ignore
66
+ raise Workflow::NondeterminismError,
67
+ "Cannot access #{cls_name} #{tp.callee_id} from inside a " \
68
+ 'workflow. If this is known to be safe, the code can be run in ' \
69
+ 'a Temporalio::Workflow::Unsafe.illegal_call_tracing_disabled block.'
70
+ end
71
+ end
72
+ end
73
+
74
+ def enable(&block)
75
+ # We've seen leaking issues in Ruby 3.2 where the TracePoint inadvertently remains enabled even for threads
76
+ # that it was not started on. So we will check the thread ourselves.
77
+ @enabled_thread = Thread.current
78
+ @tracepoint.enable do
79
+ block.call
80
+ ensure
81
+ @enabled_thread = nil
82
+ end
83
+ end
84
+
85
+ def disable(&block)
86
+ previous_thread = @enabled_thread
87
+ @tracepoint.disable do
88
+ block.call
89
+ ensure
90
+ @enabled_thread = previous_thread
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/internal/worker/workflow_instance'
4
+ require 'temporalio/worker/interceptor'
5
+ require 'temporalio/workflow'
6
+
7
+ module Temporalio
8
+ module Internal
9
+ module Worker
10
+ class WorkflowInstance
11
+ # Root implementation of the inbound interceptor.
12
+ class InboundImplementation < Temporalio::Worker::Interceptor::Workflow::Inbound
13
+ def initialize(instance)
14
+ super(nil) # steep:ignore
15
+ @instance = instance
16
+ end
17
+
18
+ def init(outbound)
19
+ @instance.context._outbound = outbound
20
+ end
21
+
22
+ def execute(input)
23
+ @instance.instance.execute(*input.args)
24
+ end
25
+
26
+ def handle_signal(input)
27
+ invoke_handler(input.signal, input)
28
+ end
29
+
30
+ def handle_query(input)
31
+ invoke_handler(input.query, input)
32
+ end
33
+
34
+ def validate_update(input)
35
+ invoke_handler(input.update, input, to_invoke: input.definition.validator_to_invoke)
36
+ end
37
+
38
+ def handle_update(input)
39
+ invoke_handler(input.update, input)
40
+ end
41
+
42
+ private
43
+
44
+ def invoke_handler(name, input, to_invoke: input.definition.to_invoke)
45
+ args = input.args
46
+ # Add name as first param if dynamic
47
+ args = [name] + args if input.definition.name.nil?
48
+ # Assume symbol or proc
49
+ case to_invoke
50
+ when Symbol
51
+ @instance.instance.send(to_invoke, *args)
52
+ when Proc
53
+ to_invoke.call(*args)
54
+ else
55
+ raise "Unrecognized invocation type #{to_invoke.class}"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,404 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/activity/definition'
4
+ require 'temporalio/cancellation'
5
+ require 'temporalio/error'
6
+ require 'temporalio/internal/bridge/api'
7
+ require 'temporalio/internal/proto_utils'
8
+ require 'temporalio/internal/worker/workflow_instance'
9
+ require 'temporalio/worker/interceptor'
10
+ require 'temporalio/workflow'
11
+ require 'temporalio/workflow/child_workflow_handle'
12
+
13
+ module Temporalio
14
+ module Internal
15
+ module Worker
16
+ class WorkflowInstance
17
+ # Root implementation of the outbound interceptor.
18
+ class OutboundImplementation < Temporalio::Worker::Interceptor::Workflow::Outbound
19
+ def initialize(instance)
20
+ super(nil) # steep:ignore
21
+ @instance = instance
22
+ @activity_counter = 0
23
+ @timer_counter = 0
24
+ @child_counter = 0
25
+ @external_signal_counter = 0
26
+ @external_cancel_counter = 0
27
+ end
28
+
29
+ def cancel_external_workflow(input)
30
+ # Add command
31
+ seq = (@external_cancel_counter += 1)
32
+ cmd = Bridge::Api::WorkflowCommands::RequestCancelExternalWorkflowExecution.new(
33
+ seq:,
34
+ workflow_execution: Bridge::Api::Common::NamespacedWorkflowExecution.new(
35
+ namespace: @instance.info.namespace,
36
+ workflow_id: input.id,
37
+ run_id: input.run_id
38
+ )
39
+ )
40
+ @instance.add_command(
41
+ Bridge::Api::WorkflowCommands::WorkflowCommand.new(request_cancel_external_workflow_execution: cmd)
42
+ )
43
+ @instance.pending_external_cancels[seq] = Fiber.current
44
+
45
+ # Wait
46
+ resolution = Fiber.yield
47
+
48
+ # Raise if resolution has failure
49
+ return unless resolution.failure
50
+
51
+ raise @instance.failure_converter.from_failure(resolution.failure, @instance.payload_converter)
52
+ end
53
+
54
+ def execute_activity(input)
55
+ if input.schedule_to_close_timeout.nil? && input.start_to_close_timeout.nil?
56
+ raise ArgumentError, 'Activity must have schedule_to_close_timeout or start_to_close_timeout'
57
+ end
58
+
59
+ execute_activity_with_local_backoffs(local: false, cancellation: input.cancellation) do
60
+ seq = (@activity_counter += 1)
61
+ @instance.add_command(
62
+ Bridge::Api::WorkflowCommands::WorkflowCommand.new(
63
+ schedule_activity: Bridge::Api::WorkflowCommands::ScheduleActivity.new(
64
+ seq:,
65
+ activity_id: input.activity_id || seq.to_s,
66
+ activity_type: input.activity,
67
+ task_queue: input.task_queue,
68
+ headers: ProtoUtils.headers_to_proto_hash(input.headers, @instance.payload_converter),
69
+ arguments: ProtoUtils.convert_to_payload_array(@instance.payload_converter, input.args),
70
+ schedule_to_close_timeout: ProtoUtils.seconds_to_duration(input.schedule_to_close_timeout),
71
+ schedule_to_start_timeout: ProtoUtils.seconds_to_duration(input.schedule_to_start_timeout),
72
+ start_to_close_timeout: ProtoUtils.seconds_to_duration(input.start_to_close_timeout),
73
+ heartbeat_timeout: ProtoUtils.seconds_to_duration(input.heartbeat_timeout),
74
+ retry_policy: input.retry_policy&._to_proto,
75
+ cancellation_type: input.cancellation_type,
76
+ do_not_eagerly_execute: input.disable_eager_execution,
77
+ priority: input.priority._to_proto
78
+ ),
79
+ user_metadata: ProtoUtils.to_user_metadata(input.summary, nil, @instance.payload_converter)
80
+ )
81
+ )
82
+ seq
83
+ end
84
+ end
85
+
86
+ def execute_local_activity(input)
87
+ if input.schedule_to_close_timeout.nil? && input.start_to_close_timeout.nil?
88
+ raise ArgumentError, 'Activity must have schedule_to_close_timeout or start_to_close_timeout'
89
+ end
90
+
91
+ @instance.assert_valid_local_activity.call(input.activity)
92
+
93
+ execute_activity_with_local_backoffs(local: true, cancellation: input.cancellation) do |do_backoff|
94
+ seq = (@activity_counter += 1)
95
+ @instance.add_command(
96
+ Bridge::Api::WorkflowCommands::WorkflowCommand.new(
97
+ schedule_local_activity: Bridge::Api::WorkflowCommands::ScheduleLocalActivity.new(
98
+ seq:,
99
+ activity_id: input.activity_id || seq.to_s,
100
+ activity_type: input.activity,
101
+ headers: ProtoUtils.headers_to_proto_hash(input.headers, @instance.payload_converter),
102
+ arguments: ProtoUtils.convert_to_payload_array(@instance.payload_converter, input.args),
103
+ schedule_to_close_timeout: ProtoUtils.seconds_to_duration(input.schedule_to_close_timeout),
104
+ schedule_to_start_timeout: ProtoUtils.seconds_to_duration(input.schedule_to_start_timeout),
105
+ start_to_close_timeout: ProtoUtils.seconds_to_duration(input.start_to_close_timeout),
106
+ retry_policy: input.retry_policy&._to_proto,
107
+ cancellation_type: input.cancellation_type,
108
+ local_retry_threshold: ProtoUtils.seconds_to_duration(input.local_retry_threshold),
109
+ attempt: do_backoff&.attempt || 0,
110
+ original_schedule_time: do_backoff&.original_schedule_time
111
+ )
112
+ )
113
+ )
114
+ seq
115
+ end
116
+ end
117
+
118
+ def execute_activity_with_local_backoffs(local:, cancellation:, &)
119
+ # We do not even want to schedule if the cancellation is already cancelled. We choose to use canceled
120
+ # failure instead of wrapping in activity failure which is similar to what other SDKs do, with the accepted
121
+ # tradeoff that it makes rescue more difficult (hence the presence of Error.canceled? helper).
122
+ raise Error::CanceledError, 'Activity canceled before scheduled' if cancellation.canceled?
123
+
124
+ # This has to be done in a loop for local activity backoff
125
+ last_local_backoff = nil
126
+ loop do
127
+ result = execute_activity_once(local:, cancellation:, last_local_backoff:, &)
128
+ return result unless result.is_a?(Bridge::Api::ActivityResult::DoBackoff)
129
+
130
+ # @type var result: untyped
131
+ last_local_backoff = result
132
+ # Have to sleep the amount of the backoff, which can be canceled with the same cancellation
133
+ # TODO(cretz): What should this cancellation raise?
134
+ Workflow.sleep(ProtoUtils.duration_to_seconds(result.backoff_duration), cancellation:)
135
+ end
136
+ end
137
+
138
+ # If this doesn't raise, it returns success | DoBackoff
139
+ def execute_activity_once(local:, cancellation:, last_local_backoff:, &)
140
+ # Add to pending activities (removed by the resolver)
141
+ seq = yield last_local_backoff
142
+ @instance.pending_activities[seq] = Fiber.current
143
+
144
+ # Add cancellation hook
145
+ cancel_callback_key = cancellation.add_cancel_callback do
146
+ # Only if the activity is present still
147
+ if @instance.pending_activities.include?(seq)
148
+ if local
149
+ @instance.add_command(
150
+ Bridge::Api::WorkflowCommands::WorkflowCommand.new(
151
+ request_cancel_local_activity: Bridge::Api::WorkflowCommands::RequestCancelLocalActivity.new(seq:)
152
+ )
153
+ )
154
+ else
155
+ @instance.add_command(
156
+ Bridge::Api::WorkflowCommands::WorkflowCommand.new(
157
+ request_cancel_activity: Bridge::Api::WorkflowCommands::RequestCancelActivity.new(seq:)
158
+ )
159
+ )
160
+ end
161
+ end
162
+ end
163
+
164
+ # Wait
165
+ resolution = Fiber.yield
166
+
167
+ # Remove cancellation callback
168
+ cancellation.remove_cancel_callback(cancel_callback_key)
169
+
170
+ case resolution.status
171
+ when :completed
172
+ @instance.payload_converter.from_payload(resolution.completed.result)
173
+ when :failed
174
+ raise @instance.failure_converter.from_failure(resolution.failed.failure, @instance.payload_converter)
175
+ when :cancelled
176
+ raise @instance.failure_converter.from_failure(resolution.cancelled.failure, @instance.payload_converter)
177
+ when :backoff
178
+ resolution.backoff
179
+ else
180
+ raise "Unrecognized resolution status: #{resolution.status}"
181
+ end
182
+ end
183
+
184
+ def initialize_continue_as_new_error(input)
185
+ # Do nothing
186
+ end
187
+
188
+ def signal_child_workflow(input)
189
+ _signal_external_workflow(
190
+ id: input.id,
191
+ run_id: nil,
192
+ child: true,
193
+ signal: input.signal,
194
+ args: input.args,
195
+ cancellation: input.cancellation,
196
+ headers: input.headers
197
+ )
198
+ end
199
+
200
+ def signal_external_workflow(input)
201
+ _signal_external_workflow(
202
+ id: input.id,
203
+ run_id: input.run_id,
204
+ child: false,
205
+ signal: input.signal,
206
+ args: input.args,
207
+ cancellation: input.cancellation,
208
+ headers: input.headers
209
+ )
210
+ end
211
+
212
+ def _signal_external_workflow(id:, run_id:, child:, signal:, args:, cancellation:, headers:)
213
+ raise Error::CanceledError, 'Signal canceled before scheduled' if cancellation.canceled?
214
+
215
+ # Add command
216
+ seq = (@external_signal_counter += 1)
217
+ cmd = Bridge::Api::WorkflowCommands::SignalExternalWorkflowExecution.new(
218
+ seq:,
219
+ signal_name: signal,
220
+ args: ProtoUtils.convert_to_payload_array(@instance.payload_converter, args),
221
+ headers: ProtoUtils.headers_to_proto_hash(headers, @instance.payload_converter)
222
+ )
223
+ if child
224
+ cmd.child_workflow_id = id
225
+ else
226
+ cmd.workflow_execution = Bridge::Api::Common::NamespacedWorkflowExecution.new(
227
+ namespace: @instance.info.namespace,
228
+ workflow_id: id,
229
+ run_id:
230
+ )
231
+ end
232
+ @instance.add_command(
233
+ Bridge::Api::WorkflowCommands::WorkflowCommand.new(signal_external_workflow_execution: cmd)
234
+ )
235
+ @instance.pending_external_signals[seq] = Fiber.current
236
+
237
+ # Add a cancellation callback
238
+ cancel_callback_key = cancellation.add_cancel_callback do
239
+ # Add the command but do not raise, we will let resolution do that
240
+ @instance.add_command(
241
+ Bridge::Api::WorkflowCommands::WorkflowCommand.new(
242
+ cancel_signal_workflow: Bridge::Api::WorkflowCommands::CancelSignalWorkflow.new(seq:)
243
+ )
244
+ )
245
+ end
246
+
247
+ # Wait
248
+ resolution = Fiber.yield
249
+
250
+ # Remove cancellation callback
251
+ cancellation.remove_cancel_callback(cancel_callback_key)
252
+
253
+ # Raise if resolution has failure
254
+ return unless resolution.failure
255
+
256
+ raise @instance.failure_converter.from_failure(resolution.failure, @instance.payload_converter)
257
+ end
258
+
259
+ def sleep(input)
260
+ # If already cancelled, raise as such
261
+ if input.cancellation.canceled?
262
+ raise Error::CanceledError,
263
+ input.cancellation.canceled_reason || 'Timer canceled before started'
264
+ end
265
+
266
+ # Disallow negative durations
267
+ raise ArgumentError, 'Sleep duration cannot be less than 0' if input.duration&.negative?
268
+
269
+ # If the duration is infinite, just wait for cancellation
270
+ if input.duration.nil?
271
+ input.cancellation.wait
272
+ raise Error::CanceledError, input.cancellation.canceled_reason || 'Timer canceled'
273
+ end
274
+
275
+ # If duration is zero, we make it one millisecond. It was decided a 0 duration still makes a timer to ensure
276
+ # determinism if a timer's duration is altered from non-zero to zero or vice versa.
277
+ duration = input.duration
278
+ duration = 0.001 if duration.zero?
279
+
280
+ # Add command
281
+ seq = (@timer_counter += 1)
282
+ @instance.add_command(
283
+ Bridge::Api::WorkflowCommands::WorkflowCommand.new(
284
+ start_timer: Bridge::Api::WorkflowCommands::StartTimer.new(
285
+ seq:,
286
+ start_to_fire_timeout: ProtoUtils.seconds_to_duration(duration)
287
+ ),
288
+ user_metadata: ProtoUtils.to_user_metadata(input.summary, nil, @instance.payload_converter)
289
+ )
290
+ )
291
+ @instance.pending_timers[seq] = Fiber.current
292
+
293
+ # Add a cancellation callback
294
+ cancel_callback_key = input.cancellation.add_cancel_callback do
295
+ # Only if the timer is still present
296
+ fiber = @instance.pending_timers.delete(seq)
297
+ if fiber
298
+ # Add the command for cancel then raise
299
+ @instance.add_command(
300
+ Bridge::Api::WorkflowCommands::WorkflowCommand.new(
301
+ cancel_timer: Bridge::Api::WorkflowCommands::CancelTimer.new(seq:)
302
+ )
303
+ )
304
+ if fiber.alive?
305
+ fiber.raise(Error::CanceledError.new(input.cancellation.canceled_reason || 'Timer canceled'))
306
+ end
307
+ end
308
+ end
309
+
310
+ # Wait
311
+ Fiber.yield
312
+
313
+ # Remove cancellation callback (only needed on success)
314
+ input.cancellation.remove_cancel_callback(cancel_callback_key)
315
+ end
316
+
317
+ def start_child_workflow(input)
318
+ raise Error::CanceledError, 'Child canceled before scheduled' if input.cancellation.canceled?
319
+
320
+ # Add the command
321
+ seq = (@child_counter += 1)
322
+ @instance.add_command(
323
+ Bridge::Api::WorkflowCommands::WorkflowCommand.new(
324
+ start_child_workflow_execution: Bridge::Api::WorkflowCommands::StartChildWorkflowExecution.new(
325
+ seq:,
326
+ namespace: @instance.info.namespace,
327
+ workflow_id: input.id,
328
+ workflow_type: input.workflow,
329
+ task_queue: input.task_queue,
330
+ input: ProtoUtils.convert_to_payload_array(@instance.payload_converter, input.args),
331
+ workflow_execution_timeout: ProtoUtils.seconds_to_duration(input.execution_timeout),
332
+ workflow_run_timeout: ProtoUtils.seconds_to_duration(input.run_timeout),
333
+ workflow_task_timeout: ProtoUtils.seconds_to_duration(input.task_timeout),
334
+ parent_close_policy: input.parent_close_policy,
335
+ workflow_id_reuse_policy: input.id_reuse_policy,
336
+ retry_policy: input.retry_policy&._to_proto,
337
+ cron_schedule: input.cron_schedule,
338
+ headers: ProtoUtils.headers_to_proto_hash(input.headers, @instance.payload_converter),
339
+ memo: ProtoUtils.memo_to_proto_hash(input.memo, @instance.payload_converter),
340
+ search_attributes: input.search_attributes&._to_proto_hash,
341
+ cancellation_type: input.cancellation_type,
342
+ priority: input.priority._to_proto
343
+ ),
344
+ user_metadata: ProtoUtils.to_user_metadata(
345
+ input.static_summary, input.static_details, @instance.payload_converter
346
+ )
347
+ )
348
+ )
349
+
350
+ # Set as pending start and register cancel callback
351
+ @instance.pending_child_workflow_starts[seq] = Fiber.current
352
+ cancel_callback_key = input.cancellation.add_cancel_callback do
353
+ # Send cancel if in start or pending
354
+ if @instance.pending_child_workflow_starts.include?(seq) ||
355
+ @instance.pending_child_workflows.include?(seq)
356
+ @instance.add_command(
357
+ Bridge::Api::WorkflowCommands::WorkflowCommand.new(
358
+ cancel_child_workflow_execution: Bridge::Api::WorkflowCommands::CancelChildWorkflowExecution.new(
359
+ child_workflow_seq: seq
360
+ )
361
+ )
362
+ )
363
+ end
364
+ end
365
+
366
+ # Wait for start
367
+ resolution = Fiber.yield
368
+
369
+ case resolution.status
370
+ when :succeeded
371
+ # Create handle, passing along the cancel callback key, and set it as pending
372
+ handle = ChildWorkflowHandle.new(
373
+ id: input.id,
374
+ first_execution_run_id: resolution.succeeded.run_id,
375
+ instance: @instance,
376
+ cancellation: input.cancellation,
377
+ cancel_callback_key:
378
+ )
379
+ @instance.pending_child_workflows[seq] = handle
380
+ handle
381
+ when :failed
382
+ # Remove cancel callback and handle failure
383
+ input.cancellation.remove_cancel_callback(cancel_callback_key)
384
+ if resolution.failed.cause == :START_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_WORKFLOW_ALREADY_EXISTS
385
+ raise Error::WorkflowAlreadyStartedError.new(
386
+ workflow_id: resolution.failed.workflow_id,
387
+ workflow_type: resolution.failed.workflow_type,
388
+ run_id: nil
389
+ )
390
+ end
391
+ raise "Unknown child start fail cause: #{resolution.failed.cause}"
392
+ when :cancelled
393
+ # Remove cancel callback and handle cancel
394
+ input.cancellation.remove_cancel_callback(cancel_callback_key)
395
+ raise @instance.failure_converter.from_failure(resolution.cancelled.failure, @instance.payload_converter)
396
+ else
397
+ raise "Unknown resolution status: #{resolution.status}"
398
+ end
399
+ end
400
+ end
401
+ end
402
+ end
403
+ end
404
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/scoped_logger'
4
+ require 'temporalio/workflow'
5
+
6
+ module Temporalio
7
+ module Internal
8
+ module Worker
9
+ class WorkflowInstance
10
+ # Wrapper for a scoped logger that does not log on replay.
11
+ class ReplaySafeLogger < ScopedLogger
12
+ def initialize(logger:, instance:)
13
+ @instance = instance
14
+ @replay_safety_disabled = false
15
+ super(logger)
16
+ end
17
+
18
+ def replay_safety_disabled(&)
19
+ @replay_safety_disabled = true
20
+ yield
21
+ ensure
22
+ @replay_safety_disabled = false
23
+ end
24
+
25
+ def add(...)
26
+ if !@replay_safety_disabled && Temporalio::Workflow.in_workflow? && Temporalio::Workflow::Unsafe.replaying?
27
+ return true
28
+ end
29
+
30
+ # Disable illegal call tracing for the log call
31
+ @instance.illegal_call_tracing_disabled { super }
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/scoped_logger'
4
+
5
+ module Temporalio
6
+ module Internal
7
+ module Worker
8
+ class WorkflowInstance
9
+ # Wrapper for a metric that does not log on replay.
10
+ class ReplaySafeMetric < SimpleDelegator
11
+ def record(value, additional_attributes: nil)
12
+ return if Temporalio::Workflow.in_workflow? && Temporalio::Workflow::Unsafe.replaying?
13
+
14
+ super
15
+ end
16
+
17
+ def with_additional_attributes(additional_attributes)
18
+ ReplaySafeMetric.new(super)
19
+ end
20
+
21
+ class Meter < SimpleDelegator
22
+ def create_metric(
23
+ metric_type,
24
+ name,
25
+ description: nil,
26
+ unit: nil,
27
+ value_type: :integer
28
+ )
29
+ ReplaySafeMetric.new(super)
30
+ end
31
+
32
+ def with_additional_attributes(additional_attributes)
33
+ Meter.new(super)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end