temporalio 0.0.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/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +130 -0
- data/bridge/Cargo.lock +2865 -0
- data/bridge/Cargo.toml +26 -0
- data/bridge/sdk-core/ARCHITECTURE.md +76 -0
- data/bridge/sdk-core/Cargo.lock +2606 -0
- data/bridge/sdk-core/Cargo.toml +2 -0
- data/bridge/sdk-core/LICENSE.txt +23 -0
- data/bridge/sdk-core/README.md +107 -0
- data/bridge/sdk-core/arch_docs/diagrams/README.md +10 -0
- data/bridge/sdk-core/arch_docs/diagrams/sticky_queues.puml +40 -0
- data/bridge/sdk-core/arch_docs/diagrams/workflow_internals.svg +1 -0
- data/bridge/sdk-core/arch_docs/sticky_queues.md +51 -0
- data/bridge/sdk-core/bridge-ffi/Cargo.toml +24 -0
- data/bridge/sdk-core/bridge-ffi/LICENSE.txt +23 -0
- data/bridge/sdk-core/bridge-ffi/build.rs +25 -0
- data/bridge/sdk-core/bridge-ffi/include/sdk-core-bridge.h +249 -0
- data/bridge/sdk-core/bridge-ffi/src/lib.rs +825 -0
- data/bridge/sdk-core/bridge-ffi/src/wrappers.rs +211 -0
- data/bridge/sdk-core/client/Cargo.toml +40 -0
- data/bridge/sdk-core/client/LICENSE.txt +23 -0
- data/bridge/sdk-core/client/src/lib.rs +1294 -0
- data/bridge/sdk-core/client/src/metrics.rs +165 -0
- data/bridge/sdk-core/client/src/raw.rs +931 -0
- data/bridge/sdk-core/client/src/retry.rs +674 -0
- data/bridge/sdk-core/client/src/workflow_handle/mod.rs +185 -0
- data/bridge/sdk-core/core/Cargo.toml +116 -0
- data/bridge/sdk-core/core/LICENSE.txt +23 -0
- data/bridge/sdk-core/core/benches/workflow_replay.rs +73 -0
- data/bridge/sdk-core/core/src/abstractions.rs +166 -0
- data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +911 -0
- data/bridge/sdk-core/core/src/core_tests/child_workflows.rs +221 -0
- data/bridge/sdk-core/core/src/core_tests/determinism.rs +107 -0
- data/bridge/sdk-core/core/src/core_tests/local_activities.rs +515 -0
- data/bridge/sdk-core/core/src/core_tests/mod.rs +100 -0
- data/bridge/sdk-core/core/src/core_tests/queries.rs +736 -0
- data/bridge/sdk-core/core/src/core_tests/replay_flag.rs +65 -0
- data/bridge/sdk-core/core/src/core_tests/workers.rs +259 -0
- data/bridge/sdk-core/core/src/core_tests/workflow_cancels.rs +124 -0
- data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +2070 -0
- data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +515 -0
- data/bridge/sdk-core/core/src/lib.rs +175 -0
- data/bridge/sdk-core/core/src/log_export.rs +62 -0
- data/bridge/sdk-core/core/src/pollers/mod.rs +54 -0
- data/bridge/sdk-core/core/src/pollers/poll_buffer.rs +297 -0
- data/bridge/sdk-core/core/src/protosext/mod.rs +428 -0
- data/bridge/sdk-core/core/src/replay/mod.rs +71 -0
- data/bridge/sdk-core/core/src/retry_logic.rs +202 -0
- data/bridge/sdk-core/core/src/telemetry/metrics.rs +383 -0
- data/bridge/sdk-core/core/src/telemetry/mod.rs +412 -0
- data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +77 -0
- data/bridge/sdk-core/core/src/test_help/mod.rs +875 -0
- data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +580 -0
- data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +1042 -0
- data/bridge/sdk-core/core/src/worker/activities.rs +464 -0
- data/bridge/sdk-core/core/src/worker/client/mocks.rs +87 -0
- data/bridge/sdk-core/core/src/worker/client.rs +347 -0
- data/bridge/sdk-core/core/src/worker/mod.rs +566 -0
- data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +37 -0
- data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +110 -0
- data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +458 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +911 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +298 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +171 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +860 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +140 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +161 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +133 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +1448 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +342 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/mutable_side_effect_state_machine.rs +127 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +712 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/side_effect_state_machine.rs +71 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +443 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +439 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +169 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +246 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +96 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +1184 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +277 -0
- data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +198 -0
- data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +647 -0
- data/bridge/sdk-core/core/src/worker/workflow/mod.rs +1143 -0
- data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +145 -0
- data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +88 -0
- data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +940 -0
- data/bridge/sdk-core/core-api/Cargo.toml +31 -0
- data/bridge/sdk-core/core-api/LICENSE.txt +23 -0
- data/bridge/sdk-core/core-api/src/errors.rs +95 -0
- data/bridge/sdk-core/core-api/src/lib.rs +151 -0
- data/bridge/sdk-core/core-api/src/worker.rs +135 -0
- data/bridge/sdk-core/etc/deps.svg +187 -0
- data/bridge/sdk-core/etc/dynamic-config.yaml +2 -0
- data/bridge/sdk-core/etc/otel-collector-config.yaml +36 -0
- data/bridge/sdk-core/etc/prometheus.yaml +6 -0
- data/bridge/sdk-core/fsm/Cargo.toml +18 -0
- data/bridge/sdk-core/fsm/LICENSE.txt +23 -0
- data/bridge/sdk-core/fsm/README.md +3 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +27 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/LICENSE.txt +23 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +647 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/progress.rs +8 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.rs +18 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.stderr +12 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dynamic_dest_pass.rs +41 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/forgot_name_fail.rs +14 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/forgot_name_fail.stderr +11 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/handler_arg_pass.rs +32 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/handler_pass.rs +31 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/medium_complex_pass.rs +46 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.rs +29 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +12 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/simple_pass.rs +32 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/struct_event_variant_fail.rs +18 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/struct_event_variant_fail.stderr +5 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_more_item_event_variant_fail.rs +11 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_more_item_event_variant_fail.stderr +5 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_zero_item_event_variant_fail.rs +11 -0
- data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_zero_item_event_variant_fail.stderr +5 -0
- data/bridge/sdk-core/fsm/rustfsm_trait/Cargo.toml +14 -0
- data/bridge/sdk-core/fsm/rustfsm_trait/LICENSE.txt +23 -0
- data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +249 -0
- data/bridge/sdk-core/fsm/src/lib.rs +2 -0
- data/bridge/sdk-core/histories/fail_wf_task.bin +0 -0
- data/bridge/sdk-core/histories/timer_workflow_history.bin +0 -0
- data/bridge/sdk-core/integ-with-otel.sh +7 -0
- data/bridge/sdk-core/protos/api_upstream/README.md +9 -0
- data/bridge/sdk-core/protos/api_upstream/api-linter.yaml +40 -0
- data/bridge/sdk-core/protos/api_upstream/buf.yaml +12 -0
- data/bridge/sdk-core/protos/api_upstream/dependencies/gogoproto/gogo.proto +141 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +86 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +83 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +259 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +112 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +46 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/cluster.proto +40 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +57 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +55 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +168 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +97 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +51 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +50 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +41 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +60 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +59 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +51 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +122 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +108 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +114 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +56 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +751 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +97 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +161 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +99 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +61 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +55 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +300 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +108 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +46 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +59 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +145 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +1124 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +401 -0
- data/bridge/sdk-core/protos/grpc/health/v1/health.proto +63 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +79 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +210 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +77 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +15 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +30 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +261 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +297 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +29 -0
- data/bridge/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
- data/bridge/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
- data/bridge/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
- data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
- data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
- data/bridge/sdk-core/rustfmt.toml +1 -0
- data/bridge/sdk-core/sdk/Cargo.toml +47 -0
- data/bridge/sdk-core/sdk/LICENSE.txt +23 -0
- data/bridge/sdk-core/sdk/src/activity_context.rs +230 -0
- data/bridge/sdk-core/sdk/src/app_data.rs +37 -0
- data/bridge/sdk-core/sdk/src/conversions.rs +8 -0
- data/bridge/sdk-core/sdk/src/interceptors.rs +17 -0
- data/bridge/sdk-core/sdk/src/lib.rs +792 -0
- data/bridge/sdk-core/sdk/src/payload_converter.rs +11 -0
- data/bridge/sdk-core/sdk/src/workflow_context/options.rs +295 -0
- data/bridge/sdk-core/sdk/src/workflow_context.rs +683 -0
- data/bridge/sdk-core/sdk/src/workflow_future.rs +503 -0
- data/bridge/sdk-core/sdk-core-protos/Cargo.toml +30 -0
- data/bridge/sdk-core/sdk-core-protos/LICENSE.txt +23 -0
- data/bridge/sdk-core/sdk-core-protos/build.rs +108 -0
- data/bridge/sdk-core/sdk-core-protos/src/constants.rs +7 -0
- data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +497 -0
- data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +230 -0
- data/bridge/sdk-core/sdk-core-protos/src/lib.rs +1910 -0
- data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
- data/bridge/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
- data/bridge/sdk-core/test-utils/Cargo.toml +35 -0
- data/bridge/sdk-core/test-utils/src/canned_histories.rs +1579 -0
- data/bridge/sdk-core/test-utils/src/histfetch.rs +28 -0
- data/bridge/sdk-core/test-utils/src/lib.rs +598 -0
- data/bridge/sdk-core/tests/integ_tests/client_tests.rs +36 -0
- data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +128 -0
- data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +218 -0
- data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +146 -0
- data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +437 -0
- data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +878 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +61 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +59 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +58 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +50 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +60 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +54 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +634 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +113 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +137 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +93 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +167 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +99 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +131 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +75 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +587 -0
- data/bridge/sdk-core/tests/load_tests.rs +191 -0
- data/bridge/sdk-core/tests/main.rs +111 -0
- data/bridge/sdk-core/tests/runner.rs +93 -0
- data/bridge/src/connection.rs +167 -0
- data/bridge/src/lib.rs +180 -0
- data/bridge/src/runtime.rs +47 -0
- data/bridge/src/worker.rs +73 -0
- data/ext/Rakefile +9 -0
- data/lib/bridge.so +0 -0
- data/lib/gen/dependencies/gogoproto/gogo_pb.rb +14 -0
- data/lib/gen/temporal/api/batch/v1/message_pb.rb +48 -0
- data/lib/gen/temporal/api/cluster/v1/message_pb.rb +67 -0
- data/lib/gen/temporal/api/command/v1/message_pb.rb +166 -0
- data/lib/gen/temporal/api/common/v1/message_pb.rb +69 -0
- data/lib/gen/temporal/api/enums/v1/batch_operation_pb.rb +32 -0
- data/lib/gen/temporal/api/enums/v1/cluster_pb.rb +26 -0
- data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +37 -0
- data/lib/gen/temporal/api/enums/v1/common_pb.rb +41 -0
- data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +67 -0
- data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +71 -0
- data/lib/gen/temporal/api/enums/v1/namespace_pb.rb +37 -0
- data/lib/gen/temporal/api/enums/v1/query_pb.rb +31 -0
- data/lib/gen/temporal/api/enums/v1/reset_pb.rb +24 -0
- data/lib/gen/temporal/api/enums/v1/schedule_pb.rb +28 -0
- data/lib/gen/temporal/api/enums/v1/task_queue_pb.rb +30 -0
- data/lib/gen/temporal/api/enums/v1/update_pb.rb +28 -0
- data/lib/gen/temporal/api/enums/v1/workflow_pb.rb +89 -0
- data/lib/gen/temporal/api/errordetails/v1/message_pb.rb +84 -0
- data/lib/gen/temporal/api/failure/v1/message_pb.rb +83 -0
- data/lib/gen/temporal/api/filter/v1/message_pb.rb +40 -0
- data/lib/gen/temporal/api/history/v1/message_pb.rb +489 -0
- data/lib/gen/temporal/api/namespace/v1/message_pb.rb +63 -0
- data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +125 -0
- data/lib/gen/temporal/api/operatorservice/v1/service_pb.rb +20 -0
- data/lib/gen/temporal/api/query/v1/message_pb.rb +38 -0
- data/lib/gen/temporal/api/replication/v1/message_pb.rb +37 -0
- data/lib/gen/temporal/api/schedule/v1/message_pb.rb +128 -0
- data/lib/gen/temporal/api/taskqueue/v1/message_pb.rb +73 -0
- data/lib/gen/temporal/api/update/v1/message_pb.rb +26 -0
- data/lib/gen/temporal/api/version/v1/message_pb.rb +41 -0
- data/lib/gen/temporal/api/workflow/v1/message_pb.rb +110 -0
- data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +771 -0
- data/lib/gen/temporal/api/workflowservice/v1/service_pb.rb +20 -0
- data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +58 -0
- data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +57 -0
- data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +222 -0
- data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +57 -0
- data/lib/gen/temporal/sdk/core/common/common_pb.rb +22 -0
- data/lib/gen/temporal/sdk/core/core_interface_pb.rb +34 -0
- data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +27 -0
- data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +164 -0
- data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +192 -0
- data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +34 -0
- data/lib/temporal/bridge.rb +14 -0
- data/lib/temporal/client/implementation.rb +339 -0
- data/lib/temporal/client/workflow_handle.rb +243 -0
- data/lib/temporal/client.rb +144 -0
- data/lib/temporal/connection.rb +736 -0
- data/lib/temporal/data_converter.rb +150 -0
- data/lib/temporal/error/failure.rb +194 -0
- data/lib/temporal/error/workflow_failure.rb +17 -0
- data/lib/temporal/errors.rb +22 -0
- data/lib/temporal/failure_converter/base.rb +26 -0
- data/lib/temporal/failure_converter/basic.rb +313 -0
- data/lib/temporal/failure_converter.rb +8 -0
- data/lib/temporal/interceptor/chain.rb +27 -0
- data/lib/temporal/interceptor/client.rb +102 -0
- data/lib/temporal/payload_codec/base.rb +32 -0
- data/lib/temporal/payload_converter/base.rb +24 -0
- data/lib/temporal/payload_converter/bytes.rb +26 -0
- data/lib/temporal/payload_converter/composite.rb +47 -0
- data/lib/temporal/payload_converter/encoding_base.rb +35 -0
- data/lib/temporal/payload_converter/json.rb +25 -0
- data/lib/temporal/payload_converter/nil.rb +25 -0
- data/lib/temporal/payload_converter.rb +14 -0
- data/lib/temporal/retry_policy.rb +82 -0
- data/lib/temporal/retry_state.rb +35 -0
- data/lib/temporal/runtime.rb +22 -0
- data/lib/temporal/timeout_type.rb +29 -0
- data/lib/temporal/version.rb +3 -0
- data/lib/temporal/workflow/execution_info.rb +54 -0
- data/lib/temporal/workflow/execution_status.rb +36 -0
- data/lib/temporal/workflow/id_reuse_policy.rb +36 -0
- data/lib/temporal/workflow/query_reject_condition.rb +33 -0
- data/lib/temporal.rb +8 -0
- data/lib/temporalio.rb +3 -0
- data/lib/thermite_patch.rb +23 -0
- data/temporalio.gemspec +41 -0
- metadata +583 -0
@@ -0,0 +1,515 @@
|
|
1
|
+
//! This module implements support for downloading and running ephemeral test
|
2
|
+
//! servers useful for testing.
|
3
|
+
|
4
|
+
use anyhow::anyhow;
|
5
|
+
use flate2::read::GzDecoder;
|
6
|
+
use futures::StreamExt;
|
7
|
+
use serde::Deserialize;
|
8
|
+
use std::{
|
9
|
+
fs::OpenOptions,
|
10
|
+
path::{Path, PathBuf},
|
11
|
+
};
|
12
|
+
use temporal_client::ClientOptionsBuilder;
|
13
|
+
use tokio::{
|
14
|
+
task::spawn_blocking,
|
15
|
+
time::{sleep, Duration},
|
16
|
+
};
|
17
|
+
use tokio_util::io::{StreamReader, SyncIoBridge};
|
18
|
+
use url::Url;
|
19
|
+
use zip::read::read_zipfile_from_stream;
|
20
|
+
|
21
|
+
#[cfg(target_family = "unix")]
|
22
|
+
use std::os::unix::fs::OpenOptionsExt;
|
23
|
+
use std::process::Stdio;
|
24
|
+
|
25
|
+
/// Configuration for Temporalite.
|
26
|
+
#[derive(Debug, Clone, derive_builder::Builder)]
|
27
|
+
pub struct TemporaliteConfig {
|
28
|
+
/// Required path to executable or download info.
|
29
|
+
pub exe: EphemeralExe,
|
30
|
+
/// Namespace to use.
|
31
|
+
#[builder(default = "\"default\".to_owned()")]
|
32
|
+
pub namespace: String,
|
33
|
+
/// IP to bind to.
|
34
|
+
#[builder(default = "\"127.0.0.1\".to_owned()")]
|
35
|
+
pub ip: String,
|
36
|
+
/// Port to use or obtains a free one if none given.
|
37
|
+
#[builder(default)]
|
38
|
+
pub port: Option<u16>,
|
39
|
+
/// Sqlite DB filename if persisting or non-persistent if none.
|
40
|
+
#[builder(default)]
|
41
|
+
pub db_filename: Option<String>,
|
42
|
+
/// Whether to enable the UI.
|
43
|
+
#[builder(default)]
|
44
|
+
pub ui: bool,
|
45
|
+
/// Log format and level
|
46
|
+
#[builder(default = "(\"pretty\".to_owned(), \"warn\".to_owned())")]
|
47
|
+
pub log: (String, String),
|
48
|
+
/// Additional arguments to Temporalite.
|
49
|
+
#[builder(default)]
|
50
|
+
pub extra_args: Vec<String>,
|
51
|
+
}
|
52
|
+
|
53
|
+
impl TemporaliteConfig {
|
54
|
+
/// Start a Temporalite server.
|
55
|
+
pub async fn start_server(&self) -> anyhow::Result<EphemeralServer> {
|
56
|
+
self.start_server_with_output(Stdio::inherit()).await
|
57
|
+
}
|
58
|
+
|
59
|
+
/// Start a Temporalite server with configurable stdout destination.
|
60
|
+
pub async fn start_server_with_output(&self, output: Stdio) -> anyhow::Result<EphemeralServer> {
|
61
|
+
// Get exe path
|
62
|
+
let exe_path = self.exe.get_or_download("temporalite").await?;
|
63
|
+
|
64
|
+
// Get free port if not already given
|
65
|
+
let port = self.port.unwrap_or_else(|| get_free_port(&self.ip));
|
66
|
+
|
67
|
+
// Build arg set
|
68
|
+
let mut args = vec![
|
69
|
+
"start".to_owned(),
|
70
|
+
"--port".to_owned(),
|
71
|
+
port.to_string(),
|
72
|
+
"--namespace".to_owned(),
|
73
|
+
self.namespace.clone(),
|
74
|
+
"--ip".to_owned(),
|
75
|
+
self.ip.clone(),
|
76
|
+
"--log-format".to_owned(),
|
77
|
+
self.log.0.clone(),
|
78
|
+
"--log-level".to_owned(),
|
79
|
+
self.log.1.clone(),
|
80
|
+
];
|
81
|
+
if let Some(db_filename) = &self.db_filename {
|
82
|
+
args.push("--filename".to_owned());
|
83
|
+
args.push(db_filename.clone());
|
84
|
+
} else {
|
85
|
+
args.push("--ephemeral".to_owned());
|
86
|
+
}
|
87
|
+
if !self.ui {
|
88
|
+
args.push("--headless".to_owned());
|
89
|
+
}
|
90
|
+
args.extend(self.extra_args.clone());
|
91
|
+
|
92
|
+
// Start
|
93
|
+
EphemeralServer::start(EphemeralServerConfig {
|
94
|
+
exe_path,
|
95
|
+
port,
|
96
|
+
args,
|
97
|
+
has_test_service: false,
|
98
|
+
output,
|
99
|
+
})
|
100
|
+
.await
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
/// Configuration for the test server.
|
105
|
+
#[derive(Debug, Clone, derive_builder::Builder)]
|
106
|
+
pub struct TestServerConfig {
|
107
|
+
/// Required path to executable or download info.
|
108
|
+
pub exe: EphemeralExe,
|
109
|
+
/// Port to use or obtains a free one if none given.
|
110
|
+
#[builder(default)]
|
111
|
+
pub port: Option<u16>,
|
112
|
+
/// Additional arguments to the test server.
|
113
|
+
#[builder(default)]
|
114
|
+
pub extra_args: Vec<String>,
|
115
|
+
}
|
116
|
+
|
117
|
+
impl TestServerConfig {
|
118
|
+
/// Start a test server.
|
119
|
+
pub async fn start_server(&self) -> anyhow::Result<EphemeralServer> {
|
120
|
+
self.start_server_with_output(Stdio::inherit()).await
|
121
|
+
}
|
122
|
+
|
123
|
+
/// Start a test server with configurable stdout.
|
124
|
+
pub async fn start_server_with_output(&self, output: Stdio) -> anyhow::Result<EphemeralServer> {
|
125
|
+
// Get exe path
|
126
|
+
let exe_path = self.exe.get_or_download("temporal-test-server").await?;
|
127
|
+
|
128
|
+
// Get free port if not already given
|
129
|
+
let port = self.port.unwrap_or_else(|| get_free_port("0.0.0.0"));
|
130
|
+
|
131
|
+
// Build arg set
|
132
|
+
let mut args = vec![port.to_string()];
|
133
|
+
args.extend(self.extra_args.clone());
|
134
|
+
|
135
|
+
// Start
|
136
|
+
EphemeralServer::start(EphemeralServerConfig {
|
137
|
+
exe_path,
|
138
|
+
port,
|
139
|
+
args,
|
140
|
+
has_test_service: true,
|
141
|
+
output,
|
142
|
+
})
|
143
|
+
.await
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
struct EphemeralServerConfig {
|
148
|
+
exe_path: PathBuf,
|
149
|
+
port: u16,
|
150
|
+
args: Vec<String>,
|
151
|
+
has_test_service: bool,
|
152
|
+
output: Stdio,
|
153
|
+
}
|
154
|
+
|
155
|
+
/// Server that will be stopped when dropped.
|
156
|
+
#[derive(Debug)]
|
157
|
+
pub struct EphemeralServer {
|
158
|
+
/// gRPC target host:port for the server frontend.
|
159
|
+
pub target: String,
|
160
|
+
/// Whether the target implements the gRPC TestService
|
161
|
+
pub has_test_service: bool,
|
162
|
+
child: tokio::process::Child,
|
163
|
+
}
|
164
|
+
|
165
|
+
impl EphemeralServer {
|
166
|
+
async fn start(config: EphemeralServerConfig) -> anyhow::Result<EphemeralServer> {
|
167
|
+
// Start process
|
168
|
+
// TODO(cretz): Offer stdio suppression?
|
169
|
+
let child = tokio::process::Command::new(config.exe_path)
|
170
|
+
.args(config.args)
|
171
|
+
.stdin(Stdio::null())
|
172
|
+
.stdout(config.output)
|
173
|
+
.spawn()?;
|
174
|
+
let target = format!("127.0.0.1:{}", config.port);
|
175
|
+
let target_url = format!("http://{}", target);
|
176
|
+
let success = Ok(EphemeralServer {
|
177
|
+
target,
|
178
|
+
has_test_service: config.has_test_service,
|
179
|
+
child,
|
180
|
+
});
|
181
|
+
|
182
|
+
// Try to connect every 100ms for 5s
|
183
|
+
// TODO(cretz): Some other way, e.g. via stdout, to know whether the
|
184
|
+
// server is up?
|
185
|
+
let client_options = ClientOptionsBuilder::default()
|
186
|
+
.identity("online_checker".to_owned())
|
187
|
+
.target_url(Url::parse(&target_url)?)
|
188
|
+
.client_name("online-checker".to_owned())
|
189
|
+
.client_version("0.1.0".to_owned())
|
190
|
+
.build()?;
|
191
|
+
for _ in 0..50 {
|
192
|
+
sleep(Duration::from_millis(100)).await;
|
193
|
+
if client_options
|
194
|
+
.connect_no_namespace(None, None)
|
195
|
+
.await
|
196
|
+
.is_ok()
|
197
|
+
{
|
198
|
+
return success;
|
199
|
+
}
|
200
|
+
}
|
201
|
+
Err(anyhow!("Failed connecting to test server after 5 seconds"))
|
202
|
+
}
|
203
|
+
|
204
|
+
/// Shutdown the server (i.e. kill the child process). This does not attempt
|
205
|
+
/// a kill if the child process appears completed, but such a check is not
|
206
|
+
/// atomic so a kill could still fail as completed if completed just before
|
207
|
+
/// kill.
|
208
|
+
#[cfg(not(target_family = "unix"))]
|
209
|
+
pub async fn shutdown(&mut self) -> anyhow::Result<()> {
|
210
|
+
// Only kill if there is a PID
|
211
|
+
if self.child.id().is_some() {
|
212
|
+
Ok(self.child.kill().await?)
|
213
|
+
} else {
|
214
|
+
Ok(())
|
215
|
+
}
|
216
|
+
}
|
217
|
+
|
218
|
+
/// Shutdown the server (i.e. kill the child process). This does not attempt
|
219
|
+
/// a kill if the child process appears completed, but such a check is not
|
220
|
+
/// atomic so a kill could still fail as completed if completed just before
|
221
|
+
/// kill.
|
222
|
+
#[cfg(target_family = "unix")]
|
223
|
+
pub async fn shutdown(&mut self) -> anyhow::Result<()> {
|
224
|
+
// For whatever reason, Tokio is not properly waiting on result
|
225
|
+
// after sending kill in some cases which is causing defunct zombie
|
226
|
+
// processes to remain and kill() to hang. Therefore, we are sending
|
227
|
+
// SIGKILL and waiting on the process ourselves using a low-level call.
|
228
|
+
//
|
229
|
+
// WARNING: This is based on empirical evidence starting a Python test
|
230
|
+
// run on Linux with Python 3.7 (does not happen on Python 3.10 nor does
|
231
|
+
// it happen on Temporalite nor does it happen in Rust integration
|
232
|
+
// tests). Don't alter without running that scenario. EX: SIGINT works but not SIGKILL
|
233
|
+
if let Some(pid) = self.child.id() {
|
234
|
+
let nix_pid = nix::unistd::Pid::from_raw(pid as i32);
|
235
|
+
Ok(spawn_blocking(move || {
|
236
|
+
nix::sys::signal::kill(nix_pid, nix::sys::signal::Signal::SIGINT)?;
|
237
|
+
nix::sys::wait::waitpid(Some(nix_pid), None)
|
238
|
+
})
|
239
|
+
.await?
|
240
|
+
.map(|_| ())?)
|
241
|
+
} else {
|
242
|
+
Ok(())
|
243
|
+
}
|
244
|
+
}
|
245
|
+
}
|
246
|
+
|
247
|
+
/// Where to find an executable. Can be a path or download.
|
248
|
+
#[derive(Debug, Clone)]
|
249
|
+
pub enum EphemeralExe {
|
250
|
+
/// Existing path on the filesystem for the executable.
|
251
|
+
ExistingPath(String),
|
252
|
+
/// Download the executable if not already there.
|
253
|
+
CachedDownload {
|
254
|
+
/// Which version to download.
|
255
|
+
version: EphemeralExeVersion,
|
256
|
+
/// Destination directory or the user temp directory if none set.
|
257
|
+
dest_dir: Option<String>,
|
258
|
+
},
|
259
|
+
}
|
260
|
+
|
261
|
+
/// Which version of the exe to download.
|
262
|
+
#[derive(Debug, Clone)]
|
263
|
+
pub enum EphemeralExeVersion {
|
264
|
+
/// Use a default version for the given SDK name and version.
|
265
|
+
Default {
|
266
|
+
/// Name of the SDK to get the default for.
|
267
|
+
sdk_name: String,
|
268
|
+
/// Version of the SDK to get the default for.
|
269
|
+
sdk_version: String,
|
270
|
+
},
|
271
|
+
/// Specific version.
|
272
|
+
Fixed(String),
|
273
|
+
}
|
274
|
+
|
275
|
+
#[derive(Deserialize, Debug)]
|
276
|
+
#[serde(rename_all = "camelCase")]
|
277
|
+
struct DownloadInfo {
|
278
|
+
archive_url: String,
|
279
|
+
file_to_extract: String,
|
280
|
+
}
|
281
|
+
|
282
|
+
impl EphemeralExe {
|
283
|
+
async fn get_or_download(&self, artifact_name: &str) -> anyhow::Result<PathBuf> {
|
284
|
+
match self {
|
285
|
+
EphemeralExe::ExistingPath(exe_path) => {
|
286
|
+
let path = PathBuf::from(exe_path);
|
287
|
+
if !path.exists() {
|
288
|
+
return Err(anyhow!("Exe path does not exist"));
|
289
|
+
}
|
290
|
+
Ok(path)
|
291
|
+
}
|
292
|
+
EphemeralExe::CachedDownload { version, dest_dir } => {
|
293
|
+
let dest_dir = dest_dir
|
294
|
+
.as_ref()
|
295
|
+
.map(PathBuf::from)
|
296
|
+
.unwrap_or_else(std::env::temp_dir);
|
297
|
+
let (platform, out_ext) = match std::env::consts::OS {
|
298
|
+
"windows" => ("windows", ".exe"),
|
299
|
+
"macos" => ("darwin", ""),
|
300
|
+
_ => ("linux", ""),
|
301
|
+
};
|
302
|
+
// Create dest file based on SDK name/version or fixed version
|
303
|
+
let dest = dest_dir.join(match version {
|
304
|
+
EphemeralExeVersion::Default {
|
305
|
+
sdk_name,
|
306
|
+
sdk_version,
|
307
|
+
} => format!("{}-{}-{}{}", artifact_name, sdk_name, sdk_version, out_ext),
|
308
|
+
EphemeralExeVersion::Fixed(version) => {
|
309
|
+
format!("{}-{}{}", artifact_name, version, out_ext)
|
310
|
+
}
|
311
|
+
});
|
312
|
+
debug!(
|
313
|
+
"Lazily downloading or using existing exe at {}",
|
314
|
+
dest.display()
|
315
|
+
);
|
316
|
+
|
317
|
+
// If it already exists, skip
|
318
|
+
if dest.exists() {
|
319
|
+
return Ok(dest);
|
320
|
+
}
|
321
|
+
|
322
|
+
// Get info about the proper archive and in-archive file
|
323
|
+
let arch = match std::env::consts::ARCH {
|
324
|
+
"x86_64" => "amd64",
|
325
|
+
"arm" | "aarch64" => "arm64",
|
326
|
+
other => return Err(anyhow!("Unsupported arch: {}", other)),
|
327
|
+
};
|
328
|
+
let mut get_info_params = vec![("arch", arch), ("platform", platform)];
|
329
|
+
let version_name = match version {
|
330
|
+
EphemeralExeVersion::Default {
|
331
|
+
sdk_name,
|
332
|
+
sdk_version,
|
333
|
+
} => {
|
334
|
+
get_info_params.push(("sdk-name", sdk_name.as_str()));
|
335
|
+
get_info_params.push(("sdk-version", sdk_version.as_str()));
|
336
|
+
"default"
|
337
|
+
}
|
338
|
+
EphemeralExeVersion::Fixed(version) => version,
|
339
|
+
};
|
340
|
+
let client = reqwest::Client::new();
|
341
|
+
let info: DownloadInfo = client
|
342
|
+
.get(format!(
|
343
|
+
"https://temporal.download/{}/{}",
|
344
|
+
artifact_name, version_name
|
345
|
+
))
|
346
|
+
.query(&get_info_params)
|
347
|
+
.send()
|
348
|
+
.await?
|
349
|
+
.json()
|
350
|
+
.await?;
|
351
|
+
|
352
|
+
// Attempt download, looping because it could have waited for
|
353
|
+
// concurrent one to finish
|
354
|
+
loop {
|
355
|
+
if lazy_download_exe(
|
356
|
+
&client,
|
357
|
+
&info.archive_url,
|
358
|
+
Path::new(&info.file_to_extract),
|
359
|
+
&dest,
|
360
|
+
)
|
361
|
+
.await?
|
362
|
+
{
|
363
|
+
return Ok(dest);
|
364
|
+
}
|
365
|
+
}
|
366
|
+
}
|
367
|
+
}
|
368
|
+
}
|
369
|
+
}
|
370
|
+
|
371
|
+
fn get_free_port(bind_ip: &str) -> u16 {
|
372
|
+
// Can just ask OS to give us a port then close socket. OS's don't give that
|
373
|
+
// port back to anyone else anytime soon.
|
374
|
+
std::net::TcpListener::bind(format!("{}:0", bind_ip))
|
375
|
+
.unwrap()
|
376
|
+
.local_addr()
|
377
|
+
.unwrap()
|
378
|
+
.port()
|
379
|
+
}
|
380
|
+
|
381
|
+
/// Returns false if we successfully waited for another download to complete, or
|
382
|
+
/// true if the destination is known to exist. Should call again if false is
|
383
|
+
/// returned.
|
384
|
+
async fn lazy_download_exe(
|
385
|
+
client: &reqwest::Client,
|
386
|
+
uri: &str,
|
387
|
+
file_to_extract: &Path,
|
388
|
+
dest: &Path,
|
389
|
+
) -> anyhow::Result<bool> {
|
390
|
+
// If it already exists, do not extract
|
391
|
+
if dest.exists() {
|
392
|
+
return Ok(true);
|
393
|
+
}
|
394
|
+
|
395
|
+
// We only want to download if we're not already downloading. To avoid some
|
396
|
+
// kind of global lock, we'll just create the file eagerly w/ a temp
|
397
|
+
// filename and delete it on failure or move it on success. If the temp file
|
398
|
+
// already exists, we'll wait a bit and re-run this.
|
399
|
+
let temp_dest_str = format!("{}{}", dest.to_str().unwrap(), ".downloading");
|
400
|
+
let temp_dest = Path::new(&temp_dest_str);
|
401
|
+
// Try to open file, using a file mode on unix families
|
402
|
+
#[cfg(target_family = "unix")]
|
403
|
+
let file = OpenOptions::new()
|
404
|
+
.create_new(true)
|
405
|
+
.write(true)
|
406
|
+
.mode(0o755)
|
407
|
+
.open(temp_dest);
|
408
|
+
#[cfg(not(target_family = "unix"))]
|
409
|
+
let file = OpenOptions::new()
|
410
|
+
.create_new(true)
|
411
|
+
.write(true)
|
412
|
+
.open(temp_dest);
|
413
|
+
// This match only gets Ok if the file was downloaded and extracted to the
|
414
|
+
// temporary path
|
415
|
+
match file {
|
416
|
+
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {
|
417
|
+
// Since it already exists, we'll try once a second for 20 seconds
|
418
|
+
// to wait for it to be done, then return false so the caller can
|
419
|
+
// try again.
|
420
|
+
for _ in 0..20 {
|
421
|
+
sleep(Duration::from_secs(1)).await;
|
422
|
+
if !temp_dest.exists() {
|
423
|
+
return Ok(false);
|
424
|
+
}
|
425
|
+
}
|
426
|
+
Err(anyhow!(
|
427
|
+
"Temp download file at {} not complete after 20 seconds. \
|
428
|
+
Make sure another download isn't running for too long and delete the temp file.",
|
429
|
+
temp_dest.display()
|
430
|
+
))
|
431
|
+
}
|
432
|
+
Err(err) => Err(err.into()),
|
433
|
+
// If the dest was added since, just remove temp file
|
434
|
+
Ok(_) if dest.exists() => {
|
435
|
+
std::fs::remove_file(temp_dest)?;
|
436
|
+
return Ok(true);
|
437
|
+
}
|
438
|
+
// Download and extract the binary
|
439
|
+
Ok(mut temp_file) => {
|
440
|
+
info!("Downloading {} to {}", uri, dest.display());
|
441
|
+
download_and_extract(client, uri, file_to_extract, &mut temp_file)
|
442
|
+
.await
|
443
|
+
.map_err(|err| {
|
444
|
+
// Failed to download, just remove file
|
445
|
+
if let Err(err) = std::fs::remove_file(temp_dest) {
|
446
|
+
warn!(
|
447
|
+
"Failed removing temp file at {}: {:?}",
|
448
|
+
temp_dest.display(),
|
449
|
+
err
|
450
|
+
);
|
451
|
+
}
|
452
|
+
err
|
453
|
+
})
|
454
|
+
}
|
455
|
+
}?;
|
456
|
+
// Now that file should be dropped, we can rename
|
457
|
+
std::fs::rename(temp_dest, dest)?;
|
458
|
+
Ok(true)
|
459
|
+
}
|
460
|
+
|
461
|
+
async fn download_and_extract(
|
462
|
+
client: &reqwest::Client,
|
463
|
+
uri: &str,
|
464
|
+
file_to_extract: &Path,
|
465
|
+
dest: &mut std::fs::File,
|
466
|
+
) -> anyhow::Result<()> {
|
467
|
+
// Start download. We are using streaming here to extract the file from the
|
468
|
+
// tarball or zip instead of loading into memory for Cursor/Seek.
|
469
|
+
let resp = client.get(uri).send().await?;
|
470
|
+
// We have to map the error type to an io error
|
471
|
+
let stream = resp
|
472
|
+
.bytes_stream()
|
473
|
+
.map(|item| item.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err)));
|
474
|
+
|
475
|
+
// Since our tar/zip impls use sync IO, we have to create a bridge and run
|
476
|
+
// in a blocking closure.
|
477
|
+
let mut reader = SyncIoBridge::new(StreamReader::new(stream));
|
478
|
+
let tarball = if uri.ends_with(".tar.gz") {
|
479
|
+
true
|
480
|
+
} else if uri.ends_with(".zip") {
|
481
|
+
false
|
482
|
+
} else {
|
483
|
+
return Err(anyhow!("URI not .tar.gz or .zip"));
|
484
|
+
};
|
485
|
+
let file_to_extract = file_to_extract.to_path_buf();
|
486
|
+
let mut dest = dest.try_clone()?;
|
487
|
+
|
488
|
+
spawn_blocking(move || {
|
489
|
+
if tarball {
|
490
|
+
for entry in tar::Archive::new(GzDecoder::new(reader)).entries()? {
|
491
|
+
let mut entry = entry?;
|
492
|
+
if entry.path()? == file_to_extract {
|
493
|
+
std::io::copy(&mut entry, &mut dest)?;
|
494
|
+
return Ok(());
|
495
|
+
}
|
496
|
+
}
|
497
|
+
Err(anyhow!("Unable to find file in tarball"))
|
498
|
+
} else {
|
499
|
+
loop {
|
500
|
+
// This is the way to stream a zip file without creating an archive
|
501
|
+
// that requires Seek.
|
502
|
+
if let Some(mut file) = read_zipfile_from_stream(&mut reader)? {
|
503
|
+
// If this is the file we're expecting, extract it
|
504
|
+
if file.enclosed_name() == Some(&file_to_extract) {
|
505
|
+
std::io::copy(&mut file, &mut dest)?;
|
506
|
+
return Ok(());
|
507
|
+
}
|
508
|
+
} else {
|
509
|
+
return Err(anyhow!("Unable to find file in zip"));
|
510
|
+
}
|
511
|
+
}
|
512
|
+
}
|
513
|
+
})
|
514
|
+
.await?
|
515
|
+
}
|
@@ -0,0 +1,175 @@
|
|
1
|
+
#![warn(missing_docs)] // error if there are missing docs
|
2
|
+
#![allow(clippy::upper_case_acronyms)]
|
3
|
+
|
4
|
+
//! This crate provides a basis for creating new Temporal SDKs without completely starting from
|
5
|
+
//! scratch
|
6
|
+
|
7
|
+
#[cfg(test)]
|
8
|
+
#[macro_use]
|
9
|
+
pub extern crate assert_matches;
|
10
|
+
#[macro_use]
|
11
|
+
extern crate tracing;
|
12
|
+
extern crate core;
|
13
|
+
|
14
|
+
mod abstractions;
|
15
|
+
pub mod ephemeral_server;
|
16
|
+
mod log_export;
|
17
|
+
mod pollers;
|
18
|
+
mod protosext;
|
19
|
+
pub mod replay;
|
20
|
+
pub(crate) mod retry_logic;
|
21
|
+
pub(crate) mod telemetry;
|
22
|
+
mod worker;
|
23
|
+
|
24
|
+
#[cfg(test)]
|
25
|
+
mod core_tests;
|
26
|
+
#[cfg(test)]
|
27
|
+
#[macro_use]
|
28
|
+
mod test_help;
|
29
|
+
|
30
|
+
pub(crate) use temporal_sdk_core_api::errors;
|
31
|
+
|
32
|
+
pub use pollers::{
|
33
|
+
Client, ClientOptions, ClientOptionsBuilder, ClientTlsConfig, RetryClient, RetryConfig,
|
34
|
+
TlsConfig, WorkflowClientTrait,
|
35
|
+
};
|
36
|
+
pub use telemetry::{
|
37
|
+
fetch_global_buffered_logs, telemetry_init, Logger, MetricTemporality, MetricsExporter,
|
38
|
+
OtelCollectorOptions, TelemetryOptions, TelemetryOptionsBuilder, TraceExporter,
|
39
|
+
};
|
40
|
+
pub use temporal_sdk_core_api as api;
|
41
|
+
pub use temporal_sdk_core_protos as protos;
|
42
|
+
pub use temporal_sdk_core_protos::TaskToken;
|
43
|
+
pub use url::Url;
|
44
|
+
pub use worker::{Worker, WorkerConfig, WorkerConfigBuilder};
|
45
|
+
|
46
|
+
use crate::{
|
47
|
+
replay::mock_client_from_history,
|
48
|
+
telemetry::metrics::{MetricsContext, METRIC_METER},
|
49
|
+
worker::client::WorkerClientBag,
|
50
|
+
};
|
51
|
+
use std::sync::Arc;
|
52
|
+
use temporal_client::{ConfiguredClient, TemporalServiceClientWithMetrics};
|
53
|
+
use temporal_sdk_core_api::{
|
54
|
+
errors::{CompleteActivityError, PollActivityError, PollWfError},
|
55
|
+
CoreLog, Worker as WorkerTrait,
|
56
|
+
};
|
57
|
+
use temporal_sdk_core_protos::{coresdk::ActivityHeartbeat, temporal::api::history::v1::History};
|
58
|
+
|
59
|
+
lazy_static::lazy_static! {
|
60
|
+
/// A process-wide unique string, which will be different on every startup
|
61
|
+
static ref PROCCESS_UNIQ_ID: String = {
|
62
|
+
uuid::Uuid::new_v4().simple().to_string()
|
63
|
+
};
|
64
|
+
}
|
65
|
+
|
66
|
+
/// Initialize a worker bound to a task queue.
|
67
|
+
///
|
68
|
+
/// Lang implementations may pass in a [temporal_client::ConfiguredClient] directly (or a
|
69
|
+
/// [RetryClient] wrapping one, or a handful of other variants of the same idea). When they do so,
|
70
|
+
/// this function will always overwrite the client retry configuration, force the client to use the
|
71
|
+
/// namespace defined in the worker config, and set the client identity appropriately. IE: Use
|
72
|
+
/// [ClientOptions::connect_no_namespace], not [ClientOptions::connect].
|
73
|
+
pub fn init_worker<CT>(worker_config: WorkerConfig, client: CT) -> Worker
|
74
|
+
where
|
75
|
+
CT: Into<sealed::AnyClient>,
|
76
|
+
{
|
77
|
+
let client = {
|
78
|
+
let ll = client.into().into_inner();
|
79
|
+
let mut client = Client::new(*ll, worker_config.namespace.clone());
|
80
|
+
client.set_worker_build_id(worker_config.worker_build_id.clone());
|
81
|
+
if let Some(ref id_override) = worker_config.client_identity_override {
|
82
|
+
client.options_mut().identity = id_override.clone();
|
83
|
+
}
|
84
|
+
RetryClient::new(client, RetryConfig::default())
|
85
|
+
};
|
86
|
+
if client.namespace() != worker_config.namespace {
|
87
|
+
panic!("Passed in client is not bound to the same namespace as the worker");
|
88
|
+
}
|
89
|
+
let client_ident = client.get_options().identity.clone();
|
90
|
+
let sticky_q = sticky_q_name_for_worker(&client_ident, &worker_config);
|
91
|
+
let client_bag = Arc::new(WorkerClientBag::new(
|
92
|
+
client,
|
93
|
+
worker_config.namespace.clone(),
|
94
|
+
client_ident,
|
95
|
+
worker_config.worker_build_id.clone(),
|
96
|
+
worker_config.use_worker_versioning,
|
97
|
+
));
|
98
|
+
|
99
|
+
let metrics = MetricsContext::top_level(worker_config.namespace.clone())
|
100
|
+
.with_task_q(worker_config.task_queue.clone());
|
101
|
+
Worker::new(worker_config, sticky_q, client_bag, metrics)
|
102
|
+
}
|
103
|
+
|
104
|
+
/// Create a worker for replaying a specific history. It will auto-shutdown as soon as the history
|
105
|
+
/// has finished being replayed. The provided client should be a mock, and this should only be used
|
106
|
+
/// for workflow testing purposes.
|
107
|
+
pub fn init_replay_worker(
|
108
|
+
mut config: WorkerConfig,
|
109
|
+
history: &History,
|
110
|
+
) -> Result<Worker, anyhow::Error> {
|
111
|
+
info!(
|
112
|
+
task_queue = config.task_queue.as_str(),
|
113
|
+
"Registering replay worker"
|
114
|
+
);
|
115
|
+
config.max_cached_workflows = 1;
|
116
|
+
config.max_concurrent_wft_polls = 1;
|
117
|
+
config.no_remote_activities = true;
|
118
|
+
// Could possibly just use mocked pollers here?
|
119
|
+
let client = mock_client_from_history(history, config.task_queue.clone());
|
120
|
+
let run_id = history.extract_run_id_from_start()?.to_string();
|
121
|
+
let last_event = history.last_event_id();
|
122
|
+
let mut worker = Worker::new(config, None, Arc::new(client), MetricsContext::default());
|
123
|
+
worker.set_shutdown_on_run_reaches_event(run_id, last_event);
|
124
|
+
Ok(worker)
|
125
|
+
}
|
126
|
+
|
127
|
+
pub(crate) fn sticky_q_name_for_worker(
|
128
|
+
process_identity: &str,
|
129
|
+
config: &WorkerConfig,
|
130
|
+
) -> Option<String> {
|
131
|
+
if config.max_cached_workflows > 0 {
|
132
|
+
Some(format!(
|
133
|
+
"{}-{}-{}",
|
134
|
+
&process_identity, &config.task_queue, *PROCCESS_UNIQ_ID
|
135
|
+
))
|
136
|
+
} else {
|
137
|
+
None
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
mod sealed {
|
142
|
+
use super::*;
|
143
|
+
|
144
|
+
/// Allows passing different kinds of clients into things that want to be flexible. Motivating
|
145
|
+
/// use-case was worker initialization.
|
146
|
+
///
|
147
|
+
/// Needs to exist in this crate to avoid blanket impl conflicts.
|
148
|
+
pub struct AnyClient(Box<ConfiguredClient<TemporalServiceClientWithMetrics>>);
|
149
|
+
impl AnyClient {
|
150
|
+
pub(crate) fn into_inner(self) -> Box<ConfiguredClient<TemporalServiceClientWithMetrics>> {
|
151
|
+
self.0
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
impl From<RetryClient<ConfiguredClient<TemporalServiceClientWithMetrics>>> for AnyClient {
|
156
|
+
fn from(c: RetryClient<ConfiguredClient<TemporalServiceClientWithMetrics>>) -> Self {
|
157
|
+
Self(Box::new(c.into_inner()))
|
158
|
+
}
|
159
|
+
}
|
160
|
+
impl From<RetryClient<Client>> for AnyClient {
|
161
|
+
fn from(c: RetryClient<Client>) -> Self {
|
162
|
+
Self(Box::new(c.into_inner().into_inner()))
|
163
|
+
}
|
164
|
+
}
|
165
|
+
impl From<Arc<RetryClient<Client>>> for AnyClient {
|
166
|
+
fn from(c: Arc<RetryClient<Client>>) -> Self {
|
167
|
+
Self(Box::new(c.get_client().inner().clone()))
|
168
|
+
}
|
169
|
+
}
|
170
|
+
impl From<ConfiguredClient<TemporalServiceClientWithMetrics>> for AnyClient {
|
171
|
+
fn from(c: ConfiguredClient<TemporalServiceClientWithMetrics>) -> Self {
|
172
|
+
Self(Box::new(c))
|
173
|
+
}
|
174
|
+
}
|
175
|
+
}
|