temporalio 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +25 -23
- data/bridge/Cargo.lock +168 -59
- data/bridge/Cargo.toml +4 -2
- data/bridge/sdk-core/README.md +19 -6
- data/bridge/sdk-core/client/src/lib.rs +215 -39
- data/bridge/sdk-core/client/src/metrics.rs +17 -8
- data/bridge/sdk-core/client/src/raw.rs +4 -4
- data/bridge/sdk-core/client/src/retry.rs +32 -20
- data/bridge/sdk-core/core/Cargo.toml +22 -9
- data/bridge/sdk-core/core/src/abstractions.rs +203 -14
- data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +76 -41
- data/bridge/sdk-core/core/src/core_tests/determinism.rs +165 -2
- data/bridge/sdk-core/core/src/core_tests/local_activities.rs +204 -83
- data/bridge/sdk-core/core/src/core_tests/queries.rs +3 -4
- data/bridge/sdk-core/core/src/core_tests/workers.rs +1 -3
- data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +397 -54
- data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +106 -12
- data/bridge/sdk-core/core/src/internal_flags.rs +136 -0
- data/bridge/sdk-core/core/src/lib.rs +16 -9
- data/bridge/sdk-core/core/src/telemetry/log_export.rs +1 -1
- data/bridge/sdk-core/core/src/telemetry/metrics.rs +69 -35
- data/bridge/sdk-core/core/src/telemetry/mod.rs +29 -13
- data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +17 -12
- data/bridge/sdk-core/core/src/test_help/mod.rs +62 -12
- data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +112 -156
- data/bridge/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
- data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +352 -122
- data/bridge/sdk-core/core/src/worker/activities.rs +233 -157
- data/bridge/sdk-core/core/src/worker/client/mocks.rs +22 -2
- data/bridge/sdk-core/core/src/worker/client.rs +18 -2
- data/bridge/sdk-core/core/src/worker/mod.rs +165 -58
- data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
- data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -5
- data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +856 -277
- data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +100 -43
- data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +7 -7
- data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +5 -4
- data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +87 -27
- data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +5 -4
- data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +5 -4
- data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +5 -4
- data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +137 -62
- data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +25 -17
- data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +7 -6
- data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +103 -152
- data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +7 -7
- data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +9 -9
- data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
- data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +14 -7
- data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +5 -16
- data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +201 -121
- data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +11 -14
- data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +30 -15
- data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +1026 -376
- data/bridge/sdk-core/core/src/worker/workflow/mod.rs +460 -384
- data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
- data/bridge/sdk-core/core/src/worker/workflow/wft_extraction.rs +125 -0
- data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +1 -4
- data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
- data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
- data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +448 -718
- data/bridge/sdk-core/core-api/Cargo.toml +2 -1
- data/bridge/sdk-core/core-api/src/errors.rs +1 -34
- data/bridge/sdk-core/core-api/src/lib.rs +6 -2
- data/bridge/sdk-core/core-api/src/telemetry.rs +0 -6
- data/bridge/sdk-core/core-api/src/worker.rs +14 -1
- data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +18 -15
- data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +8 -3
- data/bridge/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +5 -17
- data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +11 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -6
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -6
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +5 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +22 -6
- data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +48 -19
- data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +2 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +3 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/{enums/v1/interaction_type.proto → protocol/v1/message.proto} +29 -11
- data/bridge/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +111 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +59 -28
- data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +2 -2
- data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +7 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +1 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +6 -0
- data/bridge/sdk-core/sdk/Cargo.toml +3 -2
- data/bridge/sdk-core/sdk/src/lib.rs +87 -20
- data/bridge/sdk-core/sdk/src/workflow_future.rs +9 -8
- data/bridge/sdk-core/sdk-core-protos/Cargo.toml +5 -2
- data/bridge/sdk-core/sdk-core-protos/build.rs +36 -1
- data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +100 -87
- data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +5 -1
- data/bridge/sdk-core/sdk-core-protos/src/lib.rs +175 -57
- data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
- data/bridge/sdk-core/test-utils/Cargo.toml +3 -1
- data/bridge/sdk-core/test-utils/src/canned_histories.rs +106 -296
- data/bridge/sdk-core/test-utils/src/histfetch.rs +1 -1
- data/bridge/sdk-core/test-utils/src/lib.rs +82 -23
- data/bridge/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
- data/bridge/sdk-core/test-utils/src/workflows.rs +29 -0
- data/bridge/sdk-core/tests/fuzzy_workflow.rs +130 -0
- data/bridge/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
- data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
- data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +5 -3
- data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
- data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +4 -47
- data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
- data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -69
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +1 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +72 -191
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +1 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +7 -28
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +12 -7
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -4
- data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +10 -11
- data/bridge/sdk-core/tests/main.rs +3 -13
- data/bridge/sdk-core/tests/runner.rs +75 -36
- data/bridge/sdk-core/tests/wf_input_replay.rs +32 -0
- data/bridge/src/connection.rs +41 -25
- data/bridge/src/lib.rs +269 -14
- data/bridge/src/runtime.rs +1 -1
- data/bridge/src/test_server.rs +153 -0
- data/bridge/src/worker.rs +89 -16
- data/lib/gen/temporal/api/command/v1/message_pb.rb +4 -18
- data/lib/gen/temporal/api/common/v1/message_pb.rb +4 -0
- data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +1 -3
- data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +3 -3
- data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +2 -0
- data/lib/gen/temporal/api/enums/v1/update_pb.rb +6 -4
- data/lib/gen/temporal/api/history/v1/message_pb.rb +27 -19
- data/lib/gen/temporal/api/namespace/v1/message_pb.rb +1 -0
- data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +3 -0
- data/lib/gen/temporal/api/protocol/v1/message_pb.rb +30 -0
- data/lib/gen/temporal/api/sdk/v1/task_complete_metadata_pb.rb +23 -0
- data/lib/gen/temporal/api/testservice/v1/request_response_pb.rb +49 -0
- data/lib/gen/temporal/api/testservice/v1/service_pb.rb +21 -0
- data/lib/gen/temporal/api/update/v1/message_pb.rb +72 -0
- data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +26 -16
- data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +13 -9
- data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +10 -6
- data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +13 -9
- data/lib/gen/temporal/sdk/core/common/common_pb.rb +7 -3
- data/lib/gen/temporal/sdk/core/core_interface_pb.rb +9 -3
- data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +7 -3
- data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +27 -21
- data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +28 -24
- data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +12 -5
- data/lib/temporalio/activity/context.rb +13 -8
- data/lib/temporalio/activity/info.rb +1 -1
- data/lib/temporalio/bridge/connect_options.rb +15 -0
- data/lib/temporalio/bridge/retry_config.rb +24 -0
- data/lib/temporalio/bridge/tls_options.rb +19 -0
- data/lib/temporalio/client/implementation.rb +8 -8
- data/lib/temporalio/connection/retry_config.rb +44 -0
- data/lib/temporalio/connection/service.rb +20 -0
- data/lib/temporalio/connection/test_service.rb +92 -0
- data/lib/temporalio/connection/tls_options.rb +51 -0
- data/lib/temporalio/connection/workflow_service.rb +731 -0
- data/lib/temporalio/connection.rb +55 -720
- data/lib/temporalio/interceptor/activity_inbound.rb +22 -0
- data/lib/temporalio/interceptor/activity_outbound.rb +24 -0
- data/lib/temporalio/interceptor/chain.rb +5 -5
- data/lib/temporalio/interceptor/client.rb +8 -4
- data/lib/temporalio/interceptor.rb +22 -0
- data/lib/temporalio/retry_policy.rb +13 -3
- data/lib/temporalio/testing/time_skipping_handle.rb +32 -0
- data/lib/temporalio/testing/time_skipping_interceptor.rb +23 -0
- data/lib/temporalio/testing/workflow_environment.rb +112 -0
- data/lib/temporalio/testing.rb +175 -0
- data/lib/temporalio/version.rb +1 -1
- data/lib/temporalio/worker/activity_runner.rb +26 -4
- data/lib/temporalio/worker/activity_worker.rb +44 -18
- data/lib/temporalio/worker/sync_worker.rb +47 -11
- data/lib/temporalio/worker.rb +27 -21
- data/lib/temporalio/workflow/async.rb +46 -0
- data/lib/temporalio/workflow/future.rb +138 -0
- data/lib/temporalio/workflow/info.rb +76 -0
- data/temporalio.gemspec +4 -3
- metadata +67 -17
- data/bridge/sdk-core/Cargo.lock +0 -2606
- data/bridge/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +0 -87
- data/lib/bridge.so +0 -0
- data/lib/gen/temporal/api/enums/v1/interaction_type_pb.rb +0 -25
- data/lib/gen/temporal/api/interaction/v1/message_pb.rb +0 -49
- data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +0 -222
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Temporalio
|
|
2
|
+
module Interceptor
|
|
3
|
+
# A mixin for implementing inbound Activity interceptors.
|
|
4
|
+
module ActivityInbound
|
|
5
|
+
class ExecuteActivityInput < Struct.new(
|
|
6
|
+
:activity,
|
|
7
|
+
:args,
|
|
8
|
+
:headers,
|
|
9
|
+
keyword_init: true,
|
|
10
|
+
); end
|
|
11
|
+
|
|
12
|
+
# Interceptor for {Temporalio::Activity#execute}.
|
|
13
|
+
#
|
|
14
|
+
# @param input [ExecuteActivityInput]
|
|
15
|
+
#
|
|
16
|
+
# @return [any] Activity execution result
|
|
17
|
+
def execute_activity(input)
|
|
18
|
+
yield(input)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Temporalio
|
|
2
|
+
module Interceptor
|
|
3
|
+
# A mixin for implementing outbound Activity interceptors.
|
|
4
|
+
module ActivityOutbound
|
|
5
|
+
# Interceptor for {Temporalio::Activity::Context#info}.
|
|
6
|
+
#
|
|
7
|
+
# @yieldreturn Temporalio::Activity::Info
|
|
8
|
+
#
|
|
9
|
+
# @return [Temporalio::Activity::Info]
|
|
10
|
+
def activity_info
|
|
11
|
+
yield
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Interceptor for {Temporalio::Activity::Context#heartbeat}.
|
|
15
|
+
#
|
|
16
|
+
# @yieldparam Array[untyped]
|
|
17
|
+
#
|
|
18
|
+
# @param details [any] A list of details supplied with the heartbeat.
|
|
19
|
+
def heartbeat(*details)
|
|
20
|
+
yield(*details)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -6,18 +6,18 @@ module Temporalio
|
|
|
6
6
|
@interceptors = interceptors
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
def invoke(method, input)
|
|
9
|
+
def invoke(method, *input)
|
|
10
10
|
chain = interceptors.dup
|
|
11
11
|
|
|
12
|
-
traverse_chain = lambda do
|
|
12
|
+
traverse_chain = lambda do |*i|
|
|
13
13
|
if chain.empty?
|
|
14
|
-
yield(i)
|
|
14
|
+
yield(*i)
|
|
15
15
|
else
|
|
16
|
-
chain.shift.public_send(method, i, &traverse_chain)
|
|
16
|
+
chain.shift.public_send(method, *i, &traverse_chain)
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
traverse_chain.call(input)
|
|
20
|
+
traverse_chain.call(*input)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
private
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
module Temporalio
|
|
2
2
|
module Interceptor
|
|
3
|
-
#
|
|
4
|
-
|
|
5
|
-
# @abstract
|
|
6
|
-
class Client
|
|
3
|
+
# A mixin for implementing Client side interceptors.
|
|
4
|
+
module Client
|
|
7
5
|
class StartWorkflowInput < Struct.new(
|
|
8
6
|
:workflow,
|
|
9
7
|
:args,
|
|
@@ -80,6 +78,8 @@ module Temporalio
|
|
|
80
78
|
# Interceptor for {Temporalio::Client#start_workflow}.
|
|
81
79
|
#
|
|
82
80
|
# @param input [StartWorkflowInput]
|
|
81
|
+
#
|
|
82
|
+
# @return [Temporalio::Client::WorkflowHandle] A handle to interact with the workflow.
|
|
83
83
|
def start_workflow(input)
|
|
84
84
|
yield(input)
|
|
85
85
|
end
|
|
@@ -87,6 +87,8 @@ module Temporalio
|
|
|
87
87
|
# Interceptor for {Temporalio::Client::WorkflowHandle#describe}.
|
|
88
88
|
#
|
|
89
89
|
# @param input [DescribeWorkflowInput]
|
|
90
|
+
#
|
|
91
|
+
# @return [Temporalio::Workflow::ExecutionInfo] Information about the workflow.
|
|
90
92
|
def describe_workflow(input)
|
|
91
93
|
yield(input)
|
|
92
94
|
end
|
|
@@ -94,6 +96,8 @@ module Temporalio
|
|
|
94
96
|
# Interceptor for {Temporalio::Client::WorkflowHandle#query}.
|
|
95
97
|
#
|
|
96
98
|
# @param input [QueryWorkflowInput]
|
|
99
|
+
#
|
|
100
|
+
# @return [any] Query result
|
|
97
101
|
def query_workflow(input)
|
|
98
102
|
yield(input)
|
|
99
103
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'temporalio/interceptor/activity_inbound'
|
|
2
|
+
require 'temporalio/interceptor/activity_outbound'
|
|
3
|
+
|
|
4
|
+
module Temporalio
|
|
5
|
+
module Interceptor
|
|
6
|
+
# NOTE: Using #each_with_object here and below instead of a simple #select because RBS can't
|
|
7
|
+
# reconcile that resulting array only has WorkflowInbound or WorkflowOutbound in it.
|
|
8
|
+
def self.filter(interceptors, type)
|
|
9
|
+
interceptor_class =
|
|
10
|
+
case type
|
|
11
|
+
when :activity_inbound
|
|
12
|
+
Temporalio::Interceptor::ActivityInbound
|
|
13
|
+
when :activity_outbound
|
|
14
|
+
Temporalio::Interceptor::ActivityOutbound
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
interceptors.each_with_object([]) do |i, result|
|
|
18
|
+
result << i if i.is_a?(interceptor_class)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -24,13 +24,23 @@ module Temporalio
|
|
|
24
24
|
# @return [Array<String>] List of error types that are not retryable.
|
|
25
25
|
attr_reader :non_retriable_errors
|
|
26
26
|
|
|
27
|
+
def self.from_proto(proto)
|
|
28
|
+
new(
|
|
29
|
+
initial_interval: proto.initial_interval&.to_f&.round || 0,
|
|
30
|
+
backoff: proto.backoff_coefficient&.to_f || 0.0,
|
|
31
|
+
max_interval: proto.maximum_interval&.to_f&.round || 0,
|
|
32
|
+
max_attempts: proto.maximum_attempts || 1,
|
|
33
|
+
non_retriable_errors: proto.non_retryable_error_types || [],
|
|
34
|
+
).freeze
|
|
35
|
+
end
|
|
36
|
+
|
|
27
37
|
# @param initial_interval [Integer] Backoff interval (in seconds) for the first retry.
|
|
28
38
|
# @param backoff [Float] Coefficient to multiply previous backoff interval by to get new
|
|
29
39
|
# interval.
|
|
30
40
|
# @param max_interval [Integer] Maximum backoff interval between retries. Default 100x
|
|
31
41
|
# {#initial_interval}.
|
|
32
42
|
# @param max_attempts [Integer] Maximum number of attempts. If 0, there is no maximum.
|
|
33
|
-
# @param non_retriable_errors [Array<String>] List of error types that are not retryable.
|
|
43
|
+
# @param non_retriable_errors [Array<Class, String>] List of error types that are not retryable.
|
|
34
44
|
def initialize(
|
|
35
45
|
initial_interval: 1,
|
|
36
46
|
backoff: 2.0,
|
|
@@ -42,7 +52,7 @@ module Temporalio
|
|
|
42
52
|
@backoff = backoff
|
|
43
53
|
@max_interval = max_interval
|
|
44
54
|
@max_attempts = max_attempts
|
|
45
|
-
@non_retriable_errors = non_retriable_errors
|
|
55
|
+
@non_retriable_errors = non_retriable_errors.map(&:to_s).compact
|
|
46
56
|
end
|
|
47
57
|
|
|
48
58
|
def validate!
|
|
@@ -75,7 +85,7 @@ module Temporalio
|
|
|
75
85
|
backoff_coefficient: backoff,
|
|
76
86
|
maximum_interval: max_interval ? Google::Protobuf::Duration.new(seconds: max_interval) : nil,
|
|
77
87
|
maximum_attempts: max_attempts,
|
|
78
|
-
non_retryable_error_types: non_retriable_errors
|
|
88
|
+
non_retryable_error_types: non_retriable_errors,
|
|
79
89
|
)
|
|
80
90
|
end
|
|
81
91
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'forwardable'
|
|
2
|
+
|
|
3
|
+
module Temporalio
|
|
4
|
+
module Testing
|
|
5
|
+
class TimeSkippingHandle
|
|
6
|
+
extend Forwardable
|
|
7
|
+
|
|
8
|
+
# Proxy all the WorkflowHandle calls to the original handle except for :result
|
|
9
|
+
def_delegators :handle, :id, :run_id, :result_run_id, :first_execution_run_id, :describe,
|
|
10
|
+
:cancel, :query, :signal, :terminate
|
|
11
|
+
|
|
12
|
+
def initialize(handle, env)
|
|
13
|
+
@handle = handle
|
|
14
|
+
@env = env
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def result(follow_runs: true, rpc_metadata: {}, rpc_timeout: nil)
|
|
18
|
+
env.with_time_skipping do
|
|
19
|
+
handle.result(
|
|
20
|
+
follow_runs: follow_runs,
|
|
21
|
+
rpc_metadata: rpc_metadata,
|
|
22
|
+
rpc_timeout: rpc_timeout,
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
attr_reader :handle, :env
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'temporalio/interceptor/client'
|
|
2
|
+
require 'temporalio/testing/time_skipping_handle'
|
|
3
|
+
|
|
4
|
+
module Temporalio
|
|
5
|
+
module Testing
|
|
6
|
+
class TimeSkippingInterceptor
|
|
7
|
+
include Temporalio::Interceptor::Client
|
|
8
|
+
|
|
9
|
+
def initialize(env)
|
|
10
|
+
@env = env
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def start_workflow(input)
|
|
14
|
+
handle = yield(input)
|
|
15
|
+
Temporalio::Testing::TimeSkippingHandle.new(handle, env)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
attr_reader :env
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
require 'google/protobuf/well_known_types'
|
|
2
|
+
require 'temporalio/client'
|
|
3
|
+
require 'temporalio/testing/time_skipping_interceptor'
|
|
4
|
+
|
|
5
|
+
module Temporalio
|
|
6
|
+
module Testing
|
|
7
|
+
# Workflow environment for testing workflows.
|
|
8
|
+
#
|
|
9
|
+
# Most developers will want to use the {Temporalio::Testing.start_time_skipping_environment} to
|
|
10
|
+
# start a test server process that automatically skips time as needed. Alternatively,
|
|
11
|
+
# {Temporalio::Testing.start_local_environment} may be used for a full, local Temporal server
|
|
12
|
+
# with more features.
|
|
13
|
+
#
|
|
14
|
+
# @note Unless using the above mentioned methods with an explicit block, you will need to call
|
|
15
|
+
# {#shutdown} after you're finished with this environment.
|
|
16
|
+
class WorkflowEnvironment
|
|
17
|
+
# @return [Temporalio::Connection] A connection to this environment.
|
|
18
|
+
attr_reader :connection
|
|
19
|
+
|
|
20
|
+
# @return [String] A namespace for this environment.
|
|
21
|
+
attr_reader :namespace
|
|
22
|
+
|
|
23
|
+
# @api private
|
|
24
|
+
def initialize(server, connection, namespace)
|
|
25
|
+
@server = server
|
|
26
|
+
@connection = connection
|
|
27
|
+
@namespace = namespace
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# A default client to be used with this environment.
|
|
31
|
+
#
|
|
32
|
+
# @return [Temporalio::Client] A default client.
|
|
33
|
+
def client
|
|
34
|
+
@client ||= begin
|
|
35
|
+
# TODO: Add a workflow interceptor for interpreting assertion error
|
|
36
|
+
interceptors = [TimeSkippingInterceptor.new(self)]
|
|
37
|
+
Temporalio::Client.new(connection, namespace, interceptors: interceptors)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Sleep in this environment.
|
|
42
|
+
#
|
|
43
|
+
# This awaits a regular {Kernel.sleep} in regular environments, or manually skips time in
|
|
44
|
+
# time-skipping environments.
|
|
45
|
+
#
|
|
46
|
+
# @param duration [Integer] Amount of time to sleep.
|
|
47
|
+
def sleep(duration)
|
|
48
|
+
return Kernel.sleep(duration) unless supports_time_skipping?
|
|
49
|
+
|
|
50
|
+
request = Temporalio::Api::TestService::V1::SleepRequest.new(
|
|
51
|
+
duration: Google::Protobuf::Duration.new(seconds: duration),
|
|
52
|
+
)
|
|
53
|
+
connection.test_service.unlock_time_skipping_with_sleep(request)
|
|
54
|
+
|
|
55
|
+
nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Get the current time known to this environment.
|
|
59
|
+
#
|
|
60
|
+
# For non-time-skipping environments this is simply the system time. For time-skipping
|
|
61
|
+
# environments this is whatever time has been skipped to.
|
|
62
|
+
#
|
|
63
|
+
# @return [Time]
|
|
64
|
+
def current_time
|
|
65
|
+
return Time.now unless supports_time_skipping?
|
|
66
|
+
|
|
67
|
+
connection.test_service.get_current_time&.time&.to_time
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Whether this environment supports time skipping.
|
|
71
|
+
#
|
|
72
|
+
# @return [Boolean]
|
|
73
|
+
def supports_time_skipping?
|
|
74
|
+
server.has_test_service?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Shut down this environment.
|
|
78
|
+
def shutdown
|
|
79
|
+
server.shutdown
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Unlock time skipping.
|
|
83
|
+
#
|
|
84
|
+
# This will have no effect in an environment that does not support time-skipping (e.g.
|
|
85
|
+
# Temporalite).
|
|
86
|
+
#
|
|
87
|
+
# @yield A block to be called once time skipping has been unlocked.
|
|
88
|
+
#
|
|
89
|
+
# @return [any] The return value of the block.
|
|
90
|
+
def with_time_skipping
|
|
91
|
+
return yield unless supports_time_skipping?
|
|
92
|
+
|
|
93
|
+
begin
|
|
94
|
+
# Unlock to start time skipping, lock again to stop it
|
|
95
|
+
connection.test_service.unlock_time_skipping(
|
|
96
|
+
Temporalio::Api::TestService::V1::UnlockTimeSkippingRequest.new,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
yield
|
|
100
|
+
ensure
|
|
101
|
+
connection.test_service.lock_time_skipping(
|
|
102
|
+
Temporalio::Api::TestService::V1::LockTimeSkippingRequest.new,
|
|
103
|
+
)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
attr_reader :server
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
require 'temporalio/bridge'
|
|
2
|
+
require 'temporalio/connection'
|
|
3
|
+
require 'temporalio/runtime'
|
|
4
|
+
require 'temporalio/testing/workflow_environment'
|
|
5
|
+
require 'temporalio/version'
|
|
6
|
+
|
|
7
|
+
module Temporalio
|
|
8
|
+
module Testing
|
|
9
|
+
DEFAULT_NAMESPACE = 'default'.freeze
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
# Start a full Temporal server locally, downloading if necessary.
|
|
13
|
+
#
|
|
14
|
+
# This environment is good for testing full server capabilities, but does not support time
|
|
15
|
+
# skipping like {.start_time_skipping} does.
|
|
16
|
+
# {Temporalio::Testing::WorkflowEnvironment#supports_time_skipping} will always return `false`
|
|
17
|
+
# for this environment. {Temporalio::Testing::WorkflowEnvironment#sleep} will sleep the actual
|
|
18
|
+
# amount of time and {Temporalio::Testing::WorkflowEnvironment#get_current_time}` will return
|
|
19
|
+
# the current time.
|
|
20
|
+
#
|
|
21
|
+
# Internally, this uses [Temporalite](https://github.com/temporalio/temporalite). Which is a
|
|
22
|
+
# self-contained binary for Temporal using Sqlite persistence. This will download Temporalite
|
|
23
|
+
# to a temporary directory by default if it has not already been downloaded before and
|
|
24
|
+
# `:existing_path` is not set.
|
|
25
|
+
#
|
|
26
|
+
# In the future, the Temporalite implementation may be changed to another implementation.
|
|
27
|
+
# Therefore, all `temporalite_` prefixed parameters are Temporalite specific and may not apply
|
|
28
|
+
# to newer versions.
|
|
29
|
+
#
|
|
30
|
+
# @param namespace [String] Namespace name to use for this environment.
|
|
31
|
+
# @param ip [String] IP address to bind to, or 127.0.0.1 by default.
|
|
32
|
+
# @param port [Integer] Port number to bind to, or an OS-provided port by default.
|
|
33
|
+
# @param download_dir [String] Directory to download binary to if a download is needed. If
|
|
34
|
+
# unset, this is the system's temporary directory.
|
|
35
|
+
# @param ui [Boolean] If `true`, will start a UI in Temporalite.
|
|
36
|
+
# @param temporalite_existing_path [String] Existing path to the Temporalite binary. If
|
|
37
|
+
# present, no download will be attempted to fetch the binary.
|
|
38
|
+
# @param temporalite_database_filename [String] Path to the Sqlite database to use for
|
|
39
|
+
# Temporalite. Unset default means only in-memory Sqlite will be used.
|
|
40
|
+
# @param temporalite_log_format [String] Log format for Temporalite.
|
|
41
|
+
# @param temporalite_log_level [String] Log level to use for Temporalite.
|
|
42
|
+
# @param temporalite_download_version [String] Specific Temporalite version to download.
|
|
43
|
+
# Defaults to `default` which downloads the version known to work best with this SDK.
|
|
44
|
+
# @param temporalite_extra_args [Array<String>] Extra arguments for the Temporalite binary.
|
|
45
|
+
#
|
|
46
|
+
# @yield Optionally you can provide a block which will ensure that the environment has been
|
|
47
|
+
# shut down after. The newly created {Temporalio::Testing::WorkflowEnvironment} will be
|
|
48
|
+
# passed into the block as a single argument. Alternatively you will need to call
|
|
49
|
+
# {Temporalio::Testing::WorkflowEnvironment#shutdown} explicitly after you're done with it.
|
|
50
|
+
#
|
|
51
|
+
# @return [Temporalio::Testing::WorkflowEnvironment] The newly started Temporalite workflow
|
|
52
|
+
# environment.
|
|
53
|
+
def start_local_environment(
|
|
54
|
+
namespace: DEFAULT_NAMESPACE,
|
|
55
|
+
ip: '127.0.0.1',
|
|
56
|
+
port: nil,
|
|
57
|
+
download_dir: nil,
|
|
58
|
+
ui: false,
|
|
59
|
+
temporalite_existing_path: nil,
|
|
60
|
+
temporalite_database_filename: nil,
|
|
61
|
+
temporalite_log_format: 'pretty',
|
|
62
|
+
temporalite_log_level: 'warn',
|
|
63
|
+
temporalite_download_version: 'default',
|
|
64
|
+
temporalite_extra_args: [],
|
|
65
|
+
&block
|
|
66
|
+
)
|
|
67
|
+
# TODO: Sync with the SDK's logger level when implemented
|
|
68
|
+
runtime = Temporalio::Runtime.instance
|
|
69
|
+
server = Temporalio::Bridge::TestServer.start_temporalite(
|
|
70
|
+
runtime.core_runtime,
|
|
71
|
+
temporalite_existing_path,
|
|
72
|
+
'sdk-ruby',
|
|
73
|
+
Temporalio::VERSION,
|
|
74
|
+
temporalite_download_version,
|
|
75
|
+
download_dir,
|
|
76
|
+
namespace,
|
|
77
|
+
ip,
|
|
78
|
+
port,
|
|
79
|
+
temporalite_database_filename,
|
|
80
|
+
ui,
|
|
81
|
+
temporalite_log_format,
|
|
82
|
+
temporalite_log_level,
|
|
83
|
+
temporalite_extra_args,
|
|
84
|
+
)
|
|
85
|
+
env = init_workflow_environment_for(server, namespace)
|
|
86
|
+
|
|
87
|
+
return env unless block
|
|
88
|
+
|
|
89
|
+
run_server(server, env, &block)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Start a time skipping workflow environment.
|
|
93
|
+
#
|
|
94
|
+
# Time can be manually skipped forward using {Temporalio::Testing::WorkflowEnvironment#sleep}.
|
|
95
|
+
# The currently known time can be obtained via
|
|
96
|
+
# {Temporalio::Testing::WorkflowEnvironment#get_current_time}.
|
|
97
|
+
#
|
|
98
|
+
# @note Auto time skipping is not yet implemented.
|
|
99
|
+
#
|
|
100
|
+
# Internally, this environment lazily downloads a test-server binary for the current OS/arch
|
|
101
|
+
# into the temp directory if it is not already there. Then the executable is started and will
|
|
102
|
+
# be killed when {Temporalio::Testing::WorkflowEnvironment#shutdown} is called (which is
|
|
103
|
+
# implicitly done if a block is provided to this method).
|
|
104
|
+
#
|
|
105
|
+
# Users can reuse this environment for testing multiple independent workflows, but not
|
|
106
|
+
# concurrently. Time skipping, which is automatically done when awaiting a workflow result
|
|
107
|
+
# (pending implementation) and manually done on
|
|
108
|
+
# {Temporalio::Testing::WorkflowEnvironment#sleep}, is global to the environment, not to the
|
|
109
|
+
# workflow under test.
|
|
110
|
+
#
|
|
111
|
+
# In the future, the test server implementation may be changed to another implementation.
|
|
112
|
+
# Therefore, all `test_server_` prefixed parameters are test server specific and may not apply
|
|
113
|
+
# to newer versions.
|
|
114
|
+
#
|
|
115
|
+
# @param port [Integer] Port number to bind to, or an OS-provided port by default.
|
|
116
|
+
# @param download_dir [String] Directory to download binary to if a download is needed.
|
|
117
|
+
# If unset, this is the system's temporary directory.
|
|
118
|
+
# @param test_server_existing_path [String] Existing path to the test server binary. If
|
|
119
|
+
# present, no download will be attempted to fetch the binary.
|
|
120
|
+
# @param test_server_download_version [String] Specific test server version to download.
|
|
121
|
+
# Defaults to `default` which downloads the version known to work best with this SDK.
|
|
122
|
+
# @param test_server_extra_args [Array<String>] Extra arguments for the test server binary.
|
|
123
|
+
#
|
|
124
|
+
# @yield Optionally you can provide a block which will ensure that the environment has been
|
|
125
|
+
# shut down after. The newly created {Temporalio::Testing::WorkflowEnvironment} will be
|
|
126
|
+
# passed into the block as a single argument. Alternatively you will need to call
|
|
127
|
+
# {Temporalio::Testing::WorkflowEnvironment#shutdown} explicitly after you're done with it.
|
|
128
|
+
#
|
|
129
|
+
# @return [Temporalio::Testing::WorkflowEnvironment] The newly started TestServer workflow
|
|
130
|
+
# environment.
|
|
131
|
+
def start_time_skipping_environment(
|
|
132
|
+
port: nil,
|
|
133
|
+
download_dir: nil,
|
|
134
|
+
test_server_existing_path: nil,
|
|
135
|
+
test_server_download_version: 'default',
|
|
136
|
+
test_server_extra_args: [],
|
|
137
|
+
&block
|
|
138
|
+
)
|
|
139
|
+
# TODO: Use interceptors to inject a time skipping WorkflowHandle.
|
|
140
|
+
runtime = Temporalio::Runtime.instance
|
|
141
|
+
server = Temporalio::Bridge::TestServer.start(
|
|
142
|
+
runtime.core_runtime,
|
|
143
|
+
test_server_existing_path,
|
|
144
|
+
'sdk-ruby',
|
|
145
|
+
Temporalio::VERSION,
|
|
146
|
+
test_server_download_version,
|
|
147
|
+
download_dir,
|
|
148
|
+
port,
|
|
149
|
+
test_server_extra_args,
|
|
150
|
+
)
|
|
151
|
+
env = init_workflow_environment_for(server, DEFAULT_NAMESPACE)
|
|
152
|
+
|
|
153
|
+
return env unless block
|
|
154
|
+
|
|
155
|
+
run_server(server, env, &block)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
private
|
|
159
|
+
|
|
160
|
+
def init_workflow_environment_for(server, namespace)
|
|
161
|
+
connection = Temporalio::Connection.new(server.target)
|
|
162
|
+
Temporalio::Testing::WorkflowEnvironment.new(server, connection, namespace)
|
|
163
|
+
rescue Temporalio::Bridge::Error # Shutdown if unable to connect to the server
|
|
164
|
+
server.shutdown
|
|
165
|
+
raise
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def run_server(server, env, &block)
|
|
169
|
+
block.call(env)
|
|
170
|
+
ensure
|
|
171
|
+
server.shutdown
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
data/lib/temporalio/version.rb
CHANGED
|
@@ -3,6 +3,7 @@ require 'temporalio/activity/context'
|
|
|
3
3
|
require 'temporalio/activity/info'
|
|
4
4
|
require 'temporalio/error/failure'
|
|
5
5
|
require 'temporalio/errors'
|
|
6
|
+
require 'temporalio/interceptor/activity_inbound'
|
|
6
7
|
|
|
7
8
|
module Temporalio
|
|
8
9
|
class Worker
|
|
@@ -12,20 +13,39 @@ module Temporalio
|
|
|
12
13
|
#
|
|
13
14
|
# @api private
|
|
14
15
|
class ActivityRunner
|
|
15
|
-
def initialize(
|
|
16
|
+
def initialize(
|
|
17
|
+
activity_class,
|
|
18
|
+
start,
|
|
19
|
+
task_queue,
|
|
20
|
+
task_token,
|
|
21
|
+
worker,
|
|
22
|
+
converter,
|
|
23
|
+
inbound_interceptors,
|
|
24
|
+
outbound_interceptors
|
|
25
|
+
)
|
|
16
26
|
@activity_class = activity_class
|
|
17
27
|
@start = start
|
|
18
28
|
@task_queue = task_queue
|
|
19
29
|
@task_token = task_token
|
|
20
30
|
@worker = worker
|
|
21
31
|
@converter = converter
|
|
32
|
+
@inbound_interceptors = inbound_interceptors
|
|
33
|
+
@outbound_interceptors = outbound_interceptors
|
|
22
34
|
end
|
|
23
35
|
|
|
24
36
|
def run
|
|
25
37
|
activity = activity_class.new(context)
|
|
26
|
-
|
|
38
|
+
args = converter.from_payload_array(start.input.to_a)
|
|
39
|
+
headers = converter.from_payload_map(start.header_fields)
|
|
40
|
+
input = Temporalio::Interceptor::ActivityInbound::ExecuteActivityInput.new(
|
|
41
|
+
activity: activity_class,
|
|
42
|
+
args: args,
|
|
43
|
+
headers: headers || {},
|
|
44
|
+
)
|
|
27
45
|
|
|
28
|
-
result =
|
|
46
|
+
result = inbound_interceptors.invoke(:execute_activity, input) do |i|
|
|
47
|
+
activity.execute(*i.args)
|
|
48
|
+
end
|
|
29
49
|
|
|
30
50
|
converter.to_payload(result)
|
|
31
51
|
rescue StandardError => e
|
|
@@ -48,7 +68,8 @@ module Temporalio
|
|
|
48
68
|
|
|
49
69
|
private
|
|
50
70
|
|
|
51
|
-
attr_reader :activity_class, :start, :task_queue, :task_token, :worker, :converter
|
|
71
|
+
attr_reader :activity_class, :start, :task_queue, :task_token, :worker, :converter,
|
|
72
|
+
:inbound_interceptors, :outbound_interceptors
|
|
52
73
|
|
|
53
74
|
def context
|
|
54
75
|
return @context if @context
|
|
@@ -57,6 +78,7 @@ module Temporalio
|
|
|
57
78
|
@context = Temporalio::Activity::Context.new(
|
|
58
79
|
generate_activity_info,
|
|
59
80
|
heartbeat_proc,
|
|
81
|
+
outbound_interceptors,
|
|
60
82
|
shielded: activity_class._shielded,
|
|
61
83
|
)
|
|
62
84
|
end
|