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