temporalio 0.4.0-aarch64-linux-musl

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 (183) 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/complete_async_error.rb +11 -0
  6. data/lib/temporalio/activity/context.rb +123 -0
  7. data/lib/temporalio/activity/definition.rb +192 -0
  8. data/lib/temporalio/activity/info.rb +67 -0
  9. data/lib/temporalio/activity.rb +12 -0
  10. data/lib/temporalio/api/activity/v1/message.rb +25 -0
  11. data/lib/temporalio/api/batch/v1/message.rb +36 -0
  12. data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
  13. data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +126 -0
  14. data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +25 -0
  15. data/lib/temporalio/api/cloud/cloudservice.rb +3 -0
  16. data/lib/temporalio/api/cloud/identity/v1/message.rb +41 -0
  17. data/lib/temporalio/api/cloud/namespace/v1/message.rb +42 -0
  18. data/lib/temporalio/api/cloud/nexus/v1/message.rb +31 -0
  19. data/lib/temporalio/api/cloud/operation/v1/message.rb +28 -0
  20. data/lib/temporalio/api/cloud/region/v1/message.rb +24 -0
  21. data/lib/temporalio/api/cloud/resource/v1/message.rb +23 -0
  22. data/lib/temporalio/api/cloud/sink/v1/message.rb +24 -0
  23. data/lib/temporalio/api/cloud/usage/v1/message.rb +31 -0
  24. data/lib/temporalio/api/command/v1/message.rb +46 -0
  25. data/lib/temporalio/api/common/v1/grpc_status.rb +23 -0
  26. data/lib/temporalio/api/common/v1/message.rb +48 -0
  27. data/lib/temporalio/api/deployment/v1/message.rb +38 -0
  28. data/lib/temporalio/api/enums/v1/batch_operation.rb +22 -0
  29. data/lib/temporalio/api/enums/v1/command_type.rb +21 -0
  30. data/lib/temporalio/api/enums/v1/common.rb +26 -0
  31. data/lib/temporalio/api/enums/v1/deployment.rb +23 -0
  32. data/lib/temporalio/api/enums/v1/event_type.rb +21 -0
  33. data/lib/temporalio/api/enums/v1/failed_cause.rb +26 -0
  34. data/lib/temporalio/api/enums/v1/namespace.rb +23 -0
  35. data/lib/temporalio/api/enums/v1/nexus.rb +21 -0
  36. data/lib/temporalio/api/enums/v1/query.rb +22 -0
  37. data/lib/temporalio/api/enums/v1/reset.rb +23 -0
  38. data/lib/temporalio/api/enums/v1/schedule.rb +21 -0
  39. data/lib/temporalio/api/enums/v1/task_queue.rb +25 -0
  40. data/lib/temporalio/api/enums/v1/update.rb +22 -0
  41. data/lib/temporalio/api/enums/v1/workflow.rb +31 -0
  42. data/lib/temporalio/api/errordetails/v1/message.rb +44 -0
  43. data/lib/temporalio/api/export/v1/message.rb +24 -0
  44. data/lib/temporalio/api/failure/v1/message.rb +37 -0
  45. data/lib/temporalio/api/filter/v1/message.rb +27 -0
  46. data/lib/temporalio/api/history/v1/message.rb +92 -0
  47. data/lib/temporalio/api/namespace/v1/message.rb +31 -0
  48. data/lib/temporalio/api/nexus/v1/message.rb +41 -0
  49. data/lib/temporalio/api/operatorservice/v1/request_response.rb +49 -0
  50. data/lib/temporalio/api/operatorservice/v1/service.rb +23 -0
  51. data/lib/temporalio/api/operatorservice.rb +3 -0
  52. data/lib/temporalio/api/payload_visitor.rb +1581 -0
  53. data/lib/temporalio/api/protocol/v1/message.rb +23 -0
  54. data/lib/temporalio/api/query/v1/message.rb +28 -0
  55. data/lib/temporalio/api/replication/v1/message.rb +26 -0
  56. data/lib/temporalio/api/schedule/v1/message.rb +43 -0
  57. data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +25 -0
  58. data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +21 -0
  59. data/lib/temporalio/api/sdk/v1/user_metadata.rb +23 -0
  60. data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +23 -0
  61. data/lib/temporalio/api/taskqueue/v1/message.rb +48 -0
  62. data/lib/temporalio/api/testservice/v1/request_response.rb +31 -0
  63. data/lib/temporalio/api/testservice/v1/service.rb +23 -0
  64. data/lib/temporalio/api/update/v1/message.rb +33 -0
  65. data/lib/temporalio/api/version/v1/message.rb +26 -0
  66. data/lib/temporalio/api/workflow/v1/message.rb +51 -0
  67. data/lib/temporalio/api/workflowservice/v1/request_response.rb +233 -0
  68. data/lib/temporalio/api/workflowservice/v1/service.rb +23 -0
  69. data/lib/temporalio/api/workflowservice.rb +3 -0
  70. data/lib/temporalio/api.rb +15 -0
  71. data/lib/temporalio/cancellation.rb +170 -0
  72. data/lib/temporalio/client/activity_id_reference.rb +32 -0
  73. data/lib/temporalio/client/async_activity_handle.rb +85 -0
  74. data/lib/temporalio/client/connection/cloud_service.rb +726 -0
  75. data/lib/temporalio/client/connection/operator_service.rb +201 -0
  76. data/lib/temporalio/client/connection/service.rb +42 -0
  77. data/lib/temporalio/client/connection/test_service.rb +111 -0
  78. data/lib/temporalio/client/connection/workflow_service.rb +1251 -0
  79. data/lib/temporalio/client/connection.rb +316 -0
  80. data/lib/temporalio/client/interceptor.rb +455 -0
  81. data/lib/temporalio/client/schedule.rb +991 -0
  82. data/lib/temporalio/client/schedule_handle.rb +126 -0
  83. data/lib/temporalio/client/with_start_workflow_operation.rb +115 -0
  84. data/lib/temporalio/client/workflow_execution.rb +119 -0
  85. data/lib/temporalio/client/workflow_execution_count.rb +36 -0
  86. data/lib/temporalio/client/workflow_execution_status.rb +18 -0
  87. data/lib/temporalio/client/workflow_handle.rb +389 -0
  88. data/lib/temporalio/client/workflow_query_reject_condition.rb +14 -0
  89. data/lib/temporalio/client/workflow_update_handle.rb +65 -0
  90. data/lib/temporalio/client/workflow_update_wait_stage.rb +17 -0
  91. data/lib/temporalio/client.rb +607 -0
  92. data/lib/temporalio/common_enums.rb +41 -0
  93. data/lib/temporalio/contrib/open_telemetry.rb +470 -0
  94. data/lib/temporalio/converters/data_converter.rb +99 -0
  95. data/lib/temporalio/converters/failure_converter.rb +202 -0
  96. data/lib/temporalio/converters/payload_codec.rb +26 -0
  97. data/lib/temporalio/converters/payload_converter/binary_null.rb +34 -0
  98. data/lib/temporalio/converters/payload_converter/binary_plain.rb +35 -0
  99. data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +42 -0
  100. data/lib/temporalio/converters/payload_converter/composite.rb +66 -0
  101. data/lib/temporalio/converters/payload_converter/encoding.rb +35 -0
  102. data/lib/temporalio/converters/payload_converter/json_plain.rb +44 -0
  103. data/lib/temporalio/converters/payload_converter/json_protobuf.rb +41 -0
  104. data/lib/temporalio/converters/payload_converter.rb +71 -0
  105. data/lib/temporalio/converters/raw_value.rb +20 -0
  106. data/lib/temporalio/converters.rb +9 -0
  107. data/lib/temporalio/error/failure.rb +219 -0
  108. data/lib/temporalio/error.rb +156 -0
  109. data/lib/temporalio/internal/bridge/3.2/temporalio_bridge.so +0 -0
  110. data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.so +0 -0
  111. data/lib/temporalio/internal/bridge/3.4/temporalio_bridge.so +0 -0
  112. data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +34 -0
  113. data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +31 -0
  114. data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +33 -0
  115. data/lib/temporalio/internal/bridge/api/common/common.rb +27 -0
  116. data/lib/temporalio/internal/bridge/api/core_interface.rb +40 -0
  117. data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +27 -0
  118. data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +33 -0
  119. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +56 -0
  120. data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +57 -0
  121. data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +31 -0
  122. data/lib/temporalio/internal/bridge/api.rb +3 -0
  123. data/lib/temporalio/internal/bridge/client.rb +95 -0
  124. data/lib/temporalio/internal/bridge/runtime.rb +56 -0
  125. data/lib/temporalio/internal/bridge/testing.rb +69 -0
  126. data/lib/temporalio/internal/bridge/worker.rb +85 -0
  127. data/lib/temporalio/internal/bridge.rb +36 -0
  128. data/lib/temporalio/internal/client/implementation.rb +922 -0
  129. data/lib/temporalio/internal/metric.rb +122 -0
  130. data/lib/temporalio/internal/proto_utils.rb +165 -0
  131. data/lib/temporalio/internal/worker/activity_worker.rb +385 -0
  132. data/lib/temporalio/internal/worker/multi_runner.rb +213 -0
  133. data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
  134. data/lib/temporalio/internal/worker/workflow_instance/context.rb +383 -0
  135. data/lib/temporalio/internal/worker/workflow_instance/details.rb +46 -0
  136. data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +32 -0
  137. data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +22 -0
  138. data/lib/temporalio/internal/worker/workflow_instance/handler_execution.rb +25 -0
  139. data/lib/temporalio/internal/worker/workflow_instance/handler_hash.rb +41 -0
  140. data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +97 -0
  141. data/lib/temporalio/internal/worker/workflow_instance/inbound_implementation.rb +62 -0
  142. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +400 -0
  143. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +37 -0
  144. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb +40 -0
  145. data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +183 -0
  146. data/lib/temporalio/internal/worker/workflow_instance.rb +774 -0
  147. data/lib/temporalio/internal/worker/workflow_worker.rb +239 -0
  148. data/lib/temporalio/internal.rb +7 -0
  149. data/lib/temporalio/metric.rb +109 -0
  150. data/lib/temporalio/retry_policy.rb +74 -0
  151. data/lib/temporalio/runtime/metric_buffer.rb +94 -0
  152. data/lib/temporalio/runtime.rb +352 -0
  153. data/lib/temporalio/scoped_logger.rb +96 -0
  154. data/lib/temporalio/search_attributes.rb +356 -0
  155. data/lib/temporalio/testing/activity_environment.rb +160 -0
  156. data/lib/temporalio/testing/workflow_environment.rb +406 -0
  157. data/lib/temporalio/testing.rb +10 -0
  158. data/lib/temporalio/version.rb +5 -0
  159. data/lib/temporalio/worker/activity_executor/fiber.rb +49 -0
  160. data/lib/temporalio/worker/activity_executor/thread_pool.rb +46 -0
  161. data/lib/temporalio/worker/activity_executor.rb +55 -0
  162. data/lib/temporalio/worker/interceptor.rb +365 -0
  163. data/lib/temporalio/worker/thread_pool.rb +237 -0
  164. data/lib/temporalio/worker/tuner.rb +189 -0
  165. data/lib/temporalio/worker/workflow_executor/thread_pool.rb +235 -0
  166. data/lib/temporalio/worker/workflow_executor.rb +26 -0
  167. data/lib/temporalio/worker/workflow_replayer.rb +350 -0
  168. data/lib/temporalio/worker.rb +603 -0
  169. data/lib/temporalio/workflow/activity_cancellation_type.rb +20 -0
  170. data/lib/temporalio/workflow/child_workflow_cancellation_type.rb +21 -0
  171. data/lib/temporalio/workflow/child_workflow_handle.rb +43 -0
  172. data/lib/temporalio/workflow/definition.rb +598 -0
  173. data/lib/temporalio/workflow/external_workflow_handle.rb +41 -0
  174. data/lib/temporalio/workflow/future.rb +151 -0
  175. data/lib/temporalio/workflow/handler_unfinished_policy.rb +13 -0
  176. data/lib/temporalio/workflow/info.rb +104 -0
  177. data/lib/temporalio/workflow/parent_close_policy.rb +19 -0
  178. data/lib/temporalio/workflow/update_info.rb +20 -0
  179. data/lib/temporalio/workflow.rb +575 -0
  180. data/lib/temporalio/workflow_history.rb +47 -0
  181. data/lib/temporalio.rb +11 -0
  182. data/temporalio.gemspec +29 -0
  183. metadata +258 -0
