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,875 @@
|
|
1
|
+
pub(crate) use temporal_sdk_core_test_utils::canned_histories;
|
2
|
+
|
3
|
+
use crate::{
|
4
|
+
pollers::{BoxedActPoller, BoxedPoller, BoxedWFPoller, MockManualPoller, MockPoller},
|
5
|
+
protosext::ValidPollWFTQResponse,
|
6
|
+
replay::TestHistoryBuilder,
|
7
|
+
sticky_q_name_for_worker,
|
8
|
+
worker::{
|
9
|
+
client::{mocks::mock_workflow_client, MockWorkerClient, WorkerClient},
|
10
|
+
new_wft_poller,
|
11
|
+
},
|
12
|
+
TaskToken, Worker, WorkerConfig, WorkerConfigBuilder,
|
13
|
+
};
|
14
|
+
use bimap::BiMap;
|
15
|
+
use futures::{future::BoxFuture, stream, stream::BoxStream, FutureExt, Stream, StreamExt};
|
16
|
+
use mockall::TimesRange;
|
17
|
+
use parking_lot::RwLock;
|
18
|
+
use std::{
|
19
|
+
collections::{BTreeMap, HashMap, HashSet, VecDeque},
|
20
|
+
ops::{Deref, DerefMut},
|
21
|
+
pin::Pin,
|
22
|
+
sync::{
|
23
|
+
atomic::{AtomicBool, Ordering},
|
24
|
+
Arc,
|
25
|
+
},
|
26
|
+
task::{Context, Poll},
|
27
|
+
time::Duration,
|
28
|
+
};
|
29
|
+
use temporal_client::WorkflowTaskCompletion;
|
30
|
+
use temporal_sdk_core_api::Worker as WorkerTrait;
|
31
|
+
use temporal_sdk_core_protos::{
|
32
|
+
coresdk::{
|
33
|
+
workflow_activation::WorkflowActivation,
|
34
|
+
workflow_commands::workflow_command,
|
35
|
+
workflow_completion::{self, workflow_activation_completion, WorkflowActivationCompletion},
|
36
|
+
},
|
37
|
+
temporal::api::{
|
38
|
+
common::v1::WorkflowExecution,
|
39
|
+
enums::v1::WorkflowTaskFailedCause,
|
40
|
+
failure::v1::Failure,
|
41
|
+
workflowservice::v1::{
|
42
|
+
PollActivityTaskQueueResponse, PollWorkflowTaskQueueResponse,
|
43
|
+
RespondWorkflowTaskCompletedResponse,
|
44
|
+
},
|
45
|
+
},
|
46
|
+
};
|
47
|
+
use temporal_sdk_core_test_utils::TestWorker;
|
48
|
+
use tokio::sync::{mpsc::unbounded_channel, Notify};
|
49
|
+
use tokio_stream::wrappers::UnboundedReceiverStream;
|
50
|
+
use tokio_util::sync::CancellationToken;
|
51
|
+
|
52
|
+
pub const TEST_Q: &str = "q";
|
53
|
+
pub static NO_MORE_WORK_ERROR_MSG: &str = "No more work to do";
|
54
|
+
|
55
|
+
pub fn test_worker_cfg() -> WorkerConfigBuilder {
|
56
|
+
let mut wcb = WorkerConfigBuilder::default();
|
57
|
+
wcb.namespace("default")
|
58
|
+
.task_queue(TEST_Q)
|
59
|
+
.worker_build_id("test_bin_id")
|
60
|
+
// Serial polling since it makes mocking much easier.
|
61
|
+
.max_concurrent_wft_polls(1_usize);
|
62
|
+
wcb
|
63
|
+
}
|
64
|
+
|
65
|
+
/// When constructing responses for mocks, indicates how a given response should be built
|
66
|
+
#[derive(derive_more::From)]
|
67
|
+
#[allow(clippy::large_enum_variant)] // Test only code, whatever.
|
68
|
+
pub enum ResponseType {
|
69
|
+
ToTaskNum(usize),
|
70
|
+
/// Returns just the history after the WFT completed of the provided task number - 1, through to
|
71
|
+
/// the next WFT started. Simulating the incremental history for just the provided task number
|
72
|
+
#[from(ignore)]
|
73
|
+
OneTask(usize),
|
74
|
+
/// Waits until the future resolves before responding as `ToTaskNum` with the provided number
|
75
|
+
UntilResolved(BoxFuture<'static, ()>, usize),
|
76
|
+
/// Waits until the future resolves before responding with the provided response
|
77
|
+
UntilResolvedRaw(BoxFuture<'static, ()>, PollWorkflowTaskQueueResponse),
|
78
|
+
AllHistory,
|
79
|
+
Raw(PollWorkflowTaskQueueResponse),
|
80
|
+
}
|
81
|
+
#[derive(Eq, PartialEq, Hash)]
|
82
|
+
pub enum HashableResponseType {
|
83
|
+
ToTaskNum(usize),
|
84
|
+
OneTask(usize),
|
85
|
+
UntilResolved(usize),
|
86
|
+
UntilResolvedRaw(TaskToken),
|
87
|
+
AllHistory,
|
88
|
+
Raw(TaskToken),
|
89
|
+
}
|
90
|
+
impl ResponseType {
|
91
|
+
pub fn hashable(&self) -> HashableResponseType {
|
92
|
+
match self {
|
93
|
+
ResponseType::ToTaskNum(x) => HashableResponseType::ToTaskNum(*x),
|
94
|
+
ResponseType::OneTask(x) => HashableResponseType::OneTask(*x),
|
95
|
+
ResponseType::AllHistory => HashableResponseType::AllHistory,
|
96
|
+
ResponseType::Raw(r) => HashableResponseType::Raw(r.task_token.clone().into()),
|
97
|
+
ResponseType::UntilResolved(_, x) => HashableResponseType::UntilResolved(*x),
|
98
|
+
ResponseType::UntilResolvedRaw(_, r) => {
|
99
|
+
HashableResponseType::UntilResolvedRaw(r.task_token.clone().into())
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
104
|
+
impl From<&usize> for ResponseType {
|
105
|
+
fn from(u: &usize) -> Self {
|
106
|
+
Self::ToTaskNum(*u)
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
/// Given identifiers for a workflow/run, and a test history builder, construct an instance of
|
111
|
+
/// the a worker with a mock server client that will produce the responses as appropriate.
|
112
|
+
///
|
113
|
+
/// `response_batches` is used to control the fake [PollWorkflowTaskQueueResponse]s returned. For
|
114
|
+
/// each number in the input list, a fake response will be prepared which includes history up to the
|
115
|
+
/// workflow task with that number, as in [TestHistoryBuilder::get_history_info].
|
116
|
+
pub(crate) fn build_fake_worker(
|
117
|
+
wf_id: &str,
|
118
|
+
t: TestHistoryBuilder,
|
119
|
+
response_batches: impl IntoIterator<Item = impl Into<ResponseType>>,
|
120
|
+
) -> Worker {
|
121
|
+
let response_batches = response_batches.into_iter().map(Into::into).collect();
|
122
|
+
let mock_holder = build_multihist_mock_sg(
|
123
|
+
vec![FakeWfResponses {
|
124
|
+
wf_id: wf_id.to_owned(),
|
125
|
+
hist: t,
|
126
|
+
response_batches,
|
127
|
+
}],
|
128
|
+
true,
|
129
|
+
0,
|
130
|
+
);
|
131
|
+
mock_worker(mock_holder)
|
132
|
+
}
|
133
|
+
|
134
|
+
pub(crate) fn mock_worker(mocks: MocksHolder) -> Worker {
|
135
|
+
let sticky_q = sticky_q_name_for_worker("unit-test", &mocks.inputs.config);
|
136
|
+
Worker::new_with_pollers(
|
137
|
+
mocks.inputs.config,
|
138
|
+
sticky_q,
|
139
|
+
mocks.client,
|
140
|
+
mocks.inputs.wft_stream,
|
141
|
+
mocks.inputs.act_poller,
|
142
|
+
Default::default(),
|
143
|
+
CancellationToken::new(),
|
144
|
+
)
|
145
|
+
}
|
146
|
+
|
147
|
+
pub(crate) fn mock_sdk(poll_cfg: MockPollCfg) -> TestWorker {
|
148
|
+
mock_sdk_cfg(poll_cfg, |_| {})
|
149
|
+
}
|
150
|
+
pub(crate) fn mock_sdk_cfg(
|
151
|
+
mut poll_cfg: MockPollCfg,
|
152
|
+
mutator: impl FnOnce(&mut WorkerConfig),
|
153
|
+
) -> TestWorker {
|
154
|
+
poll_cfg.using_rust_sdk = true;
|
155
|
+
let mut mock = build_mock_pollers(poll_cfg);
|
156
|
+
mock.worker_cfg(mutator);
|
157
|
+
let core = mock_worker(mock);
|
158
|
+
TestWorker::new(Arc::new(core), TEST_Q.to_string())
|
159
|
+
}
|
160
|
+
|
161
|
+
pub struct FakeWfResponses {
|
162
|
+
pub wf_id: String,
|
163
|
+
pub hist: TestHistoryBuilder,
|
164
|
+
pub response_batches: Vec<ResponseType>,
|
165
|
+
}
|
166
|
+
|
167
|
+
// TODO: Should be all-internal to this module
|
168
|
+
pub struct MocksHolder {
|
169
|
+
client: Arc<dyn WorkerClient>,
|
170
|
+
inputs: MockWorkerInputs,
|
171
|
+
pub outstanding_task_map: Option<OutstandingWFTMap>,
|
172
|
+
}
|
173
|
+
|
174
|
+
impl MocksHolder {
|
175
|
+
pub fn worker_cfg(&mut self, mutator: impl FnOnce(&mut WorkerConfig)) {
|
176
|
+
mutator(&mut self.inputs.config);
|
177
|
+
}
|
178
|
+
pub fn set_act_poller(&mut self, poller: BoxedActPoller) {
|
179
|
+
self.inputs.act_poller = Some(poller);
|
180
|
+
}
|
181
|
+
/// Can be used for tests that need to avoid auto-shutdown due to running out of mock responses
|
182
|
+
pub fn make_wft_stream_interminable(&mut self) {
|
183
|
+
let old_stream = std::mem::replace(&mut self.inputs.wft_stream, stream::pending().boxed());
|
184
|
+
self.inputs.wft_stream = old_stream.chain(stream::pending()).boxed();
|
185
|
+
}
|
186
|
+
}
|
187
|
+
|
188
|
+
pub struct MockWorkerInputs {
|
189
|
+
pub wft_stream: BoxStream<'static, Result<ValidPollWFTQResponse, tonic::Status>>,
|
190
|
+
pub act_poller: Option<BoxedActPoller>,
|
191
|
+
pub config: WorkerConfig,
|
192
|
+
}
|
193
|
+
|
194
|
+
impl Default for MockWorkerInputs {
|
195
|
+
fn default() -> Self {
|
196
|
+
Self::new_from_poller(Box::from(mock_poller()))
|
197
|
+
}
|
198
|
+
}
|
199
|
+
|
200
|
+
impl MockWorkerInputs {
|
201
|
+
pub fn new(
|
202
|
+
wft_stream: BoxStream<'static, Result<ValidPollWFTQResponse, tonic::Status>>,
|
203
|
+
) -> Self {
|
204
|
+
Self {
|
205
|
+
wft_stream,
|
206
|
+
act_poller: None,
|
207
|
+
config: test_worker_cfg().build().unwrap(),
|
208
|
+
}
|
209
|
+
}
|
210
|
+
pub fn new_from_poller(wf_poller: BoxedWFPoller) -> Self {
|
211
|
+
Self {
|
212
|
+
wft_stream: new_wft_poller(wf_poller, Default::default()).boxed(),
|
213
|
+
act_poller: None,
|
214
|
+
config: test_worker_cfg().build().unwrap(),
|
215
|
+
}
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
impl MocksHolder {
|
220
|
+
pub(crate) fn from_mock_worker(
|
221
|
+
client: impl WorkerClient + 'static,
|
222
|
+
mock_worker: MockWorkerInputs,
|
223
|
+
) -> Self {
|
224
|
+
Self {
|
225
|
+
client: Arc::new(client),
|
226
|
+
inputs: mock_worker,
|
227
|
+
outstanding_task_map: None,
|
228
|
+
}
|
229
|
+
}
|
230
|
+
|
231
|
+
/// Uses the provided list of tasks to create a mock poller for the `TEST_Q`
|
232
|
+
pub(crate) fn from_client_with_activities<ACT>(
|
233
|
+
client: impl WorkerClient + 'static,
|
234
|
+
act_tasks: ACT,
|
235
|
+
) -> Self
|
236
|
+
where
|
237
|
+
ACT: IntoIterator<Item = QueueResponse<PollActivityTaskQueueResponse>>,
|
238
|
+
<ACT as IntoIterator>::IntoIter: Send + 'static,
|
239
|
+
{
|
240
|
+
let wft_stream = stream::pending().boxed();
|
241
|
+
let mock_act_poller = mock_poller_from_resps(act_tasks);
|
242
|
+
let mock_worker = MockWorkerInputs {
|
243
|
+
wft_stream,
|
244
|
+
act_poller: Some(mock_act_poller),
|
245
|
+
config: test_worker_cfg().build().unwrap(),
|
246
|
+
};
|
247
|
+
Self {
|
248
|
+
client: Arc::new(client),
|
249
|
+
inputs: mock_worker,
|
250
|
+
outstanding_task_map: None,
|
251
|
+
}
|
252
|
+
}
|
253
|
+
|
254
|
+
/// Uses the provided task responses and delivers them as quickly as possible when polled.
|
255
|
+
/// This is only useful to test buffering, as typically you do not want to pretend that
|
256
|
+
/// the server is delivering WFTs super fast for the same run.
|
257
|
+
pub(crate) fn from_wft_stream(
|
258
|
+
client: impl WorkerClient + 'static,
|
259
|
+
stream: impl Stream<Item = PollWorkflowTaskQueueResponse> + Send + 'static,
|
260
|
+
) -> Self {
|
261
|
+
let wft_stream = stream
|
262
|
+
.map(|r| Ok(r.try_into().expect("Mock responses must be valid work")))
|
263
|
+
.boxed();
|
264
|
+
let mock_worker = MockWorkerInputs {
|
265
|
+
wft_stream,
|
266
|
+
act_poller: None,
|
267
|
+
config: test_worker_cfg().build().unwrap(),
|
268
|
+
};
|
269
|
+
Self {
|
270
|
+
client: Arc::new(client),
|
271
|
+
inputs: mock_worker,
|
272
|
+
outstanding_task_map: None,
|
273
|
+
}
|
274
|
+
}
|
275
|
+
}
|
276
|
+
|
277
|
+
// TODO: Un-pub ideally
|
278
|
+
pub(crate) fn mock_poller_from_resps<T, I>(tasks: I) -> BoxedPoller<T>
|
279
|
+
where
|
280
|
+
T: Send + Sync + 'static,
|
281
|
+
I: IntoIterator<Item = QueueResponse<T>>,
|
282
|
+
<I as IntoIterator>::IntoIter: Send + 'static,
|
283
|
+
{
|
284
|
+
let mut mock_poller = mock_manual_poller();
|
285
|
+
let mut tasks = tasks.into_iter();
|
286
|
+
mock_poller.expect_poll().returning(move || {
|
287
|
+
if let Some(t) = tasks.next() {
|
288
|
+
async move {
|
289
|
+
if let Some(f) = t.delay_until {
|
290
|
+
f.await;
|
291
|
+
}
|
292
|
+
Some(Ok(t.resp))
|
293
|
+
}
|
294
|
+
.boxed()
|
295
|
+
} else {
|
296
|
+
async { Some(Err(tonic::Status::cancelled(NO_MORE_WORK_ERROR_MSG))) }.boxed()
|
297
|
+
}
|
298
|
+
});
|
299
|
+
Box::new(mock_poller) as BoxedPoller<T>
|
300
|
+
}
|
301
|
+
|
302
|
+
pub fn mock_poller<T>() -> MockPoller<T>
|
303
|
+
where
|
304
|
+
T: Send + Sync + 'static,
|
305
|
+
{
|
306
|
+
let mut mock_poller = MockPoller::new();
|
307
|
+
mock_poller.expect_shutdown_box().return_const(());
|
308
|
+
mock_poller.expect_notify_shutdown().return_const(());
|
309
|
+
mock_poller
|
310
|
+
}
|
311
|
+
|
312
|
+
pub fn mock_manual_poller<T>() -> MockManualPoller<T>
|
313
|
+
where
|
314
|
+
T: Send + Sync + 'static,
|
315
|
+
{
|
316
|
+
let mut mock_poller = MockManualPoller::new();
|
317
|
+
mock_poller
|
318
|
+
.expect_shutdown_box()
|
319
|
+
.returning(|| async {}.boxed());
|
320
|
+
mock_poller.expect_notify_shutdown().return_const(());
|
321
|
+
mock_poller
|
322
|
+
}
|
323
|
+
|
324
|
+
/// Build a mock server client capable of returning multiple different histories for different
|
325
|
+
/// workflows. It does so by tracking outstanding workflow tasks like is also happening in core
|
326
|
+
/// (which is unfortunately a bit redundant, we could provide hooks in core but that feels a little
|
327
|
+
/// nasty). If there is an outstanding task for a given workflow, new chunks of its history are not
|
328
|
+
/// returned. If there is not, the next batch of history is returned for any workflow without an
|
329
|
+
/// outstanding task. Outstanding tasks are cleared on completion, failure, or eviction.
|
330
|
+
///
|
331
|
+
/// `num_expected_fails` can be provided to set a specific number of expected failed workflow tasks
|
332
|
+
/// sent to the server.
|
333
|
+
pub(crate) fn build_multihist_mock_sg(
|
334
|
+
hists: impl IntoIterator<Item = FakeWfResponses>,
|
335
|
+
enforce_correct_number_of_polls: bool,
|
336
|
+
num_expected_fails: usize,
|
337
|
+
) -> MocksHolder {
|
338
|
+
let mh = MockPollCfg::new(
|
339
|
+
hists.into_iter().collect(),
|
340
|
+
enforce_correct_number_of_polls,
|
341
|
+
num_expected_fails,
|
342
|
+
);
|
343
|
+
build_mock_pollers(mh)
|
344
|
+
}
|
345
|
+
|
346
|
+
/// See [build_multihist_mock_sg] -- one history convenience version
|
347
|
+
pub(crate) fn single_hist_mock_sg(
|
348
|
+
wf_id: &str,
|
349
|
+
t: TestHistoryBuilder,
|
350
|
+
response_batches: impl IntoIterator<Item = impl Into<ResponseType>>,
|
351
|
+
mock_client: MockWorkerClient,
|
352
|
+
enforce_num_polls: bool,
|
353
|
+
) -> MocksHolder {
|
354
|
+
let mut mh = MockPollCfg::from_resp_batches(wf_id, t, response_batches, mock_client);
|
355
|
+
mh.enforce_correct_number_of_polls = enforce_num_polls;
|
356
|
+
build_mock_pollers(mh)
|
357
|
+
}
|
358
|
+
|
359
|
+
#[allow(clippy::type_complexity)]
|
360
|
+
pub(crate) struct MockPollCfg {
|
361
|
+
pub hists: Vec<FakeWfResponses>,
|
362
|
+
pub enforce_correct_number_of_polls: bool,
|
363
|
+
pub num_expected_fails: usize,
|
364
|
+
pub num_expected_legacy_query_resps: usize,
|
365
|
+
pub mock_client: MockWorkerClient,
|
366
|
+
/// All calls to fail WFTs must match this predicate
|
367
|
+
pub expect_fail_wft_matcher:
|
368
|
+
Box<dyn Fn(&TaskToken, &WorkflowTaskFailedCause, &Option<Failure>) -> bool + Send>,
|
369
|
+
pub completion_asserts: Option<Box<dyn Fn(&WorkflowTaskCompletion) + Send>>,
|
370
|
+
/// If being used with the Rust SDK, this is set true. It ensures pollers will not error out
|
371
|
+
/// early with no work, since we cannot know the exact number of times polling will happen.
|
372
|
+
/// Instead, they will just block forever.
|
373
|
+
pub using_rust_sdk: bool,
|
374
|
+
}
|
375
|
+
|
376
|
+
impl MockPollCfg {
|
377
|
+
pub fn new(
|
378
|
+
hists: Vec<FakeWfResponses>,
|
379
|
+
enforce_correct_number_of_polls: bool,
|
380
|
+
num_expected_fails: usize,
|
381
|
+
) -> Self {
|
382
|
+
Self {
|
383
|
+
hists,
|
384
|
+
enforce_correct_number_of_polls,
|
385
|
+
num_expected_fails,
|
386
|
+
num_expected_legacy_query_resps: 0,
|
387
|
+
mock_client: mock_workflow_client(),
|
388
|
+
expect_fail_wft_matcher: Box::new(|_, _, _| true),
|
389
|
+
completion_asserts: None,
|
390
|
+
using_rust_sdk: false,
|
391
|
+
}
|
392
|
+
}
|
393
|
+
pub fn from_resp_batches(
|
394
|
+
wf_id: &str,
|
395
|
+
t: TestHistoryBuilder,
|
396
|
+
resps: impl IntoIterator<Item = impl Into<ResponseType>>,
|
397
|
+
mock_client: MockWorkerClient,
|
398
|
+
) -> Self {
|
399
|
+
Self {
|
400
|
+
hists: vec![FakeWfResponses {
|
401
|
+
wf_id: wf_id.to_owned(),
|
402
|
+
hist: t,
|
403
|
+
response_batches: resps.into_iter().map(Into::into).collect(),
|
404
|
+
}],
|
405
|
+
enforce_correct_number_of_polls: true,
|
406
|
+
num_expected_fails: 0,
|
407
|
+
num_expected_legacy_query_resps: 0,
|
408
|
+
mock_client,
|
409
|
+
expect_fail_wft_matcher: Box::new(|_, _, _| true),
|
410
|
+
completion_asserts: None,
|
411
|
+
using_rust_sdk: false,
|
412
|
+
}
|
413
|
+
}
|
414
|
+
}
|
415
|
+
|
416
|
+
#[derive(Default, Clone)]
|
417
|
+
pub struct OutstandingWFTMap {
|
418
|
+
map: Arc<RwLock<BiMap<String, TaskToken>>>,
|
419
|
+
waker: Arc<Notify>,
|
420
|
+
all_work_delivered: Arc<AtomicBool>,
|
421
|
+
}
|
422
|
+
impl OutstandingWFTMap {
|
423
|
+
fn has_run(&self, run_id: &str) -> bool {
|
424
|
+
self.map.read().contains_left(run_id)
|
425
|
+
}
|
426
|
+
fn put_token(&self, run_id: String, token: TaskToken) {
|
427
|
+
self.map.write().insert(run_id, token);
|
428
|
+
}
|
429
|
+
fn release_token(&self, token: &TaskToken) {
|
430
|
+
self.map.write().remove_by_right(token);
|
431
|
+
self.waker.notify_one();
|
432
|
+
}
|
433
|
+
pub fn release_run(&self, run_id: &str) {
|
434
|
+
self.map.write().remove_by_left(run_id);
|
435
|
+
self.waker.notify_waiters();
|
436
|
+
}
|
437
|
+
pub fn all_work_delivered(&self) -> bool {
|
438
|
+
self.all_work_delivered.load(Ordering::Acquire)
|
439
|
+
}
|
440
|
+
}
|
441
|
+
|
442
|
+
struct EnsuresWorkDoneWFTStream {
|
443
|
+
inner: UnboundedReceiverStream<ValidPollWFTQResponse>,
|
444
|
+
all_work_was_completed: Arc<AtomicBool>,
|
445
|
+
}
|
446
|
+
impl Stream for EnsuresWorkDoneWFTStream {
|
447
|
+
type Item = ValidPollWFTQResponse;
|
448
|
+
|
449
|
+
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
450
|
+
self.inner.poll_next_unpin(cx)
|
451
|
+
}
|
452
|
+
}
|
453
|
+
impl Drop for EnsuresWorkDoneWFTStream {
|
454
|
+
fn drop(&mut self) {
|
455
|
+
if !self.all_work_was_completed.load(Ordering::Acquire) && !std::thread::panicking() {
|
456
|
+
panic!("Not all workflow tasks were taken from mock!");
|
457
|
+
}
|
458
|
+
}
|
459
|
+
}
|
460
|
+
|
461
|
+
/// Given an iterable of fake responses, return the mocks & associated data to work with them
|
462
|
+
pub(crate) fn build_mock_pollers(mut cfg: MockPollCfg) -> MocksHolder {
|
463
|
+
let mut task_q_resps: BTreeMap<String, VecDeque<_>> = BTreeMap::new();
|
464
|
+
let all_work_delivered = if cfg.enforce_correct_number_of_polls && !cfg.using_rust_sdk {
|
465
|
+
Arc::new(AtomicBool::new(false))
|
466
|
+
} else {
|
467
|
+
Arc::new(AtomicBool::new(true))
|
468
|
+
};
|
469
|
+
|
470
|
+
let outstanding_wf_task_tokens = OutstandingWFTMap {
|
471
|
+
map: Arc::new(Default::default()),
|
472
|
+
waker: Arc::new(Default::default()),
|
473
|
+
all_work_delivered: all_work_delivered.clone(),
|
474
|
+
};
|
475
|
+
|
476
|
+
for hist in cfg.hists {
|
477
|
+
let full_hist_info = hist.hist.get_full_history_info().unwrap();
|
478
|
+
// Ensure no response batch is trying to return more tasks than the history contains
|
479
|
+
for respt in &hist.response_batches {
|
480
|
+
if let ResponseType::ToTaskNum(rb_wf_num) = respt {
|
481
|
+
assert!(
|
482
|
+
*rb_wf_num <= full_hist_info.wf_task_count(),
|
483
|
+
"Wf task count {} is not <= total task count {}",
|
484
|
+
rb_wf_num,
|
485
|
+
full_hist_info.wf_task_count()
|
486
|
+
);
|
487
|
+
}
|
488
|
+
}
|
489
|
+
|
490
|
+
// Convert history batches into poll responses, while also tracking how many times a given
|
491
|
+
// history has been returned so we can increment the associated attempt number on the WFT.
|
492
|
+
// NOTE: This is hard to use properly with the `AfterEveryReply` testing eviction mode.
|
493
|
+
// Such usages need a history different from other eviction modes which would include
|
494
|
+
// WFT timeouts or something to simulate the task getting dropped.
|
495
|
+
let mut attempts_at_task_num = HashMap::new();
|
496
|
+
let responses: Vec<_> = hist
|
497
|
+
.response_batches
|
498
|
+
.into_iter()
|
499
|
+
.map(|response| {
|
500
|
+
let cur_attempt = attempts_at_task_num.entry(response.hashable()).or_insert(1);
|
501
|
+
let mut r = hist_to_poll_resp(&hist.hist, hist.wf_id.clone(), response, TEST_Q);
|
502
|
+
r.attempt = *cur_attempt;
|
503
|
+
*cur_attempt += 1;
|
504
|
+
r
|
505
|
+
})
|
506
|
+
.collect();
|
507
|
+
|
508
|
+
let tasks = VecDeque::from(responses);
|
509
|
+
task_q_resps.insert(hist.wf_id, tasks);
|
510
|
+
}
|
511
|
+
|
512
|
+
// The poller will return history from any workflow runs that do not have currently
|
513
|
+
// outstanding tasks.
|
514
|
+
let outstanding = outstanding_wf_task_tokens.clone();
|
515
|
+
let outstanding_wakeup = outstanding.waker.clone();
|
516
|
+
let (wft_tx, wft_rx) = unbounded_channel();
|
517
|
+
tokio::task::spawn(async move {
|
518
|
+
loop {
|
519
|
+
let mut resp = None;
|
520
|
+
let mut resp_iter = task_q_resps.iter_mut();
|
521
|
+
for (_, tasks) in &mut resp_iter {
|
522
|
+
// Must extract run id from a workflow task associated with this workflow
|
523
|
+
// TODO: Case where run id changes for same workflow id is not handled here
|
524
|
+
if let Some(t) = tasks.get(0) {
|
525
|
+
let rid = t.workflow_execution.as_ref().unwrap().run_id.clone();
|
526
|
+
if !outstanding.has_run(&rid) {
|
527
|
+
let t = tasks.pop_front().unwrap();
|
528
|
+
outstanding.put_token(rid, TaskToken(t.task_token.clone()));
|
529
|
+
resp = Some(t);
|
530
|
+
break;
|
531
|
+
}
|
532
|
+
}
|
533
|
+
}
|
534
|
+
let no_tasks_for_anyone = resp_iter.next().is_none();
|
535
|
+
|
536
|
+
if let Some(resp) = resp {
|
537
|
+
if let Some(d) = resp.delay_until {
|
538
|
+
d.await;
|
539
|
+
}
|
540
|
+
if wft_tx
|
541
|
+
.send(
|
542
|
+
resp.resp
|
543
|
+
.try_into()
|
544
|
+
.expect("Mock responses must be valid work"),
|
545
|
+
)
|
546
|
+
.is_err()
|
547
|
+
{
|
548
|
+
dbg!("Exiting mock WFT task because rcv half of stream was dropped");
|
549
|
+
break;
|
550
|
+
}
|
551
|
+
}
|
552
|
+
|
553
|
+
// No more work to do
|
554
|
+
if task_q_resps.values().all(|q| q.is_empty()) {
|
555
|
+
outstanding
|
556
|
+
.all_work_delivered
|
557
|
+
.store(true, Ordering::Release);
|
558
|
+
break;
|
559
|
+
}
|
560
|
+
|
561
|
+
if no_tasks_for_anyone {
|
562
|
+
tokio::select! {
|
563
|
+
_ = outstanding_wakeup.notified() => {}
|
564
|
+
_ = tokio::time::sleep(Duration::from_secs(60)) => {}
|
565
|
+
};
|
566
|
+
}
|
567
|
+
}
|
568
|
+
});
|
569
|
+
let mock_worker = MockWorkerInputs::new(
|
570
|
+
EnsuresWorkDoneWFTStream {
|
571
|
+
inner: UnboundedReceiverStream::new(wft_rx),
|
572
|
+
all_work_was_completed: all_work_delivered,
|
573
|
+
}
|
574
|
+
.map(Ok)
|
575
|
+
.boxed(),
|
576
|
+
);
|
577
|
+
|
578
|
+
let outstanding = outstanding_wf_task_tokens.clone();
|
579
|
+
cfg.mock_client
|
580
|
+
.expect_complete_workflow_task()
|
581
|
+
.returning(move |comp| {
|
582
|
+
if let Some(ass) = cfg.completion_asserts.as_ref() {
|
583
|
+
// tee hee
|
584
|
+
ass(&comp)
|
585
|
+
}
|
586
|
+
outstanding.release_token(&comp.task_token);
|
587
|
+
Ok(RespondWorkflowTaskCompletedResponse::default())
|
588
|
+
});
|
589
|
+
let outstanding = outstanding_wf_task_tokens.clone();
|
590
|
+
cfg.mock_client
|
591
|
+
.expect_fail_workflow_task()
|
592
|
+
.withf(cfg.expect_fail_wft_matcher)
|
593
|
+
.times::<TimesRange>(cfg.num_expected_fails.into())
|
594
|
+
.returning(move |tt, _, _| {
|
595
|
+
outstanding.release_token(&tt);
|
596
|
+
Ok(Default::default())
|
597
|
+
});
|
598
|
+
let outstanding = outstanding_wf_task_tokens.clone();
|
599
|
+
cfg.mock_client
|
600
|
+
.expect_respond_legacy_query()
|
601
|
+
.times::<TimesRange>(cfg.num_expected_legacy_query_resps.into())
|
602
|
+
.returning(move |tt, _| {
|
603
|
+
outstanding.release_token(&tt);
|
604
|
+
Ok(Default::default())
|
605
|
+
});
|
606
|
+
|
607
|
+
MocksHolder {
|
608
|
+
client: Arc::new(cfg.mock_client),
|
609
|
+
inputs: mock_worker,
|
610
|
+
outstanding_task_map: Some(outstanding_wf_task_tokens),
|
611
|
+
}
|
612
|
+
}
|
613
|
+
|
614
|
+
pub struct QueueResponse<T> {
|
615
|
+
pub resp: T,
|
616
|
+
pub delay_until: Option<BoxFuture<'static, ()>>,
|
617
|
+
}
|
618
|
+
impl<T> From<T> for QueueResponse<T> {
|
619
|
+
fn from(resp: T) -> Self {
|
620
|
+
QueueResponse {
|
621
|
+
resp,
|
622
|
+
delay_until: None,
|
623
|
+
}
|
624
|
+
}
|
625
|
+
}
|
626
|
+
impl From<QueueResponse<PollWorkflowTaskQueueResponse>> for ResponseType {
|
627
|
+
fn from(qr: QueueResponse<PollWorkflowTaskQueueResponse>) -> Self {
|
628
|
+
if let Some(du) = qr.delay_until {
|
629
|
+
ResponseType::UntilResolvedRaw(du, qr.resp)
|
630
|
+
} else {
|
631
|
+
ResponseType::Raw(qr.resp)
|
632
|
+
}
|
633
|
+
}
|
634
|
+
}
|
635
|
+
impl<T> Deref for QueueResponse<T> {
|
636
|
+
type Target = T;
|
637
|
+
|
638
|
+
fn deref(&self) -> &Self::Target {
|
639
|
+
&self.resp
|
640
|
+
}
|
641
|
+
}
|
642
|
+
impl<T> DerefMut for QueueResponse<T> {
|
643
|
+
fn deref_mut(&mut self) -> &mut Self::Target {
|
644
|
+
&mut self.resp
|
645
|
+
}
|
646
|
+
}
|
647
|
+
|
648
|
+
pub fn hist_to_poll_resp(
|
649
|
+
t: &TestHistoryBuilder,
|
650
|
+
wf_id: impl Into<String>,
|
651
|
+
response_type: ResponseType,
|
652
|
+
task_queue: impl Into<String>,
|
653
|
+
) -> QueueResponse<PollWorkflowTaskQueueResponse> {
|
654
|
+
let run_id = t.get_orig_run_id();
|
655
|
+
let wf = WorkflowExecution {
|
656
|
+
workflow_id: wf_id.into(),
|
657
|
+
run_id: run_id.to_string(),
|
658
|
+
};
|
659
|
+
let mut delay_until = None;
|
660
|
+
let hist_info = match response_type {
|
661
|
+
ResponseType::ToTaskNum(tn) => t.get_history_info(tn).unwrap(),
|
662
|
+
ResponseType::OneTask(tn) => t.get_one_wft(tn).unwrap(),
|
663
|
+
ResponseType::AllHistory => t.get_full_history_info().unwrap(),
|
664
|
+
ResponseType::Raw(r) => {
|
665
|
+
return QueueResponse {
|
666
|
+
resp: r,
|
667
|
+
delay_until: None,
|
668
|
+
}
|
669
|
+
}
|
670
|
+
ResponseType::UntilResolved(fut, tn) => {
|
671
|
+
delay_until = Some(fut);
|
672
|
+
t.get_history_info(tn).unwrap()
|
673
|
+
}
|
674
|
+
ResponseType::UntilResolvedRaw(fut, r) => {
|
675
|
+
return QueueResponse {
|
676
|
+
resp: r,
|
677
|
+
delay_until: Some(fut),
|
678
|
+
}
|
679
|
+
}
|
680
|
+
};
|
681
|
+
let mut resp = hist_info.as_poll_wft_response(task_queue);
|
682
|
+
resp.workflow_execution = Some(wf);
|
683
|
+
QueueResponse { resp, delay_until }
|
684
|
+
}
|
685
|
+
|
686
|
+
type AsserterWithReply<'a> = (
|
687
|
+
&'a dyn Fn(&WorkflowActivation),
|
688
|
+
workflow_activation_completion::Status,
|
689
|
+
);
|
690
|
+
|
691
|
+
/// Determines when workflows are kept in the cache or evicted for [poll_and_reply] type tests
|
692
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
693
|
+
pub(crate) enum WorkflowCachingPolicy {
|
694
|
+
/// Workflows are evicted after each workflow task completion. Note that this is *not* after
|
695
|
+
/// each workflow activation - there are often multiple activations per workflow task.
|
696
|
+
NonSticky,
|
697
|
+
|
698
|
+
/// Not a real mode, but good for imitating crashes. Evict workflows after *every* reply,
|
699
|
+
/// even if there are pending activations
|
700
|
+
#[cfg(test)]
|
701
|
+
AfterEveryReply,
|
702
|
+
}
|
703
|
+
|
704
|
+
/// This function accepts a list of asserts and replies to workflow activations to run against the
|
705
|
+
/// provided instance of fake core.
|
706
|
+
///
|
707
|
+
/// It handles the business of re-sending the same activation replies over again in the event
|
708
|
+
/// of eviction or workflow activation failure. Activation failures specifically are only run once,
|
709
|
+
/// since they clearly can't be returned every time we replay the workflow, or it could never
|
710
|
+
/// proceed
|
711
|
+
pub(crate) async fn poll_and_reply<'a>(
|
712
|
+
worker: &'a Worker,
|
713
|
+
eviction_mode: WorkflowCachingPolicy,
|
714
|
+
expect_and_reply: &'a [AsserterWithReply<'a>],
|
715
|
+
) {
|
716
|
+
poll_and_reply_clears_outstanding_evicts(worker, None, eviction_mode, expect_and_reply).await;
|
717
|
+
}
|
718
|
+
|
719
|
+
pub(crate) async fn poll_and_reply_clears_outstanding_evicts<'a>(
|
720
|
+
worker: &'a Worker,
|
721
|
+
outstanding_map: Option<OutstandingWFTMap>,
|
722
|
+
eviction_mode: WorkflowCachingPolicy,
|
723
|
+
expect_and_reply: &'a [AsserterWithReply<'a>],
|
724
|
+
) {
|
725
|
+
let mut evictions = 0;
|
726
|
+
let expected_evictions = expect_and_reply.len() - 1;
|
727
|
+
let mut executed_failures = HashSet::new();
|
728
|
+
let expected_fail_count = expect_and_reply
|
729
|
+
.iter()
|
730
|
+
.filter(|(_, reply)| !reply.is_success())
|
731
|
+
.count();
|
732
|
+
|
733
|
+
'outer: loop {
|
734
|
+
let expect_iter = expect_and_reply.iter();
|
735
|
+
|
736
|
+
for (i, interaction) in expect_iter.enumerate() {
|
737
|
+
let (asserter, reply) = interaction;
|
738
|
+
let complete_is_failure = !reply.is_success();
|
739
|
+
// Only send activation failures once
|
740
|
+
if executed_failures.contains(&i) {
|
741
|
+
continue;
|
742
|
+
}
|
743
|
+
|
744
|
+
let mut res = worker.poll_workflow_activation().await.unwrap();
|
745
|
+
let contains_eviction = res.eviction_index();
|
746
|
+
|
747
|
+
let mut do_release = false;
|
748
|
+
if let Some(eviction_job_ix) = contains_eviction {
|
749
|
+
// If the job list has an eviction, make sure it was the last item in the list
|
750
|
+
// then remove it, since in the tests we don't explicitly specify evict assertions
|
751
|
+
assert_eq!(
|
752
|
+
eviction_job_ix,
|
753
|
+
res.jobs.len() - 1,
|
754
|
+
"Eviction job was not last job in job list"
|
755
|
+
);
|
756
|
+
res.jobs.remove(eviction_job_ix);
|
757
|
+
do_release = true;
|
758
|
+
}
|
759
|
+
|
760
|
+
// TODO: Can remove this if?
|
761
|
+
if !res.jobs.is_empty() {
|
762
|
+
asserter(&res);
|
763
|
+
}
|
764
|
+
|
765
|
+
let reply = if res.jobs.is_empty() {
|
766
|
+
// Just an eviction
|
767
|
+
WorkflowActivationCompletion::empty(res.run_id.clone())
|
768
|
+
} else {
|
769
|
+
// Eviction plus some work, we still want to issue the reply
|
770
|
+
WorkflowActivationCompletion {
|
771
|
+
run_id: res.run_id.clone(),
|
772
|
+
status: Some(reply.clone()),
|
773
|
+
}
|
774
|
+
};
|
775
|
+
|
776
|
+
let ends_execution = reply.has_execution_ending();
|
777
|
+
|
778
|
+
worker.complete_workflow_activation(reply).await.unwrap();
|
779
|
+
|
780
|
+
if do_release {
|
781
|
+
if let Some(omap) = outstanding_map.as_ref() {
|
782
|
+
omap.release_run(&res.run_id);
|
783
|
+
}
|
784
|
+
}
|
785
|
+
// Restart assertions from the beginning if it was an eviction (and workflow execution
|
786
|
+
// isn't over)
|
787
|
+
if contains_eviction.is_some() && !ends_execution {
|
788
|
+
continue 'outer;
|
789
|
+
}
|
790
|
+
|
791
|
+
if complete_is_failure {
|
792
|
+
executed_failures.insert(i);
|
793
|
+
}
|
794
|
+
|
795
|
+
match eviction_mode {
|
796
|
+
WorkflowCachingPolicy::NonSticky => (),
|
797
|
+
WorkflowCachingPolicy::AfterEveryReply => {
|
798
|
+
if evictions < expected_evictions {
|
799
|
+
worker.request_workflow_eviction(&res.run_id);
|
800
|
+
evictions += 1;
|
801
|
+
}
|
802
|
+
}
|
803
|
+
}
|
804
|
+
}
|
805
|
+
|
806
|
+
break;
|
807
|
+
}
|
808
|
+
|
809
|
+
assert_eq!(expected_fail_count, executed_failures.len());
|
810
|
+
assert_eq!(worker.outstanding_workflow_tasks().await, 0);
|
811
|
+
}
|
812
|
+
|
813
|
+
pub(crate) fn gen_assert_and_reply(
|
814
|
+
asserter: &dyn Fn(&WorkflowActivation),
|
815
|
+
reply_commands: Vec<workflow_command::Variant>,
|
816
|
+
) -> AsserterWithReply<'_> {
|
817
|
+
(
|
818
|
+
asserter,
|
819
|
+
workflow_completion::Success::from_variants(reply_commands).into(),
|
820
|
+
)
|
821
|
+
}
|
822
|
+
|
823
|
+
pub(crate) fn gen_assert_and_fail(asserter: &dyn Fn(&WorkflowActivation)) -> AsserterWithReply<'_> {
|
824
|
+
(
|
825
|
+
asserter,
|
826
|
+
workflow_completion::Failure {
|
827
|
+
failure: Some(Failure {
|
828
|
+
message: "Intentional test failure".to_string(),
|
829
|
+
..Default::default()
|
830
|
+
}),
|
831
|
+
}
|
832
|
+
.into(),
|
833
|
+
)
|
834
|
+
}
|
835
|
+
|
836
|
+
/// Generate asserts for [poll_and_reply] by passing patterns to match against the job list
|
837
|
+
#[macro_export]
|
838
|
+
macro_rules! job_assert {
|
839
|
+
($($pat:pat),+) => {
|
840
|
+
|res| {
|
841
|
+
assert_matches!(
|
842
|
+
res.jobs.as_slice(),
|
843
|
+
[$(WorkflowActivationJob {
|
844
|
+
variant: Some($pat),
|
845
|
+
}),+]
|
846
|
+
);
|
847
|
+
}
|
848
|
+
};
|
849
|
+
}
|
850
|
+
|
851
|
+
/// Forcibly drive a future a number of times, enforcing it is always returning Pending. This is
|
852
|
+
/// useful for ensuring some future has proceeded "enough" before racing it against another future.
|
853
|
+
#[macro_export]
|
854
|
+
macro_rules! advance_fut {
|
855
|
+
($fut:ident) => {
|
856
|
+
::futures::pin_mut!($fut);
|
857
|
+
{
|
858
|
+
let waker = ::futures::task::noop_waker();
|
859
|
+
let mut cx = core::task::Context::from_waker(&waker);
|
860
|
+
for _ in 0..10 {
|
861
|
+
assert_matches!($fut.poll_unpin(&mut cx), core::task::Poll::Pending);
|
862
|
+
::tokio::task::yield_now().await;
|
863
|
+
}
|
864
|
+
}
|
865
|
+
};
|
866
|
+
}
|
867
|
+
|
868
|
+
#[macro_export]
|
869
|
+
macro_rules! prost_dur {
|
870
|
+
($dur_call:ident $args:tt) => {
|
871
|
+
std::time::Duration::$dur_call$args
|
872
|
+
.try_into()
|
873
|
+
.expect("test duration fits")
|
874
|
+
};
|
875
|
+
}
|