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,1143 @@
|
|
1
|
+
//! This module and its submodules implement Core's logic for managing workflows (which is the
|
2
|
+
//! lion's share of the complexity in Core). See the `ARCHITECTURE.md` file in the repo root for
|
3
|
+
//! a diagram of the internals.
|
4
|
+
|
5
|
+
mod bridge;
|
6
|
+
mod driven_workflow;
|
7
|
+
mod history_update;
|
8
|
+
mod machines;
|
9
|
+
mod managed_run;
|
10
|
+
mod run_cache;
|
11
|
+
pub(crate) mod wft_poller;
|
12
|
+
mod workflow_stream;
|
13
|
+
|
14
|
+
pub(crate) use bridge::WorkflowBridge;
|
15
|
+
pub(crate) use driven_workflow::{DrivenWorkflow, WorkflowFetcher};
|
16
|
+
pub(crate) use history_update::{HistoryPaginator, HistoryUpdate};
|
17
|
+
pub(crate) use machines::WFMachinesError;
|
18
|
+
#[cfg(test)]
|
19
|
+
pub(crate) use managed_run::ManagedWFFunc;
|
20
|
+
|
21
|
+
use crate::{
|
22
|
+
abstractions::OwnedMeteredSemPermit,
|
23
|
+
protosext::{legacy_query_failure, ValidPollWFTQResponse, WorkflowActivationExt},
|
24
|
+
telemetry::VecDisplayer,
|
25
|
+
worker::{
|
26
|
+
activities::{ActivitiesFromWFTsHandle, PermittedTqResp},
|
27
|
+
client::WorkerClient,
|
28
|
+
workflow::{
|
29
|
+
managed_run::{ManagedRun, WorkflowManager},
|
30
|
+
wft_poller::validate_wft,
|
31
|
+
workflow_stream::{LocalInput, LocalInputs, WFStream},
|
32
|
+
},
|
33
|
+
LocalActRequest, LocalActivityResolution,
|
34
|
+
},
|
35
|
+
MetricsContext,
|
36
|
+
};
|
37
|
+
use futures::{stream::BoxStream, Stream, StreamExt};
|
38
|
+
use std::{
|
39
|
+
fmt::{Debug, Display, Formatter},
|
40
|
+
future::Future,
|
41
|
+
ops::DerefMut,
|
42
|
+
result,
|
43
|
+
sync::Arc,
|
44
|
+
time::{Duration, Instant},
|
45
|
+
};
|
46
|
+
use temporal_client::WorkflowTaskCompletion;
|
47
|
+
use temporal_sdk_core_api::errors::{CompleteWfError, PollWfError};
|
48
|
+
use temporal_sdk_core_protos::{
|
49
|
+
coresdk::{
|
50
|
+
workflow_activation::{
|
51
|
+
remove_from_cache::EvictionReason, QueryWorkflow, WorkflowActivation,
|
52
|
+
},
|
53
|
+
workflow_commands::*,
|
54
|
+
workflow_completion,
|
55
|
+
workflow_completion::{
|
56
|
+
workflow_activation_completion, Failure, WorkflowActivationCompletion,
|
57
|
+
},
|
58
|
+
},
|
59
|
+
temporal::api::{
|
60
|
+
command::v1::{command::Attributes, Command as ProtoCommand, Command},
|
61
|
+
common::v1::{Memo, RetryPolicy, SearchAttributes},
|
62
|
+
enums::v1::WorkflowTaskFailedCause,
|
63
|
+
taskqueue::v1::StickyExecutionAttributes,
|
64
|
+
workflowservice::v1::PollActivityTaskQueueResponse,
|
65
|
+
},
|
66
|
+
TaskToken,
|
67
|
+
};
|
68
|
+
use tokio::{
|
69
|
+
sync::{
|
70
|
+
mpsc::{unbounded_channel, UnboundedSender},
|
71
|
+
oneshot,
|
72
|
+
},
|
73
|
+
task,
|
74
|
+
task::{JoinError, JoinHandle},
|
75
|
+
};
|
76
|
+
use tokio_stream::wrappers::UnboundedReceiverStream;
|
77
|
+
use tokio_util::sync::CancellationToken;
|
78
|
+
use tracing::Span;
|
79
|
+
|
80
|
+
pub(crate) const LEGACY_QUERY_ID: &str = "legacy_query";
|
81
|
+
|
82
|
+
type Result<T, E = WFMachinesError> = result::Result<T, E>;
|
83
|
+
type BoxedActivationStream = BoxStream<'static, Result<ActivationOrAuto, PollWfError>>;
|
84
|
+
|
85
|
+
/// Centralizes all state related to workflows and workflow tasks
|
86
|
+
pub(crate) struct Workflows {
|
87
|
+
task_queue: String,
|
88
|
+
local_tx: UnboundedSender<LocalInput>,
|
89
|
+
processing_task: tokio::sync::Mutex<Option<JoinHandle<()>>>,
|
90
|
+
activation_stream: tokio::sync::Mutex<(
|
91
|
+
BoxedActivationStream,
|
92
|
+
// Used to indicate polling may begin
|
93
|
+
Option<oneshot::Sender<()>>,
|
94
|
+
)>,
|
95
|
+
client: Arc<dyn WorkerClient>,
|
96
|
+
/// Will be populated when this worker is using a cache and should complete WFTs with a sticky
|
97
|
+
/// queue.
|
98
|
+
sticky_attrs: Option<StickyExecutionAttributes>,
|
99
|
+
/// If set, can be used to reserve activity task slots for eager-return of new activity tasks.
|
100
|
+
activity_tasks_handle: Option<ActivitiesFromWFTsHandle>,
|
101
|
+
}
|
102
|
+
|
103
|
+
pub(super) struct WorkflowBasics {
|
104
|
+
pub max_cached_workflows: usize,
|
105
|
+
pub max_outstanding_wfts: usize,
|
106
|
+
pub shutdown_token: CancellationToken,
|
107
|
+
pub metrics: MetricsContext,
|
108
|
+
pub namespace: String,
|
109
|
+
pub task_queue: String,
|
110
|
+
}
|
111
|
+
|
112
|
+
impl Workflows {
|
113
|
+
pub(super) fn new(
|
114
|
+
basics: WorkflowBasics,
|
115
|
+
sticky_attrs: Option<StickyExecutionAttributes>,
|
116
|
+
client: Arc<dyn WorkerClient>,
|
117
|
+
wft_stream: impl Stream<Item = Result<ValidPollWFTQResponse, tonic::Status>> + Send + 'static,
|
118
|
+
local_activity_request_sink: impl Fn(Vec<LocalActRequest>) -> Vec<LocalActivityResolution>
|
119
|
+
+ Send
|
120
|
+
+ Sync
|
121
|
+
+ 'static,
|
122
|
+
activity_tasks_handle: Option<ActivitiesFromWFTsHandle>,
|
123
|
+
) -> Self {
|
124
|
+
let (local_tx, local_rx) = unbounded_channel();
|
125
|
+
let shutdown_tok = basics.shutdown_token.clone();
|
126
|
+
let task_queue = basics.task_queue.clone();
|
127
|
+
let mut stream = WFStream::build(
|
128
|
+
basics,
|
129
|
+
wft_stream,
|
130
|
+
UnboundedReceiverStream::new(local_rx),
|
131
|
+
client.clone(),
|
132
|
+
local_activity_request_sink,
|
133
|
+
);
|
134
|
+
let (activation_tx, activation_rx) = unbounded_channel();
|
135
|
+
let (start_polling_tx, start_polling_rx) = oneshot::channel();
|
136
|
+
// We must spawn a task to constantly poll the activation stream, because otherwise
|
137
|
+
// activation completions would not cause anything to happen until the next poll.
|
138
|
+
let processing_task = task::spawn(async move {
|
139
|
+
// However, we want to avoid plowing ahead until we've been asked to poll at least once.
|
140
|
+
// This supports activity-only workers.
|
141
|
+
let do_poll = tokio::select! {
|
142
|
+
sp = start_polling_rx => {
|
143
|
+
sp.is_ok()
|
144
|
+
}
|
145
|
+
_ = shutdown_tok.cancelled() => {
|
146
|
+
false
|
147
|
+
}
|
148
|
+
};
|
149
|
+
if !do_poll {
|
150
|
+
return;
|
151
|
+
}
|
152
|
+
while let Some(act) = stream.next().await {
|
153
|
+
activation_tx
|
154
|
+
.send(act)
|
155
|
+
.expect("Activation processor channel not dropped");
|
156
|
+
}
|
157
|
+
});
|
158
|
+
Self {
|
159
|
+
task_queue,
|
160
|
+
local_tx,
|
161
|
+
processing_task: tokio::sync::Mutex::new(Some(processing_task)),
|
162
|
+
activation_stream: tokio::sync::Mutex::new((
|
163
|
+
UnboundedReceiverStream::new(activation_rx).boxed(),
|
164
|
+
Some(start_polling_tx),
|
165
|
+
)),
|
166
|
+
client,
|
167
|
+
sticky_attrs,
|
168
|
+
activity_tasks_handle,
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
pub async fn next_workflow_activation(&self) -> Result<WorkflowActivation, PollWfError> {
|
173
|
+
loop {
|
174
|
+
let r = {
|
175
|
+
let mut lock = self.activation_stream.lock().await;
|
176
|
+
let (ref mut stream, ref mut beginner) = lock.deref_mut();
|
177
|
+
if let Some(beginner) = beginner.take() {
|
178
|
+
let _ = beginner.send(());
|
179
|
+
}
|
180
|
+
stream.next().await.unwrap_or(Err(PollWfError::ShutDown))?
|
181
|
+
};
|
182
|
+
Span::current().record("run_id", &r.run_id());
|
183
|
+
match r {
|
184
|
+
ActivationOrAuto::LangActivation(act) | ActivationOrAuto::ReadyForQueries(act) => {
|
185
|
+
debug!(activation=%act, "Sending activation to lang");
|
186
|
+
break Ok(act);
|
187
|
+
}
|
188
|
+
ActivationOrAuto::Autocomplete { run_id } => {
|
189
|
+
self.activation_completed(WorkflowActivationCompletion {
|
190
|
+
run_id,
|
191
|
+
status: Some(workflow_completion::Success::from_variants(vec![]).into()),
|
192
|
+
})
|
193
|
+
.await?;
|
194
|
+
}
|
195
|
+
}
|
196
|
+
}
|
197
|
+
}
|
198
|
+
|
199
|
+
/// Queue an activation completion for processing, returning a future that will resolve with
|
200
|
+
/// the outcome of that completion. See [ActivationCompletedOutcome].
|
201
|
+
///
|
202
|
+
/// Returns the most-recently-processed event number for the run
|
203
|
+
pub async fn activation_completed(
|
204
|
+
&self,
|
205
|
+
completion: WorkflowActivationCompletion,
|
206
|
+
) -> Result<usize, CompleteWfError> {
|
207
|
+
let is_empty_completion = completion.is_empty();
|
208
|
+
let completion = validate_completion(completion)?;
|
209
|
+
let run_id = completion.run_id().to_string();
|
210
|
+
let (tx, rx) = oneshot::channel();
|
211
|
+
let was_sent = self.send_local(WFActCompleteMsg {
|
212
|
+
completion,
|
213
|
+
response_tx: tx,
|
214
|
+
});
|
215
|
+
if !was_sent {
|
216
|
+
if is_empty_completion {
|
217
|
+
// Empty complete which is likely an evict reply, we can just ignore.
|
218
|
+
return Ok(0);
|
219
|
+
}
|
220
|
+
panic!(
|
221
|
+
"A non-empty completion was not processed. Workflow processing may have \
|
222
|
+
terminated unexpectedly. This is a bug."
|
223
|
+
);
|
224
|
+
}
|
225
|
+
|
226
|
+
let completion_outcome = rx
|
227
|
+
.await
|
228
|
+
.expect("Send half of activation complete response not dropped");
|
229
|
+
let mut wft_from_complete = None;
|
230
|
+
let reported_wft_to_server = match completion_outcome.outcome {
|
231
|
+
ActivationCompleteOutcome::ReportWFTSuccess(report) => match report {
|
232
|
+
ServerCommandsWithWorkflowInfo {
|
233
|
+
task_token,
|
234
|
+
action:
|
235
|
+
ActivationAction::WftComplete {
|
236
|
+
mut commands,
|
237
|
+
query_responses,
|
238
|
+
force_new_wft,
|
239
|
+
},
|
240
|
+
} => {
|
241
|
+
let reserved_act_permits =
|
242
|
+
self.reserve_activity_slots_for_outgoing_commands(commands.as_mut_slice());
|
243
|
+
debug!(commands=%commands.display(), query_responses=%query_responses.display(),
|
244
|
+
force_new_wft, "Sending responses to server");
|
245
|
+
let mut completion = WorkflowTaskCompletion {
|
246
|
+
task_token,
|
247
|
+
commands,
|
248
|
+
query_responses,
|
249
|
+
sticky_attributes: None,
|
250
|
+
return_new_workflow_task: true,
|
251
|
+
force_create_new_workflow_task: force_new_wft,
|
252
|
+
};
|
253
|
+
let sticky_attrs = self.sticky_attrs.clone();
|
254
|
+
// Do not return new WFT if we would not cache, because returned new WFTs are
|
255
|
+
// always partial.
|
256
|
+
if sticky_attrs.is_none() {
|
257
|
+
completion.return_new_workflow_task = false;
|
258
|
+
}
|
259
|
+
completion.sticky_attributes = sticky_attrs;
|
260
|
+
|
261
|
+
self.handle_wft_reporting_errs(&run_id, || async {
|
262
|
+
let maybe_wft = self.client.complete_workflow_task(completion).await?;
|
263
|
+
if let Some(wft) = maybe_wft.workflow_task {
|
264
|
+
wft_from_complete = Some(validate_wft(wft)?);
|
265
|
+
}
|
266
|
+
self.handle_eager_activities(
|
267
|
+
reserved_act_permits,
|
268
|
+
maybe_wft.activity_tasks,
|
269
|
+
);
|
270
|
+
Ok(())
|
271
|
+
})
|
272
|
+
.await;
|
273
|
+
true
|
274
|
+
}
|
275
|
+
ServerCommandsWithWorkflowInfo {
|
276
|
+
task_token,
|
277
|
+
action: ActivationAction::RespondLegacyQuery { result },
|
278
|
+
} => {
|
279
|
+
self.respond_legacy_query(task_token, *result).await;
|
280
|
+
true
|
281
|
+
}
|
282
|
+
},
|
283
|
+
ActivationCompleteOutcome::ReportWFTFail(outcome) => match outcome {
|
284
|
+
FailedActivationWFTReport::Report(tt, cause, failure) => {
|
285
|
+
warn!(run_id=%run_id, failure=?failure, "Failing workflow task");
|
286
|
+
self.handle_wft_reporting_errs(&run_id, || async {
|
287
|
+
self.client
|
288
|
+
.fail_workflow_task(tt, cause, failure.failure.map(Into::into))
|
289
|
+
.await
|
290
|
+
})
|
291
|
+
.await;
|
292
|
+
true
|
293
|
+
}
|
294
|
+
FailedActivationWFTReport::ReportLegacyQueryFailure(task_token, failure) => {
|
295
|
+
warn!(run_id=%run_id, failure=?failure, "Failing legacy query request");
|
296
|
+
self.respond_legacy_query(task_token, legacy_query_failure(failure))
|
297
|
+
.await;
|
298
|
+
true
|
299
|
+
}
|
300
|
+
},
|
301
|
+
ActivationCompleteOutcome::DoNothing => false,
|
302
|
+
};
|
303
|
+
|
304
|
+
self.post_activation(PostActivationMsg {
|
305
|
+
run_id,
|
306
|
+
reported_wft_to_server,
|
307
|
+
wft_from_complete,
|
308
|
+
});
|
309
|
+
|
310
|
+
Ok(completion_outcome.most_recently_processed_event)
|
311
|
+
}
|
312
|
+
|
313
|
+
/// Tell workflow that a local activity has finished with the provided result
|
314
|
+
pub fn notify_of_local_result(&self, run_id: impl Into<String>, resolved: LocalResolution) {
|
315
|
+
self.send_local(LocalResolutionMsg {
|
316
|
+
run_id: run_id.into(),
|
317
|
+
res: resolved,
|
318
|
+
});
|
319
|
+
}
|
320
|
+
|
321
|
+
/// Request eviction of a workflow
|
322
|
+
pub fn request_eviction(
|
323
|
+
&self,
|
324
|
+
run_id: impl Into<String>,
|
325
|
+
message: impl Into<String>,
|
326
|
+
reason: EvictionReason,
|
327
|
+
) {
|
328
|
+
self.send_local(RequestEvictMsg {
|
329
|
+
run_id: run_id.into(),
|
330
|
+
message: message.into(),
|
331
|
+
reason,
|
332
|
+
});
|
333
|
+
}
|
334
|
+
|
335
|
+
/// Query the state of workflow management. Can return `None` if workflow state is shut down.
|
336
|
+
pub fn get_state_info(&self) -> impl Future<Output = Option<WorkflowStateInfo>> {
|
337
|
+
let (tx, rx) = oneshot::channel();
|
338
|
+
self.send_local(GetStateInfoMsg { response_tx: tx });
|
339
|
+
async move { rx.await.ok() }
|
340
|
+
}
|
341
|
+
|
342
|
+
pub async fn shutdown(&self) -> Result<(), JoinError> {
|
343
|
+
let maybe_jh = self.processing_task.lock().await.take();
|
344
|
+
if let Some(jh) = maybe_jh {
|
345
|
+
// This acts as a final wake up in case the stream is still alive and wouldn't otherwise
|
346
|
+
// receive another message. It allows it to shut itself down.
|
347
|
+
let _ = self.get_state_info();
|
348
|
+
jh.await
|
349
|
+
} else {
|
350
|
+
Ok(())
|
351
|
+
}
|
352
|
+
}
|
353
|
+
|
354
|
+
/// Must be called after every activation completion has finished
|
355
|
+
fn post_activation(&self, msg: PostActivationMsg) {
|
356
|
+
self.send_local(msg);
|
357
|
+
}
|
358
|
+
|
359
|
+
/// Handle server errors from either completing or failing a workflow task. Un-handleable errors
|
360
|
+
/// trigger a workflow eviction and are logged.
|
361
|
+
async fn handle_wft_reporting_errs<T, Fut>(&self, run_id: &str, completer: impl FnOnce() -> Fut)
|
362
|
+
where
|
363
|
+
Fut: Future<Output = Result<T, tonic::Status>>,
|
364
|
+
{
|
365
|
+
let mut should_evict = None;
|
366
|
+
if let Err(err) = completer().await {
|
367
|
+
match err.code() {
|
368
|
+
// Silence unhandled command errors since the lang SDK cannot do anything
|
369
|
+
// about them besides poll again, which it will do anyway.
|
370
|
+
tonic::Code::InvalidArgument if err.message() == "UnhandledCommand" => {
|
371
|
+
debug!(error = %err, run_id, "Unhandled command response when completing");
|
372
|
+
should_evict = Some(EvictionReason::UnhandledCommand);
|
373
|
+
}
|
374
|
+
tonic::Code::NotFound => {
|
375
|
+
warn!(error = %err, run_id, "Task not found when completing");
|
376
|
+
should_evict = Some(EvictionReason::TaskNotFound);
|
377
|
+
}
|
378
|
+
_ => {
|
379
|
+
warn!(error= %err, "Network error while completing workflow activation");
|
380
|
+
should_evict = Some(EvictionReason::Fatal);
|
381
|
+
}
|
382
|
+
}
|
383
|
+
}
|
384
|
+
if let Some(reason) = should_evict {
|
385
|
+
self.request_eviction(run_id, "Error reporting WFT to server", reason);
|
386
|
+
}
|
387
|
+
}
|
388
|
+
|
389
|
+
/// Sends a message to the workflow processing stream. Returns true if the message was sent
|
390
|
+
/// successfully.
|
391
|
+
fn send_local(&self, msg: impl Into<LocalInputs>) -> bool {
|
392
|
+
let msg = msg.into();
|
393
|
+
let print_err = !matches!(msg, LocalInputs::GetStateInfo(_));
|
394
|
+
if let Err(e) = self.local_tx.send(LocalInput {
|
395
|
+
input: msg,
|
396
|
+
span: Span::current(),
|
397
|
+
}) {
|
398
|
+
if print_err {
|
399
|
+
warn!(
|
400
|
+
"Tried to interact with workflow state after it shut down. This may be benign \
|
401
|
+
when processing evictions during shutdown. When sending {:?}",
|
402
|
+
e.0.input
|
403
|
+
)
|
404
|
+
}
|
405
|
+
false
|
406
|
+
} else {
|
407
|
+
true
|
408
|
+
}
|
409
|
+
}
|
410
|
+
|
411
|
+
/// Process eagerly returned activities from WFT completion
|
412
|
+
fn handle_eager_activities(
|
413
|
+
&self,
|
414
|
+
reserved_act_permits: Vec<OwnedMeteredSemPermit>,
|
415
|
+
eager_acts: Vec<PollActivityTaskQueueResponse>,
|
416
|
+
) {
|
417
|
+
if let Some(at_handle) = self.activity_tasks_handle.as_ref() {
|
418
|
+
let excess_reserved = reserved_act_permits.len().saturating_sub(eager_acts.len());
|
419
|
+
if excess_reserved > 0 {
|
420
|
+
debug!(
|
421
|
+
"Server returned {excess_reserved} fewer activities for \
|
422
|
+
eager execution than we requested"
|
423
|
+
);
|
424
|
+
} else if eager_acts.len() > reserved_act_permits.len() {
|
425
|
+
// If we somehow got more activities from server than we asked for, server did
|
426
|
+
// something wrong.
|
427
|
+
error!(
|
428
|
+
"Server sent more activities for eager execution than we requested! They will \
|
429
|
+
be dropped and eventually time out. Please report this, as it is a server bug."
|
430
|
+
)
|
431
|
+
}
|
432
|
+
let with_permits = reserved_act_permits
|
433
|
+
.into_iter()
|
434
|
+
.zip(eager_acts.into_iter())
|
435
|
+
.map(|(permit, resp)| PermittedTqResp { permit, resp });
|
436
|
+
if with_permits.len() > 0 {
|
437
|
+
debug!(
|
438
|
+
"Adding {} activity tasks received from WFT complete",
|
439
|
+
with_permits.len()
|
440
|
+
);
|
441
|
+
at_handle.add_tasks(with_permits);
|
442
|
+
}
|
443
|
+
} else if !eager_acts.is_empty() {
|
444
|
+
panic!(
|
445
|
+
"Requested eager activity execution but this worker has no activity task \
|
446
|
+
manager! This is an internal bug, Core should not have asked for tasks."
|
447
|
+
)
|
448
|
+
}
|
449
|
+
}
|
450
|
+
|
451
|
+
/// Attempt to reserve activity slots for activities we could eagerly execute on
|
452
|
+
/// this worker.
|
453
|
+
///
|
454
|
+
/// Returns the number of activity slots that were reserved
|
455
|
+
fn reserve_activity_slots_for_outgoing_commands(
|
456
|
+
&self,
|
457
|
+
commands: &mut [Command],
|
458
|
+
) -> Vec<OwnedMeteredSemPermit> {
|
459
|
+
let mut reserved = vec![];
|
460
|
+
if let Some(at_handle) = self.activity_tasks_handle.as_ref() {
|
461
|
+
for cmd in commands {
|
462
|
+
if let Some(Attributes::ScheduleActivityTaskCommandAttributes(attrs)) =
|
463
|
+
cmd.attributes.as_mut()
|
464
|
+
{
|
465
|
+
// If request_eager_execution was already false, that means lang explicitly
|
466
|
+
// told us it didn't want to eagerly execute for some reason. So, we only
|
467
|
+
// ever turn *off* eager execution if a slot is not available or the activity
|
468
|
+
// is scheduled on a different task queue.
|
469
|
+
if attrs.request_eager_execution {
|
470
|
+
let same_task_queue = attrs
|
471
|
+
.task_queue
|
472
|
+
.as_ref()
|
473
|
+
.map(|q| q.name == self.task_queue)
|
474
|
+
.unwrap_or_default();
|
475
|
+
if same_task_queue {
|
476
|
+
if let Some(p) = at_handle.reserve_slot() {
|
477
|
+
reserved.push(p);
|
478
|
+
} else {
|
479
|
+
attrs.request_eager_execution = false;
|
480
|
+
}
|
481
|
+
} else {
|
482
|
+
attrs.request_eager_execution = false;
|
483
|
+
}
|
484
|
+
}
|
485
|
+
}
|
486
|
+
}
|
487
|
+
}
|
488
|
+
reserved
|
489
|
+
}
|
490
|
+
|
491
|
+
/// Wraps responding to legacy queries. Handles ignore-able failures.
|
492
|
+
async fn respond_legacy_query(&self, tt: TaskToken, res: QueryResult) {
|
493
|
+
match self.client.respond_legacy_query(tt, res).await {
|
494
|
+
Ok(_) => {}
|
495
|
+
Err(e) if e.code() == tonic::Code::NotFound => {
|
496
|
+
warn!(error=?e, "Query not found when attempting to respond to it");
|
497
|
+
}
|
498
|
+
Err(e) => {
|
499
|
+
warn!(error= %e, "Network error while responding to legacy query");
|
500
|
+
}
|
501
|
+
}
|
502
|
+
}
|
503
|
+
}
|
504
|
+
|
505
|
+
/// Manages access to a specific workflow run, and contains various bookkeeping information that the
|
506
|
+
/// [WFStream] may need to access quickly.
|
507
|
+
#[derive(derive_more::DebugCustom)]
|
508
|
+
#[debug(
|
509
|
+
fmt = "ManagedRunHandle {{ wft: {:?}, activation: {:?}, buffered_resp: {:?} \
|
510
|
+
have_seen_terminal_event: {}, most_recently_processed_event: {}, more_pending_work: {}, \
|
511
|
+
trying_to_evict: {}, last_action_acked: {} }}",
|
512
|
+
wft,
|
513
|
+
activation,
|
514
|
+
buffered_resp,
|
515
|
+
have_seen_terminal_event,
|
516
|
+
most_recently_processed_event_number,
|
517
|
+
more_pending_work,
|
518
|
+
"trying_to_evict.is_some()",
|
519
|
+
last_action_acked
|
520
|
+
)]
|
521
|
+
struct ManagedRunHandle {
|
522
|
+
/// If set, the WFT this run is currently/will be processing.
|
523
|
+
wft: Option<OutstandingTask>,
|
524
|
+
/// An outstanding activation to lang
|
525
|
+
activation: Option<OutstandingActivation>,
|
526
|
+
/// If set, it indicates there is a buffered poll response from the server that applies to this
|
527
|
+
/// run. This can happen when lang takes too long to complete a task and the task times out, for
|
528
|
+
/// example. Upon next completion, the buffered response will be removed and can be made ready
|
529
|
+
/// to be returned from polling
|
530
|
+
buffered_resp: Option<PermittedWFT>,
|
531
|
+
/// True if this machine has seen an event which ends the execution
|
532
|
+
have_seen_terminal_event: bool,
|
533
|
+
/// The most recently processed event id this machine has seen. 0 means it has seen nothing.
|
534
|
+
most_recently_processed_event_number: usize,
|
535
|
+
/// Is set true when the machines indicate that there is additional known work to be processed
|
536
|
+
more_pending_work: bool,
|
537
|
+
/// Is set if an eviction has been requested for this run
|
538
|
+
trying_to_evict: Option<RequestEvictMsg>,
|
539
|
+
/// Set to true if the last action we tried to take to this run has been processed (ie: the
|
540
|
+
/// [RunUpdateResponse] for it has been seen.
|
541
|
+
last_action_acked: bool,
|
542
|
+
/// For sending work to the machines
|
543
|
+
run_actions_tx: UnboundedSender<RunAction>,
|
544
|
+
/// Handle to the task where the actual machines live
|
545
|
+
handle: JoinHandle<()>,
|
546
|
+
metrics: MetricsContext,
|
547
|
+
}
|
548
|
+
impl ManagedRunHandle {
|
549
|
+
fn new(
|
550
|
+
wfm: WorkflowManager,
|
551
|
+
activations_tx: UnboundedSender<RunUpdateResponse>,
|
552
|
+
local_activity_request_sink: LocalActivityRequestSink,
|
553
|
+
metrics: MetricsContext,
|
554
|
+
) -> Self {
|
555
|
+
let (run_actions_tx, run_actions_rx) = unbounded_channel();
|
556
|
+
let managed = ManagedRun::new(wfm, activations_tx, local_activity_request_sink);
|
557
|
+
let handle = tokio::task::spawn(managed.run(run_actions_rx));
|
558
|
+
Self {
|
559
|
+
wft: None,
|
560
|
+
activation: None,
|
561
|
+
buffered_resp: None,
|
562
|
+
have_seen_terminal_event: false,
|
563
|
+
most_recently_processed_event_number: 0,
|
564
|
+
more_pending_work: false,
|
565
|
+
trying_to_evict: None,
|
566
|
+
last_action_acked: true,
|
567
|
+
handle,
|
568
|
+
metrics,
|
569
|
+
run_actions_tx,
|
570
|
+
}
|
571
|
+
}
|
572
|
+
|
573
|
+
fn incoming_wft(&mut self, wft: NewIncomingWFT) {
|
574
|
+
if self.wft.is_some() {
|
575
|
+
error!("Trying to send a new WFT for a run which already has one!");
|
576
|
+
}
|
577
|
+
self.send_run_action(RunActions::NewIncomingWFT(wft));
|
578
|
+
}
|
579
|
+
fn check_more_activations(&mut self) {
|
580
|
+
// No point in checking for more activations if we have not acked the last update, or
|
581
|
+
// if there's already an outstanding activation.
|
582
|
+
if self.last_action_acked && self.activation.is_none() {
|
583
|
+
self.send_run_action(RunActions::CheckMoreWork {
|
584
|
+
want_to_evict: self.trying_to_evict.clone(),
|
585
|
+
has_pending_queries: self
|
586
|
+
.wft
|
587
|
+
.as_ref()
|
588
|
+
.map(|wft| !wft.pending_queries.is_empty())
|
589
|
+
.unwrap_or_default(),
|
590
|
+
has_wft: self.wft.is_some(),
|
591
|
+
});
|
592
|
+
}
|
593
|
+
}
|
594
|
+
fn send_completion(&mut self, c: RunActivationCompletion) {
|
595
|
+
self.send_run_action(RunActions::ActivationCompletion(c));
|
596
|
+
}
|
597
|
+
fn send_local_resolution(&mut self, r: LocalResolution) {
|
598
|
+
self.send_run_action(RunActions::LocalResolution(r));
|
599
|
+
}
|
600
|
+
|
601
|
+
fn insert_outstanding_activation(&mut self, act: &ActivationOrAuto) {
|
602
|
+
let act_type = match &act {
|
603
|
+
ActivationOrAuto::LangActivation(act) | ActivationOrAuto::ReadyForQueries(act) => {
|
604
|
+
if act.is_legacy_query() {
|
605
|
+
OutstandingActivation::LegacyQuery
|
606
|
+
} else {
|
607
|
+
OutstandingActivation::Normal {
|
608
|
+
contains_eviction: act.eviction_index().is_some(),
|
609
|
+
num_jobs: act.jobs.len(),
|
610
|
+
}
|
611
|
+
}
|
612
|
+
}
|
613
|
+
ActivationOrAuto::Autocomplete { .. } => OutstandingActivation::Autocomplete,
|
614
|
+
};
|
615
|
+
if let Some(old_act) = self.activation {
|
616
|
+
// This is a panic because we have screwed up core logic if this is violated. It must be
|
617
|
+
// upheld.
|
618
|
+
panic!(
|
619
|
+
"Attempted to insert a new outstanding activation {:?}, but there already was \
|
620
|
+
one outstanding: {:?}",
|
621
|
+
act, old_act
|
622
|
+
);
|
623
|
+
}
|
624
|
+
self.activation = Some(act_type);
|
625
|
+
}
|
626
|
+
|
627
|
+
fn send_run_action(&mut self, action: RunActions) {
|
628
|
+
self.last_action_acked = false;
|
629
|
+
self.run_actions_tx
|
630
|
+
.send(RunAction {
|
631
|
+
action,
|
632
|
+
trace_span: Span::current(),
|
633
|
+
})
|
634
|
+
.expect("Receive half of run actions not dropped");
|
635
|
+
}
|
636
|
+
|
637
|
+
/// Returns true if the managed run has any form of pending work
|
638
|
+
/// If `ignore_evicts` is true, pending evictions do not count as pending work.
|
639
|
+
/// If `ignore_buffered` is true, buffered workflow tasks do not count as pending work.
|
640
|
+
fn has_any_pending_work(&self, ignore_evicts: bool, ignore_buffered: bool) -> bool {
|
641
|
+
let evict_work = if ignore_evicts {
|
642
|
+
false
|
643
|
+
} else {
|
644
|
+
self.trying_to_evict.is_some()
|
645
|
+
};
|
646
|
+
let act_work = if ignore_evicts {
|
647
|
+
if let Some(ref act) = self.activation {
|
648
|
+
!act.has_only_eviction()
|
649
|
+
} else {
|
650
|
+
false
|
651
|
+
}
|
652
|
+
} else {
|
653
|
+
self.activation.is_some()
|
654
|
+
};
|
655
|
+
let buffered = if ignore_buffered {
|
656
|
+
false
|
657
|
+
} else {
|
658
|
+
self.buffered_resp.is_some()
|
659
|
+
};
|
660
|
+
self.wft.is_some()
|
661
|
+
|| buffered
|
662
|
+
|| !self.last_action_acked
|
663
|
+
|| self.more_pending_work
|
664
|
+
|| act_work
|
665
|
+
|| evict_work
|
666
|
+
}
|
667
|
+
|
668
|
+
/// Returns true if the handle is currently processing a WFT which contains a legacy query.
|
669
|
+
fn pending_work_is_legacy_query(&self) -> bool {
|
670
|
+
// Either we know because there is a pending legacy query, or it's already been drained and
|
671
|
+
// sent as an activation.
|
672
|
+
matches!(self.activation, Some(OutstandingActivation::LegacyQuery))
|
673
|
+
|| self
|
674
|
+
.wft
|
675
|
+
.as_ref()
|
676
|
+
.map(|t| t.has_pending_legacy_query())
|
677
|
+
.unwrap_or_default()
|
678
|
+
}
|
679
|
+
}
|
680
|
+
|
681
|
+
#[derive(Debug, derive_more::Display)]
|
682
|
+
enum ActivationOrAuto {
|
683
|
+
LangActivation(WorkflowActivation),
|
684
|
+
/// This type should only be filled with an empty activation which is ready to have queries
|
685
|
+
/// inserted into the joblist
|
686
|
+
ReadyForQueries(WorkflowActivation),
|
687
|
+
Autocomplete {
|
688
|
+
run_id: String,
|
689
|
+
},
|
690
|
+
}
|
691
|
+
impl ActivationOrAuto {
|
692
|
+
pub fn run_id(&self) -> &str {
|
693
|
+
match self {
|
694
|
+
ActivationOrAuto::LangActivation(act) => &act.run_id,
|
695
|
+
ActivationOrAuto::Autocomplete { run_id, .. } => run_id,
|
696
|
+
ActivationOrAuto::ReadyForQueries(act) => &act.run_id,
|
697
|
+
}
|
698
|
+
}
|
699
|
+
}
|
700
|
+
|
701
|
+
#[derive(derive_more::DebugCustom)]
|
702
|
+
#[debug(fmt = "PermittedWft {{ {:?} }}", wft)]
|
703
|
+
pub(crate) struct PermittedWFT {
|
704
|
+
wft: ValidPollWFTQResponse,
|
705
|
+
permit: OwnedMeteredSemPermit,
|
706
|
+
}
|
707
|
+
|
708
|
+
#[derive(Debug)]
|
709
|
+
pub(crate) struct OutstandingTask {
|
710
|
+
pub info: WorkflowTaskInfo,
|
711
|
+
pub hit_cache: bool,
|
712
|
+
/// Set if the outstanding task has quer(ies) which must be fulfilled upon finishing replay
|
713
|
+
pub pending_queries: Vec<QueryWorkflow>,
|
714
|
+
pub start_time: Instant,
|
715
|
+
/// The WFT permit owned by this task, ensures we don't exceed max concurrent WFT, and makes
|
716
|
+
/// sure the permit is automatically freed when we delete the task.
|
717
|
+
pub permit: OwnedMeteredSemPermit,
|
718
|
+
}
|
719
|
+
|
720
|
+
impl OutstandingTask {
|
721
|
+
pub fn has_pending_legacy_query(&self) -> bool {
|
722
|
+
self.pending_queries
|
723
|
+
.iter()
|
724
|
+
.any(|q| q.query_id == LEGACY_QUERY_ID)
|
725
|
+
}
|
726
|
+
}
|
727
|
+
|
728
|
+
#[derive(Copy, Clone, Debug)]
|
729
|
+
pub(crate) enum OutstandingActivation {
|
730
|
+
/// A normal activation with a joblist
|
731
|
+
Normal {
|
732
|
+
/// True if there is an eviction in the joblist
|
733
|
+
contains_eviction: bool,
|
734
|
+
/// Number of jobs in the activation
|
735
|
+
num_jobs: usize,
|
736
|
+
},
|
737
|
+
/// An activation for a legacy query
|
738
|
+
LegacyQuery,
|
739
|
+
/// A fake activation which is never sent to lang, but used internally
|
740
|
+
Autocomplete,
|
741
|
+
}
|
742
|
+
|
743
|
+
impl OutstandingActivation {
|
744
|
+
pub(crate) const fn has_only_eviction(self) -> bool {
|
745
|
+
matches!(
|
746
|
+
self,
|
747
|
+
OutstandingActivation::Normal {
|
748
|
+
contains_eviction: true,
|
749
|
+
num_jobs: nj
|
750
|
+
}
|
751
|
+
if nj == 1)
|
752
|
+
}
|
753
|
+
pub(crate) const fn has_eviction(self) -> bool {
|
754
|
+
matches!(
|
755
|
+
self,
|
756
|
+
OutstandingActivation::Normal {
|
757
|
+
contains_eviction: true,
|
758
|
+
..
|
759
|
+
}
|
760
|
+
)
|
761
|
+
}
|
762
|
+
}
|
763
|
+
|
764
|
+
/// Contains important information about a given workflow task that we need to memorize while
|
765
|
+
/// lang handles it.
|
766
|
+
#[derive(Clone, Debug)]
|
767
|
+
pub struct WorkflowTaskInfo {
|
768
|
+
pub task_token: TaskToken,
|
769
|
+
pub attempt: u32,
|
770
|
+
}
|
771
|
+
|
772
|
+
#[derive(Debug)]
|
773
|
+
pub enum FailedActivationWFTReport {
|
774
|
+
Report(TaskToken, WorkflowTaskFailedCause, Failure),
|
775
|
+
ReportLegacyQueryFailure(TaskToken, Failure),
|
776
|
+
}
|
777
|
+
|
778
|
+
#[derive(Debug)]
|
779
|
+
pub(crate) struct ServerCommandsWithWorkflowInfo {
|
780
|
+
pub task_token: TaskToken,
|
781
|
+
pub action: ActivationAction,
|
782
|
+
}
|
783
|
+
|
784
|
+
#[derive(Debug)]
|
785
|
+
pub(crate) enum ActivationAction {
|
786
|
+
/// We should respond that the workflow task is complete
|
787
|
+
WftComplete {
|
788
|
+
commands: Vec<ProtoCommand>,
|
789
|
+
query_responses: Vec<QueryResult>,
|
790
|
+
force_new_wft: bool,
|
791
|
+
},
|
792
|
+
/// We should respond to a legacy query request
|
793
|
+
RespondLegacyQuery { result: Box<QueryResult> },
|
794
|
+
}
|
795
|
+
|
796
|
+
#[derive(Debug, Eq, PartialEq, Hash)]
|
797
|
+
pub(crate) enum EvictionRequestResult {
|
798
|
+
EvictionRequested(Option<u32>),
|
799
|
+
NotFound,
|
800
|
+
EvictionAlreadyRequested(Option<u32>),
|
801
|
+
}
|
802
|
+
|
803
|
+
#[derive(Debug)]
|
804
|
+
#[allow(dead_code)] // Not always used in non-test
|
805
|
+
pub(crate) struct WorkflowStateInfo {
|
806
|
+
pub cached_workflows: usize,
|
807
|
+
pub outstanding_wft: usize,
|
808
|
+
pub available_wft_permits: usize,
|
809
|
+
}
|
810
|
+
|
811
|
+
#[derive(Debug)]
|
812
|
+
struct WFActCompleteMsg {
|
813
|
+
completion: ValidatedCompletion,
|
814
|
+
response_tx: oneshot::Sender<ActivationCompleteResult>,
|
815
|
+
}
|
816
|
+
#[derive(Debug)]
|
817
|
+
struct LocalResolutionMsg {
|
818
|
+
run_id: String,
|
819
|
+
res: LocalResolution,
|
820
|
+
}
|
821
|
+
#[derive(Debug)]
|
822
|
+
struct PostActivationMsg {
|
823
|
+
run_id: String,
|
824
|
+
reported_wft_to_server: bool,
|
825
|
+
wft_from_complete: Option<ValidPollWFTQResponse>,
|
826
|
+
}
|
827
|
+
#[derive(Debug, Clone)]
|
828
|
+
struct RequestEvictMsg {
|
829
|
+
run_id: String,
|
830
|
+
message: String,
|
831
|
+
reason: EvictionReason,
|
832
|
+
}
|
833
|
+
#[derive(Debug)]
|
834
|
+
struct GetStateInfoMsg {
|
835
|
+
response_tx: oneshot::Sender<WorkflowStateInfo>,
|
836
|
+
}
|
837
|
+
|
838
|
+
/// Each activation completion produces one of these
|
839
|
+
#[derive(Debug)]
|
840
|
+
struct ActivationCompleteResult {
|
841
|
+
most_recently_processed_event: usize,
|
842
|
+
outcome: ActivationCompleteOutcome,
|
843
|
+
}
|
844
|
+
/// What needs to be done after calling [Workflows::activation_completed]
|
845
|
+
#[derive(Debug)]
|
846
|
+
#[allow(clippy::large_enum_variant)]
|
847
|
+
enum ActivationCompleteOutcome {
|
848
|
+
/// The WFT must be reported as successful to the server using the contained information.
|
849
|
+
ReportWFTSuccess(ServerCommandsWithWorkflowInfo),
|
850
|
+
/// The WFT must be reported as failed to the server using the contained information.
|
851
|
+
ReportWFTFail(FailedActivationWFTReport),
|
852
|
+
/// There's nothing to do right now. EX: The workflow needs to keep replaying.
|
853
|
+
DoNothing,
|
854
|
+
}
|
855
|
+
#[derive(Debug)]
|
856
|
+
struct FulfillableActivationComplete {
|
857
|
+
result: ActivationCompleteResult,
|
858
|
+
resp_chan: oneshot::Sender<ActivationCompleteResult>,
|
859
|
+
}
|
860
|
+
impl FulfillableActivationComplete {
|
861
|
+
fn fulfill(self) {
|
862
|
+
let _ = self.resp_chan.send(self.result);
|
863
|
+
}
|
864
|
+
}
|
865
|
+
|
866
|
+
fn validate_completion(
|
867
|
+
completion: WorkflowActivationCompletion,
|
868
|
+
) -> Result<ValidatedCompletion, CompleteWfError> {
|
869
|
+
match completion.status {
|
870
|
+
Some(workflow_activation_completion::Status::Successful(success)) => {
|
871
|
+
// Convert to wf commands
|
872
|
+
let commands = success
|
873
|
+
.commands
|
874
|
+
.into_iter()
|
875
|
+
.map(|c| c.try_into())
|
876
|
+
.collect::<Result<Vec<_>, EmptyWorkflowCommandErr>>()
|
877
|
+
.map_err(|_| CompleteWfError::MalformedWorkflowCompletion {
|
878
|
+
reason: "At least one workflow command in the completion contained \
|
879
|
+
an empty variant"
|
880
|
+
.to_owned(),
|
881
|
+
run_id: completion.run_id.clone(),
|
882
|
+
})?;
|
883
|
+
|
884
|
+
if commands.len() > 1
|
885
|
+
&& commands.iter().any(
|
886
|
+
|c| matches!(c, WFCommand::QueryResponse(q) if q.query_id == LEGACY_QUERY_ID),
|
887
|
+
)
|
888
|
+
{
|
889
|
+
return Err(CompleteWfError::MalformedWorkflowCompletion {
|
890
|
+
reason: "Workflow completion had a legacy query response along with other \
|
891
|
+
commands. This is not allowed and constitutes an error in the \
|
892
|
+
lang SDK"
|
893
|
+
.to_owned(),
|
894
|
+
run_id: completion.run_id,
|
895
|
+
});
|
896
|
+
}
|
897
|
+
|
898
|
+
Ok(ValidatedCompletion::Success {
|
899
|
+
run_id: completion.run_id,
|
900
|
+
commands,
|
901
|
+
})
|
902
|
+
}
|
903
|
+
Some(workflow_activation_completion::Status::Failed(failure)) => {
|
904
|
+
Ok(ValidatedCompletion::Fail {
|
905
|
+
run_id: completion.run_id,
|
906
|
+
failure,
|
907
|
+
})
|
908
|
+
}
|
909
|
+
None => Err(CompleteWfError::MalformedWorkflowCompletion {
|
910
|
+
reason: "Workflow completion had empty status field".to_owned(),
|
911
|
+
run_id: completion.run_id,
|
912
|
+
}),
|
913
|
+
}
|
914
|
+
}
|
915
|
+
|
916
|
+
#[derive(Debug)]
|
917
|
+
#[allow(clippy::large_enum_variant)]
|
918
|
+
enum ValidatedCompletion {
|
919
|
+
Success {
|
920
|
+
run_id: String,
|
921
|
+
commands: Vec<WFCommand>,
|
922
|
+
},
|
923
|
+
Fail {
|
924
|
+
run_id: String,
|
925
|
+
failure: Failure,
|
926
|
+
},
|
927
|
+
}
|
928
|
+
|
929
|
+
impl ValidatedCompletion {
|
930
|
+
pub fn run_id(&self) -> &str {
|
931
|
+
match self {
|
932
|
+
ValidatedCompletion::Success { run_id, .. } => run_id,
|
933
|
+
ValidatedCompletion::Fail { run_id, .. } => run_id,
|
934
|
+
}
|
935
|
+
}
|
936
|
+
}
|
937
|
+
|
938
|
+
/// Input to run tasks, sent to [ManagedRun]s via [ManagedRunHandle]s
|
939
|
+
#[derive(Debug)]
|
940
|
+
struct RunAction {
|
941
|
+
action: RunActions,
|
942
|
+
trace_span: Span,
|
943
|
+
}
|
944
|
+
#[derive(Debug)]
|
945
|
+
#[allow(clippy::large_enum_variant)]
|
946
|
+
enum RunActions {
|
947
|
+
NewIncomingWFT(NewIncomingWFT),
|
948
|
+
ActivationCompletion(RunActivationCompletion),
|
949
|
+
CheckMoreWork {
|
950
|
+
want_to_evict: Option<RequestEvictMsg>,
|
951
|
+
has_pending_queries: bool,
|
952
|
+
has_wft: bool,
|
953
|
+
},
|
954
|
+
LocalResolution(LocalResolution),
|
955
|
+
HeartbeatTimeout,
|
956
|
+
}
|
957
|
+
#[derive(Debug)]
|
958
|
+
struct NewIncomingWFT {
|
959
|
+
/// This field is only populated if the machines already exist. Otherwise the machines
|
960
|
+
/// are instantiated with the workflow history.
|
961
|
+
history_update: Option<HistoryUpdate>,
|
962
|
+
/// Wft start time
|
963
|
+
start_time: Instant,
|
964
|
+
}
|
965
|
+
#[derive(Debug)]
|
966
|
+
struct RunActivationCompletion {
|
967
|
+
task_token: TaskToken,
|
968
|
+
start_time: Instant,
|
969
|
+
commands: Vec<WFCommand>,
|
970
|
+
activation_was_eviction: bool,
|
971
|
+
activation_was_only_eviction: bool,
|
972
|
+
has_pending_query: bool,
|
973
|
+
query_responses: Vec<QueryResult>,
|
974
|
+
/// Used to notify the worker when the completion is done processing and the completion can
|
975
|
+
/// unblock. Must always be `Some` when initialized.
|
976
|
+
resp_chan: Option<oneshot::Sender<ActivationCompleteResult>>,
|
977
|
+
}
|
978
|
+
|
979
|
+
/// A response from a [ManagedRun] held by a [ManagedRunHandle]
|
980
|
+
#[derive(Debug)]
|
981
|
+
struct RunUpdateResponse {
|
982
|
+
kind: RunUpdateResponseKind,
|
983
|
+
span: Span,
|
984
|
+
}
|
985
|
+
#[derive(Debug, derive_more::Display)]
|
986
|
+
#[allow(clippy::large_enum_variant)]
|
987
|
+
enum RunUpdateResponseKind {
|
988
|
+
Good(GoodRunUpdate),
|
989
|
+
Fail(FailRunUpdate),
|
990
|
+
}
|
991
|
+
|
992
|
+
#[derive(Debug)]
|
993
|
+
struct GoodRunUpdate {
|
994
|
+
run_id: String,
|
995
|
+
outgoing_activation: Option<ActivationOrAuto>,
|
996
|
+
fulfillable_complete: Option<FulfillableActivationComplete>,
|
997
|
+
have_seen_terminal_event: bool,
|
998
|
+
/// Is true if there are more jobs that need to be sent to lang
|
999
|
+
more_pending_work: bool,
|
1000
|
+
most_recently_processed_event_number: usize,
|
1001
|
+
/// Is true if this update was in response to a new WFT
|
1002
|
+
in_response_to_wft: bool,
|
1003
|
+
}
|
1004
|
+
impl Display for GoodRunUpdate {
|
1005
|
+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
1006
|
+
write!(
|
1007
|
+
f,
|
1008
|
+
"GoodRunUpdate(run_id: {}, outgoing_activation: {}, more_pending_work: {})",
|
1009
|
+
self.run_id,
|
1010
|
+
if let Some(og) = self.outgoing_activation.as_ref() {
|
1011
|
+
format!("{}", og)
|
1012
|
+
} else {
|
1013
|
+
"None".to_string()
|
1014
|
+
},
|
1015
|
+
self.more_pending_work
|
1016
|
+
)
|
1017
|
+
}
|
1018
|
+
}
|
1019
|
+
#[derive(Debug)]
|
1020
|
+
pub(crate) struct FailRunUpdate {
|
1021
|
+
run_id: String,
|
1022
|
+
err: WFMachinesError,
|
1023
|
+
/// This is populated if the run update failed while processing a completion - and thus we
|
1024
|
+
/// must respond down it when handling the failure.
|
1025
|
+
completion_resp: Option<oneshot::Sender<ActivationCompleteResult>>,
|
1026
|
+
}
|
1027
|
+
impl Display for FailRunUpdate {
|
1028
|
+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
1029
|
+
write!(
|
1030
|
+
f,
|
1031
|
+
"FailRunUpdate(run_id: {}, error: {:?})",
|
1032
|
+
self.run_id, self.err
|
1033
|
+
)
|
1034
|
+
}
|
1035
|
+
}
|
1036
|
+
#[derive(Debug)]
|
1037
|
+
pub struct OutgoingServerCommands {
|
1038
|
+
pub commands: Vec<ProtoCommand>,
|
1039
|
+
pub replaying: bool,
|
1040
|
+
}
|
1041
|
+
|
1042
|
+
#[derive(Debug)]
|
1043
|
+
pub(crate) enum LocalResolution {
|
1044
|
+
LocalActivity(LocalActivityResolution),
|
1045
|
+
}
|
1046
|
+
|
1047
|
+
#[derive(thiserror::Error, Debug, derive_more::From)]
|
1048
|
+
#[error("Lang provided workflow command with empty variant")]
|
1049
|
+
pub struct EmptyWorkflowCommandErr;
|
1050
|
+
|
1051
|
+
/// [DrivenWorkflow]s respond with these when called, to indicate what they want to do next.
|
1052
|
+
/// EX: Create a new timer, complete the workflow, etc.
|
1053
|
+
#[derive(Debug, derive_more::From, derive_more::Display)]
|
1054
|
+
#[allow(clippy::large_enum_variant)]
|
1055
|
+
pub enum WFCommand {
|
1056
|
+
/// Returned when we need to wait for the lang sdk to send us something
|
1057
|
+
NoCommandsFromLang,
|
1058
|
+
AddActivity(ScheduleActivity),
|
1059
|
+
AddLocalActivity(ScheduleLocalActivity),
|
1060
|
+
RequestCancelActivity(RequestCancelActivity),
|
1061
|
+
RequestCancelLocalActivity(RequestCancelLocalActivity),
|
1062
|
+
AddTimer(StartTimer),
|
1063
|
+
CancelTimer(CancelTimer),
|
1064
|
+
CompleteWorkflow(CompleteWorkflowExecution),
|
1065
|
+
FailWorkflow(FailWorkflowExecution),
|
1066
|
+
QueryResponse(QueryResult),
|
1067
|
+
ContinueAsNew(ContinueAsNewWorkflowExecution),
|
1068
|
+
CancelWorkflow(CancelWorkflowExecution),
|
1069
|
+
SetPatchMarker(SetPatchMarker),
|
1070
|
+
AddChildWorkflow(StartChildWorkflowExecution),
|
1071
|
+
CancelChild(CancelChildWorkflowExecution),
|
1072
|
+
RequestCancelExternalWorkflow(RequestCancelExternalWorkflowExecution),
|
1073
|
+
SignalExternalWorkflow(SignalExternalWorkflowExecution),
|
1074
|
+
CancelSignalWorkflow(CancelSignalWorkflow),
|
1075
|
+
UpsertSearchAttributes(UpsertWorkflowSearchAttributes),
|
1076
|
+
}
|
1077
|
+
|
1078
|
+
impl TryFrom<WorkflowCommand> for WFCommand {
|
1079
|
+
type Error = EmptyWorkflowCommandErr;
|
1080
|
+
|
1081
|
+
fn try_from(c: WorkflowCommand) -> result::Result<Self, Self::Error> {
|
1082
|
+
match c.variant.ok_or(EmptyWorkflowCommandErr)? {
|
1083
|
+
workflow_command::Variant::StartTimer(s) => Ok(Self::AddTimer(s)),
|
1084
|
+
workflow_command::Variant::CancelTimer(s) => Ok(Self::CancelTimer(s)),
|
1085
|
+
workflow_command::Variant::ScheduleActivity(s) => Ok(Self::AddActivity(s)),
|
1086
|
+
workflow_command::Variant::RequestCancelActivity(s) => {
|
1087
|
+
Ok(Self::RequestCancelActivity(s))
|
1088
|
+
}
|
1089
|
+
workflow_command::Variant::CompleteWorkflowExecution(c) => {
|
1090
|
+
Ok(Self::CompleteWorkflow(c))
|
1091
|
+
}
|
1092
|
+
workflow_command::Variant::FailWorkflowExecution(s) => Ok(Self::FailWorkflow(s)),
|
1093
|
+
workflow_command::Variant::RespondToQuery(s) => Ok(Self::QueryResponse(s)),
|
1094
|
+
workflow_command::Variant::ContinueAsNewWorkflowExecution(s) => {
|
1095
|
+
Ok(Self::ContinueAsNew(s))
|
1096
|
+
}
|
1097
|
+
workflow_command::Variant::CancelWorkflowExecution(s) => Ok(Self::CancelWorkflow(s)),
|
1098
|
+
workflow_command::Variant::SetPatchMarker(s) => Ok(Self::SetPatchMarker(s)),
|
1099
|
+
workflow_command::Variant::StartChildWorkflowExecution(s) => {
|
1100
|
+
Ok(Self::AddChildWorkflow(s))
|
1101
|
+
}
|
1102
|
+
workflow_command::Variant::RequestCancelExternalWorkflowExecution(s) => {
|
1103
|
+
Ok(Self::RequestCancelExternalWorkflow(s))
|
1104
|
+
}
|
1105
|
+
workflow_command::Variant::SignalExternalWorkflowExecution(s) => {
|
1106
|
+
Ok(Self::SignalExternalWorkflow(s))
|
1107
|
+
}
|
1108
|
+
workflow_command::Variant::CancelSignalWorkflow(s) => Ok(Self::CancelSignalWorkflow(s)),
|
1109
|
+
workflow_command::Variant::CancelChildWorkflowExecution(s) => Ok(Self::CancelChild(s)),
|
1110
|
+
workflow_command::Variant::ScheduleLocalActivity(s) => Ok(Self::AddLocalActivity(s)),
|
1111
|
+
workflow_command::Variant::RequestCancelLocalActivity(s) => {
|
1112
|
+
Ok(Self::RequestCancelLocalActivity(s))
|
1113
|
+
}
|
1114
|
+
workflow_command::Variant::UpsertWorkflowSearchAttributes(s) => {
|
1115
|
+
Ok(Self::UpsertSearchAttributes(s))
|
1116
|
+
}
|
1117
|
+
}
|
1118
|
+
}
|
1119
|
+
}
|
1120
|
+
|
1121
|
+
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
1122
|
+
enum CommandID {
|
1123
|
+
Timer(u32),
|
1124
|
+
Activity(u32),
|
1125
|
+
LocalActivity(u32),
|
1126
|
+
ChildWorkflowStart(u32),
|
1127
|
+
SignalExternal(u32),
|
1128
|
+
CancelExternal(u32),
|
1129
|
+
}
|
1130
|
+
|
1131
|
+
/// Details remembered from the workflow execution started event that we may need to recall later.
|
1132
|
+
/// Is a subset of `WorkflowExecutionStartedEventAttributes`, but avoids holding on to huge fields.
|
1133
|
+
#[derive(Debug, Clone)]
|
1134
|
+
pub struct WorkflowStartedInfo {
|
1135
|
+
workflow_task_timeout: Option<Duration>,
|
1136
|
+
workflow_execution_timeout: Option<Duration>,
|
1137
|
+
memo: Option<Memo>,
|
1138
|
+
search_attrs: Option<SearchAttributes>,
|
1139
|
+
retry_policy: Option<RetryPolicy>,
|
1140
|
+
}
|
1141
|
+
|
1142
|
+
type LocalActivityRequestSink =
|
1143
|
+
Arc<dyn Fn(Vec<LocalActRequest>) -> Vec<LocalActivityResolution> + Send + Sync>;
|