@@ -0,0 +1,406 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'delegate'
4
+ require 'temporalio/api'
5
+ require 'temporalio/api/testservice/v1/request_response'
6
+ require 'temporalio/client'
7
+ require 'temporalio/client/connection/test_service'
8
+ require 'temporalio/client/workflow_handle'
9
+ require 'temporalio/converters'
10
+ require 'temporalio/internal/bridge/testing'
11
+ require 'temporalio/internal/proto_utils'
12
+ require 'temporalio/runtime'
13
+ require 'temporalio/search_attributes'
14
+ require 'temporalio/version'
15
+
16
+ module Temporalio
17
+ module Testing
18
+ # Test environment with a Temporal server for running workflows and more.
19
+ class WorkflowEnvironment
20
+ # @return [Client] Client for the server.
21
+ attr_reader :client
22
+
23
+ # Start a local dev server. This is a full Temporal dev server from the CLI that by default downloaded to tmp if
24
+ # not already present. The dev server is run as a child process. All options that start with +dev_server_+ are for
25
+ # this specific implementation and therefore are not stable and may be changed as the underlying implementation
26
+ # changes.
27
+ #
28
+ # If a block is given it is passed the environment and the environment is shut down after. If a block is not
29
+ # given, the environment is returned and {shutdown} needs to be called manually.
30
+ #
31
+ # @param namespace [String] Namespace for the server.
32
+ # @param data_converter [Converters::DataConverter] Data converter for the client.
33
+ # @param interceptors [Array<Client::Interceptor>] Interceptors for the client.
34
+ # @param logger [Logger] Logger for the client.
35
+ # @param default_workflow_query_reject_condition [WorkflowQueryRejectCondition, nil] Default rejection condition
36
+ # for the client.
37
+ # @param ip [String] IP to bind to.
38
+ # @param port [Integer, nil] Port to bind on, or `nil` for random.
39
+ # @param ui [Boolean] If +true+, also starts the UI.
40
+ # @param ui_port [Integer, nil] Port to bind on if `ui` is true, or `nil` for random.
41
+ # @param search_attributes [Array<SearchAttributes::Key>] Search attributes to make available on start.
42
+ # @param runtime [Runtime] Runtime for the server and client.
43
+ # @param dev_server_existing_path [String, nil] Existing CLI path to use instead of downloading and caching to
44
+ # tmp.
45
+ # @param dev_server_database_filename [String, nil] Persistent SQLite filename to use across local server runs.
46
+ # Default of +nil+ means in-memory only.
47
+ # @param dev_server_log_format [String] Log format for CLI dev server.
48
+ # @param dev_server_log_level [String] Log level for CLI dev server.
49
+ # @param dev_server_download_version [String] Version of dev server to download and cache.
50
+ # @param dev_server_download_dest_dir [String, nil] Where to download. Defaults to tmp.
51
+ # @param dev_server_extra_args [Array<String>] Any extra arguments for the CLI dev server.
52
+ # @param dev_server_download_ttl [Float, nil] How long the automatic download should be cached for. If nil, cached
53
+ # indefinitely.
54
+ #
55
+ # @yield [environment] If a block is given, it is called with the environment and upon complete the environment is
56
+ # shutdown.
57
+ # @yieldparam environment [WorkflowEnvironment] Environment that is shut down upon block completion.
58
+ #
59
+ # @return [WorkflowEnvironment, Object] Started local server environment with client if there was no block given,
60
+ # or block result if block was given.
61
+ def self.start_local(
62
+ namespace: 'default',
63
+ data_converter: Converters::DataConverter.default,
64
+ interceptors: [],
65
+ logger: Logger.new($stdout, level: Logger::WARN),
66
+ default_workflow_query_reject_condition: nil,
67
+ ip: '127.0.0.1',
68
+ port: nil,
69
+ ui: false, # rubocop:disable Naming/MethodParameterName
70
+ ui_port: nil,
71
+ search_attributes: [],
72
+ runtime: Runtime.default,
73
+ dev_server_existing_path: nil,
74
+ dev_server_database_filename: nil,
75
+ dev_server_log_format: 'pretty',
76
+ dev_server_log_level: 'warn',
77
+ dev_server_download_version: 'default',
78
+ dev_server_download_dest_dir: nil,
79
+ dev_server_extra_args: [],
80
+ dev_server_download_ttl: nil,
81
+ &
82
+ )
83
+ # Add search attribute args
84
+ unless search_attributes.empty?
85
+ dev_server_extra_args += search_attributes.flat_map do |key|
86
+ raise 'Search attribute must be Key' unless key.is_a?(SearchAttributes::Key)
87
+
88
+ ['--search-attribute', "#{key.name}=#{SearchAttributes::IndexedValueType::PROTO_NAMES[key.type]}"]
89
+ end
90
+ end
91
+
92
+ server_options = Internal::Bridge::Testing::EphemeralServer::StartDevServerOptions.new(
93
+ existing_path: dev_server_existing_path,
94
+ sdk_name: 'sdk-ruby',
95
+ sdk_version: VERSION,
96
+ download_version: dev_server_download_version,
97
+ download_dest_dir: dev_server_download_dest_dir,
98
+ namespace:,
99
+ ip:,
100
+ port:,
101
+ database_filename: dev_server_database_filename,
102
+ ui:,
103
+ ui_port: ui ? ui_port : nil,
104
+ log_format: dev_server_log_format,
105
+ log_level: dev_server_log_level,
106
+ extra_args: dev_server_extra_args,
107
+ download_ttl: dev_server_download_ttl
108
+ )
109
+ _with_core_server(
110
+ core_server: Internal::Bridge::Testing::EphemeralServer.start_dev_server(
111
+ runtime._core_runtime, server_options
112
+ ),
113
+ namespace:,
114
+ data_converter:,
115
+ interceptors:,
116
+ logger:,
117
+ default_workflow_query_reject_condition:,
118
+ runtime:,
119
+ supports_time_skipping: false,
120
+ & # steep:ignore
121
+ )
122
+ end
123
+
124
+ # Start a time-skipping test server. This server can skip time but may not have all of the Temporal features of
125
+ # the {start_local} form. By default, the server is downloaded to tmp if not already present. The test server is
126
+ # run as a child process. All options that start with +test_server_+ are for this specific implementation and
127
+ # therefore are not stable and may be changed as the underlying implementation changes.
128
+ #
129
+ # If a block is given it is passed the environment and the environment is shut down after. If a block is not
130
+ # given, the environment is returned and {shutdown} needs to be called manually.
131
+ #
132
+ # @param data_converter [Converters::DataConverter] Data converter for the client.
133
+ # @param interceptors [Array<Client::Interceptor>] Interceptors for the client.
134
+ # @param logger [Logger] Logger for the client.
135
+ # @param default_workflow_query_reject_condition [WorkflowQueryRejectCondition, nil] Default rejection condition
136
+ # for the client.
137
+ # @param port [Integer, nil] Port to bind on, or +nil+ for random.
138
+ # @param runtime [Runtime] Runtime for the server and client.
139
+ # @param test_server_existing_path [String, nil] Existing CLI path to use instead of downloading and caching to
140
+ # tmp.
141
+ # @param test_server_download_version [String] Version of test server to download and cache.
142
+ # @param test_server_download_dest_dir [String, nil] Where to download. Defaults to tmp.
143
+ # @param test_server_extra_args [Array<String>] Any extra arguments for the test server.
144
+ # @param test_server_download_ttl [Float, nil] How long the automatic download should be cached for. If nil,
145
+ # cached indefinitely.
146
+ #
147
+ # @yield [environment] If a block is given, it is called with the environment and upon complete the environment is
148
+ # shutdown.
149
+ # @yieldparam environment [WorkflowEnvironment] Environment that is shut down upon block completion.
150
+ #
151
+ # @return [WorkflowEnvironment, Object] Started local server environment with client if there was no block given,
152
+ # or block result if block was given.
153
+ def self.start_time_skipping(
154
+ data_converter: Converters::DataConverter.default,
155
+ interceptors: [],
156
+ logger: Logger.new($stdout, level: Logger::WARN),
157
+ default_workflow_query_reject_condition: nil,
158
+ port: nil,
159
+ runtime: Runtime.default,
160
+ test_server_existing_path: nil,
161
+ test_server_download_version: 'default',
162
+ test_server_download_dest_dir: nil,
163
+ test_server_extra_args: [],
164
+ test_server_download_ttl: nil,
165
+ &
166
+ )
167
+ server_options = Internal::Bridge::Testing::EphemeralServer::StartTestServerOptions.new(
168
+ existing_path: test_server_existing_path,
169
+ sdk_name: 'sdk-ruby',
170
+ sdk_version: VERSION,
171
+ download_version: test_server_download_version,
172
+ download_dest_dir: test_server_download_dest_dir,
173
+ port:,
174
+ extra_args: test_server_extra_args,
175
+ download_ttl: test_server_download_ttl
176
+ )
177
+ _with_core_server(
178
+ core_server: Internal::Bridge::Testing::EphemeralServer.start_test_server(
179
+ runtime._core_runtime, server_options
180
+ ),
181
+ namespace: 'default',
182
+ data_converter:,
183
+ interceptors:,
184
+ logger:,
185
+ default_workflow_query_reject_condition:,
186
+ runtime:,
187
+ supports_time_skipping: true,
188
+ & # steep:ignore
189
+ )
190
+ end
191
+
192
+ # @!visibility private
193
+ def self._with_core_server(
194
+ core_server:,
195
+ namespace:,
196
+ data_converter:,
197
+ interceptors:,
198
+ logger:,
199
+ default_workflow_query_reject_condition:,
200
+ runtime:,
201
+ supports_time_skipping:
202
+ )
203
+ # Try to connect, shutdown if we can't
204
+ begin
205
+ client = Client.connect(
206
+ core_server.target,
207
+ namespace,
208
+ data_converter:,
209
+ interceptors:,
210
+ logger:,
211
+ default_workflow_query_reject_condition:,
212
+ runtime:
213
+ )
214
+ server = Ephemeral.new(client, core_server, supports_time_skipping:)
215
+ rescue Exception # rubocop:disable Lint/RescueException
216
+ core_server.shutdown
217
+ raise
218
+ end
219
+ if block_given?
220
+ begin
221
+ yield server
222
+ ensure
223
+ server.shutdown
224
+ end
225
+ else
226
+ server
227
+ end
228
+ end
229
+
230
+ # Create workflow environment to an existing server with the given client.
231
+ #
232
+ # @param client [Client] Client to existing server.
233
+ def initialize(client)
234
+ @client = client
235
+ end
236
+
237
+ # Shutdown this workflow environment.
238
+ def shutdown
239
+ # Do nothing by default
240
+ end
241
+
242
+ # @return [Boolean] Whether this environment supports time skipping.
243
+ def supports_time_skipping?
244
+ false
245
+ end
246
+
247
+ # Advanced time.
248
+ #
249
+ # If this server supports time skipping, this will immediately advance time and return. If it does not, this is
250
+ # a standard {::sleep}.
251
+ #
252
+ # @param duration [Float] Duration seconds.
253
+ def sleep(duration)
254
+ Kernel.sleep(duration)
255
+ end
256
+
257
+ # Current time of the environment.
258
+ #
259
+ # If this server supports time skipping, this will be the current time as known to the environment. If it does
260
+ # not, this is a standard {::Time.now}.
261
+ #
262
+ # @return [Time] Current time.
263
+ def current_time
264
+ Time.now
265
+ end
266
+
267
+ # Run a block with automatic time skipping disabled. This just runs the block for environments that don't support
268
+ # time skipping.
269
+ #
270
+ # @yield Block to run.
271
+ # @return [Object] Result of the block.
272
+ def auto_time_skipping_disabled(&)
273
+ raise 'Block required' unless block_given?
274
+
275
+ yield
276
+ end
277
+
278
+ # @!visibility private
279
+ class Ephemeral < WorkflowEnvironment
280
+ def initialize(client, core_server, supports_time_skipping:)
281
+ # Add our interceptor at the end of the existing interceptors that skips time
282
+ client_options = client.options.with(
283
+ interceptors: client.options.interceptors + [TimeSkippingClientInterceptor.new(self)]
284
+ )
285
+ client = Client.new(**client_options.to_h) # steep:ignore
286
+ super(client)
287
+
288
+ @auto_time_skipping = true
289
+ @core_server = core_server
290
+ @test_service = Client::Connection::TestService.new(client.connection) if supports_time_skipping
291
+ end
292
+
293
+ # @!visibility private
294
+ def shutdown
295
+ @core_server.shutdown
296
+ end
297
+
298
+ # @!visibility private
299
+ def supports_time_skipping?
300
+ !@test_service.nil?
301
+ end
302
+
303
+ # @!visibility private
304
+ def sleep(duration)
305
+ return super unless supports_time_skipping?
306
+
307
+ @test_service.unlock_time_skipping_with_sleep(
308
+ Api::TestService::V1::SleepRequest.new(duration: Internal::ProtoUtils.seconds_to_duration(duration))
309
+ )
310
+ end
311
+
312
+ # @!visibility private
313
+ def current_time
314
+ return super unless supports_time_skipping?
315
+
316
+ resp = @test_service.get_current_time(Google::Protobuf::Empty.new)
317
+ Internal::ProtoUtils.timestamp_to_time(resp.time) or raise 'Time missing'
318
+ end
319
+
320
+ # @!visibility private
321
+ def auto_time_skipping_disabled(&)
322
+ raise 'Block required' unless block_given?
323
+ return super unless supports_time_skipping?
324
+
325
+ already_disabled = @auto_time_skipping
326
+ @auto_time_skipping = false
327
+ begin
328
+ yield
329
+ ensure
330
+ @auto_time_skipping = true unless already_disabled
331
+ end
332
+ end
333
+
334
+ # @!visibility private
335
+ def time_skipping_unlocked(&)
336
+ # If disabled or unsupported, no locking/unlocking, just run and return
337
+ return yield if !supports_time_skipping? || !@auto_time_skipping
338
+
339
+ # Unlock to start time skipping, lock again to stop it
340
+ @test_service.unlock_time_skipping(Api::TestService::V1::UnlockTimeSkippingRequest.new)
341
+ user_code_success = false
342
+ begin
343
+ result = yield
344
+ user_code_success = true
345
+ result
346
+ ensure
347
+ # Lock it back
348
+ begin
349
+ @test_service.lock_time_skipping(Api::TestService::V1::LockTimeSkippingRequest.new)
350
+ rescue StandardError => e
351
+ # Re-raise if user code succeeded, otherwise swallow
352
+ raise if user_code_success
353
+
354
+ client.options.logger.error('Failed locking time skipping after error')
355
+ client.options.logger.error(e)
356
+ end
357
+ end
358
+ end
359
+ end
360
+
361
+ private_constant :Ephemeral
362
+
363
+ # @!visibility private
364
+ class TimeSkippingClientInterceptor
365
+ include Client::Interceptor
366
+
367
+ def initialize(env)
368
+ @env = env
369
+ end
370
+
371
+ # @!visibility private
372
+ def intercept_client(next_interceptor)
373
+ Outbound.new(next_interceptor, @env)
374
+ end
375
+
376
+ # @!visibility private
377
+ class Outbound < Client::Interceptor::Outbound
378
+ def initialize(next_interceptor, env)
379
+ super(next_interceptor)
380
+ @env = env
381
+ end
382
+
383
+ # @!visibility private
384
+ def start_workflow(input)
385
+ TimeSkippingWorkflowHandle.new(super, @env)
386
+ end
387
+ end
388
+
389
+ # @!visibility private
390
+ class TimeSkippingWorkflowHandle < SimpleDelegator
391
+ def initialize(handle, env)
392
+ super(handle) # steep:ignore
393
+ @env = env
394
+ end
395
+
396
+ # @!visibility private
397
+ def result(follow_runs: true, rpc_options: nil)
398
+ @env.time_skipping_unlocked { super(follow_runs:, rpc_options:) }
399
+ end
400
+ end
401
+ end
402
+
403
+ private_constant :TimeSkippingClientInterceptor
404
+ end
405
+ end
406
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/testing/activity_environment'
4
+ require 'temporalio/testing/workflow_environment'
5
+
6
+ module Temporalio
7
+ # Module for all testing environments.
8
+ module Testing
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Temporalio
4
+ VERSION = '0.4.0'
5
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/error'
4
+ require 'temporalio/worker/activity_executor'
5
+
6
+ module Temporalio
7
+ class Worker
8
+ class ActivityExecutor
9
+ # Activity executor for scheduling activites as fibers.
10
+ class Fiber
11
+ # @return [Fiber] Default/shared Fiber executor instance.
12
+ def self.default
13
+ @default ||= new
14
+ end
15
+
16
+ # @see ActivityExecutor.initialize_activity
17
+ def initialize_activity(defn)
18
+ # If there is not a current scheduler, we're going to preemptively
19
+ # fail the registration
20
+ return unless ::Fiber.current_scheduler.nil?
21
+
22
+ raise ArgumentError, "Activity '#{defn.name}' wants a fiber executor but no current fiber scheduler"
23
+ end
24
+
25
+ # @see ActivityExecutor.initialize_activity
26
+ def execute_activity(_defn, &)
27
+ ::Fiber.schedule(&)
28
+ end
29
+
30
+ # @see ActivityExecutor.activity_context
31
+ def activity_context
32
+ ::Fiber[:temporal_activity_context]
33
+ end
34
+
35
+ # @see ActivityExecutor.set_activity_context
36
+ def set_activity_context(defn, context)
37
+ ::Fiber[:temporal_activity_context] = context
38
+ # If they have opted in to raising on cancel, wire that up
39
+ return unless defn.cancel_raise
40
+
41
+ fiber = ::Fiber.current
42
+ context&.cancellation&.add_cancel_callback do
43
+ fiber.raise(Error::CanceledError.new('Activity canceled'))
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/worker/thread_pool'
4
+
5
+ module Temporalio
6
+ class Worker
7
+ class ActivityExecutor
8
+ # Activity executor for scheduling activities in their own thread using {Worker::ThreadPool}.
9
+ class ThreadPool < ActivityExecutor
10
+ # @return [ThreadPool] Default/shared thread pool executor using default thread pool.
11
+ def self.default
12
+ @default ||= new
13
+ end
14
+
15
+ # Create a new thread pool executor.
16
+ #
17
+ # @param thread_pool [Worker::ThreadPool] Thread pool to use.
18
+ def initialize(thread_pool = Worker::ThreadPool.default) # rubocop:disable Lint/MissingSuper
19
+ @thread_pool = thread_pool
20
+ end
21
+
22
+ # @see ActivityExecutor.execute_activity
23
+ def execute_activity(_defn, &)
24
+ @thread_pool.execute(&)
25
+ end
26
+
27
+ # @see ActivityExecutor.activity_context
28
+ def activity_context
29
+ Thread.current[:temporal_activity_context]
30
+ end
31
+
32
+ # @see ActivityExecutor.set_activity_context
33
+ def set_activity_context(defn, context)
34
+ Thread.current[:temporal_activity_context] = context
35
+ # If they have opted in to raising on cancel, wire that up
36
+ return unless defn.cancel_raise
37
+
38
+ thread = Thread.current
39
+ context&.cancellation&.add_cancel_callback do
40
+ thread.raise(Error::CanceledError.new('Activity canceled')) if thread[:temporal_activity_context] == context
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/worker/activity_executor/fiber'
4
+ require 'temporalio/worker/activity_executor/thread_pool'
5
+
6
+ module Temporalio
7
+ class Worker
8
+ # Base class to be extended by activity executor implementations. Most users will not use this, but rather keep with
9
+ # the two defaults of thread pool and fiber executors.
10
+ class ActivityExecutor
11
+ # @return [Hash<Symbol, ActivityExecutor>] Default set of executors (immutable).
12
+ def self.defaults
13
+ @defaults ||= {
14
+ default: ThreadPool.default,
15
+ thread_pool: ThreadPool.default,
16
+ fiber: Fiber.default
17
+ }.freeze
18
+ end
19
+
20
+ # Initialize an activity. This is called on worker initialize for every activity that will use this executor. This
21
+ # allows executor implementations to do eager validation based on the definition. This does not have to be
22
+ # implemented and the default is a no-op.
23
+ #
24
+ # @param defn [Activity::Definition::Info] Activity definition info.
25
+ def initialize_activity(defn)
26
+ # Default no-op
27
+ end
28
+
29
+ # Execute the given block in the executor. The block is built to never raise and need no arguments. Implementers
30
+ # must implement this.
31
+ #
32
+ # @param defn [Activity::Definition::Info] Activity definition info.
33
+ # @yield Block to execute.
34
+ def execute_activity(defn, &)
35
+ raise NotImplementedError
36
+ end
37
+
38
+ # @return [Activity::Context, nil] Get the current activity context. This is called by users from inside the
39
+ # activity. Implementers must implement this.
40
+ def activity_context
41
+ raise NotImplementedError
42
+ end
43
+
44
+ # Set the current activity context (or unset if nil). This is called by the system from within the block given to
45
+ # {execute_activity} with a context before user code is executed and with nil after user code is complete.
46
+ # Implementers must implement this.
47
+ #
48
+ # @param defn [Activity::Definition::Info] Activity definition info.
49
+ # @param context [Activity::Context, nil] The value to set.
50
+ def set_activity_context(defn, context)
51
+ raise NotImplementedError
52
+ end
53
+ end
54
+ end
55
+ end