temporalio 0.0.0 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +301 -0
- data/bridge/Cargo.lock +2888 -0
- data/bridge/Cargo.toml +27 -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 +104 -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/client/Cargo.toml +40 -0
- data/bridge/sdk-core/client/LICENSE.txt +23 -0
- data/bridge/sdk-core/client/src/lib.rs +1286 -0
- data/bridge/sdk-core/client/src/metrics.rs +165 -0
- data/bridge/sdk-core/client/src/raw.rs +932 -0
- data/bridge/sdk-core/client/src/retry.rs +751 -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 +76 -0
- data/bridge/sdk-core/core/src/abstractions.rs +166 -0
- data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +1014 -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 +925 -0
- data/bridge/sdk-core/core/src/core_tests/mod.rs +100 -0
- data/bridge/sdk-core/core/src/core_tests/queries.rs +894 -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 +2090 -0
- data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +515 -0
- data/bridge/sdk-core/core/src/lib.rs +282 -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 +215 -0
- data/bridge/sdk-core/core/src/retry_logic.rs +202 -0
- data/bridge/sdk-core/core/src/telemetry/log_export.rs +190 -0
- data/bridge/sdk-core/core/src/telemetry/metrics.rs +428 -0
- data/bridge/sdk-core/core/src/telemetry/mod.rs +407 -0
- data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +78 -0
- data/bridge/sdk-core/core/src/test_help/mod.rs +889 -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 +1048 -0
- data/bridge/sdk-core/core/src/worker/activities.rs +481 -0
- data/bridge/sdk-core/core/src/worker/client/mocks.rs +87 -0
- data/bridge/sdk-core/core/src/worker/client.rs +373 -0
- data/bridge/sdk-core/core/src/worker/mod.rs +570 -0
- data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +37 -0
- data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +101 -0
- data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +532 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +907 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +294 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +167 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +858 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +136 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +157 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +129 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +1450 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +316 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +178 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +708 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +439 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +435 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +175 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +242 -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 +1200 -0
- data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +272 -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 +655 -0
- data/bridge/sdk-core/core/src/worker/workflow/mod.rs +1200 -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 +985 -0
- data/bridge/sdk-core/core-api/Cargo.toml +32 -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 +109 -0
- data/bridge/sdk-core/core-api/src/telemetry.rs +147 -0
- data/bridge/sdk-core/core-api/src/worker.rs +148 -0
- data/bridge/sdk-core/etc/deps.svg +162 -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/etc/regen-depgraph.sh +5 -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/evict_while_la_running_no_interference-23_history.bin +0 -0
- data/bridge/sdk-core/histories/evict_while_la_running_no_interference-85_history.bin +0 -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 +9 -0
- data/bridge/sdk-core/protos/api_upstream/build/go.mod +7 -0
- data/bridge/sdk-core/protos/api_upstream/build/go.sum +5 -0
- data/bridge/sdk-core/protos/api_upstream/build/tools.go +29 -0
- data/bridge/sdk-core/protos/api_upstream/dependencies/gogoproto/gogo.proto +141 -0
- data/bridge/sdk-core/protos/api_upstream/go.mod +6 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +89 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +260 -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 +47 -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 +56 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +170 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +118 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/interaction_type.proto +39 -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 +40 -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 +758 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +87 -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 +121 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +80 -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 +379 -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/version/v1/message.proto +59 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +146 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +1168 -0
- data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +415 -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/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 +263 -0
- data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +304 -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/interceptors.rs +50 -0
- data/bridge/sdk-core/sdk/src/lib.rs +794 -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 +694 -0
- data/bridge/sdk-core/sdk/src/workflow_future.rs +499 -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 +107 -0
- data/bridge/sdk-core/sdk-core-protos/src/constants.rs +7 -0
- data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +544 -0
- data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +230 -0
- data/bridge/sdk-core/sdk-core-protos/src/lib.rs +1970 -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 +36 -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 +650 -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 +221 -0
- data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +37 -0
- data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +133 -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 +788 -0
- data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +53 -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 +223 -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 +597 -0
- data/bridge/sdk-core/tests/load_tests.rs +191 -0
- data/bridge/sdk-core/tests/main.rs +113 -0
- data/bridge/sdk-core/tests/runner.rs +93 -0
- data/bridge/src/connection.rs +186 -0
- data/bridge/src/lib.rs +239 -0
- data/bridge/src/runtime.rs +54 -0
- data/bridge/src/worker.rs +124 -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 +50 -0
- data/lib/gen/temporal/api/command/v1/message_pb.rb +174 -0
- data/lib/gen/temporal/api/common/v1/message_pb.rb +69 -0
- data/lib/gen/temporal/api/enums/v1/batch_operation_pb.rb +33 -0
- data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +39 -0
- data/lib/gen/temporal/api/enums/v1/common_pb.rb +42 -0
- data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +68 -0
- data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +77 -0
- data/lib/gen/temporal/api/enums/v1/interaction_type_pb.rb +25 -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 +23 -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 +490 -0
- data/lib/gen/temporal/api/interaction/v1/message_pb.rb +49 -0
- data/lib/gen/temporal/api/namespace/v1/message_pb.rb +63 -0
- data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +85 -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 +149 -0
- data/lib/gen/temporal/api/taskqueue/v1/message_pb.rb +73 -0
- data/lib/gen/temporal/api/version/v1/message_pb.rb +41 -0
- data/lib/gen/temporal/api/workflow/v1/message_pb.rb +111 -0
- data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +788 -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 +165 -0
- data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +196 -0
- data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +34 -0
- data/lib/temporalio/activity/context.rb +97 -0
- data/lib/temporalio/activity/info.rb +67 -0
- data/lib/temporalio/activity.rb +85 -0
- data/lib/temporalio/bridge/error.rb +8 -0
- data/lib/temporalio/bridge.rb +14 -0
- data/lib/temporalio/client/implementation.rb +340 -0
- data/lib/temporalio/client/workflow_handle.rb +243 -0
- data/lib/temporalio/client.rb +131 -0
- data/lib/temporalio/connection.rb +751 -0
- data/lib/temporalio/data_converter.rb +191 -0
- data/lib/temporalio/error/failure.rb +194 -0
- data/lib/temporalio/error/workflow_failure.rb +19 -0
- data/lib/temporalio/errors.rb +40 -0
- data/lib/temporalio/failure_converter/base.rb +26 -0
- data/lib/temporalio/failure_converter/basic.rb +319 -0
- data/lib/temporalio/failure_converter.rb +7 -0
- data/lib/temporalio/interceptor/chain.rb +28 -0
- data/lib/temporalio/interceptor/client.rb +123 -0
- data/lib/temporalio/payload_codec/base.rb +32 -0
- data/lib/temporalio/payload_converter/base.rb +24 -0
- data/lib/temporalio/payload_converter/bytes.rb +27 -0
- data/lib/temporalio/payload_converter/composite.rb +49 -0
- data/lib/temporalio/payload_converter/encoding_base.rb +35 -0
- data/lib/temporalio/payload_converter/json.rb +26 -0
- data/lib/temporalio/payload_converter/nil.rb +26 -0
- data/lib/temporalio/payload_converter.rb +14 -0
- data/lib/temporalio/retry_policy.rb +82 -0
- data/lib/temporalio/retry_state.rb +35 -0
- data/lib/temporalio/runtime.rb +25 -0
- data/lib/temporalio/timeout_type.rb +29 -0
- data/lib/temporalio/version.rb +3 -0
- data/lib/temporalio/worker/activity_runner.rb +92 -0
- data/lib/temporalio/worker/activity_worker.rb +138 -0
- data/lib/temporalio/worker/reactor.rb +46 -0
- data/lib/temporalio/worker/runner.rb +63 -0
- data/lib/temporalio/worker/sync_worker.rb +88 -0
- data/lib/temporalio/worker/thread_pool_executor.rb +51 -0
- data/lib/temporalio/worker.rb +198 -0
- data/lib/temporalio/workflow/execution_info.rb +54 -0
- data/lib/temporalio/workflow/execution_status.rb +36 -0
- data/lib/temporalio/workflow/id_reuse_policy.rb +36 -0
- data/lib/temporalio/workflow/query_reject_condition.rb +33 -0
- data/lib/temporalio.rb +12 -1
- data/lib/thermite_patch.rb +23 -0
- data/temporalio.gemspec +45 -0
- metadata +566 -9
- data/lib/temporal/version.rb +0 -3
- data/lib/temporal.rb +0 -4
- data/temporal.gemspec +0 -20
@@ -0,0 +1,655 @@
|
|
1
|
+
#[cfg(test)]
|
2
|
+
mod managed_wf_test;
|
3
|
+
|
4
|
+
use crate::{
|
5
|
+
worker::{
|
6
|
+
workflow::{
|
7
|
+
machines::WorkflowMachines, ActivationAction, ActivationCompleteOutcome, HistoryUpdate,
|
8
|
+
LocalResolution, NewIncomingWFT, OutgoingServerCommands, RequestEvictMsg, RunActions,
|
9
|
+
RunActivationCompletion, RunUpdateResponse, ServerCommandsWithWorkflowInfo, WFCommand,
|
10
|
+
WorkflowBridge,
|
11
|
+
},
|
12
|
+
LocalActRequest,
|
13
|
+
},
|
14
|
+
MetricsContext,
|
15
|
+
};
|
16
|
+
use futures::{stream, StreamExt};
|
17
|
+
use std::{
|
18
|
+
ops::Add,
|
19
|
+
sync::mpsc::Sender,
|
20
|
+
time::{Duration, Instant},
|
21
|
+
};
|
22
|
+
use temporal_sdk_core_api::errors::WFMachinesError;
|
23
|
+
use temporal_sdk_core_protos::coresdk::{
|
24
|
+
workflow_activation::{RemoveFromCache, WorkflowActivation},
|
25
|
+
workflow_commands::QueryResult,
|
26
|
+
};
|
27
|
+
use tokio::{
|
28
|
+
sync::{
|
29
|
+
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
30
|
+
oneshot,
|
31
|
+
},
|
32
|
+
task,
|
33
|
+
task::JoinHandle,
|
34
|
+
};
|
35
|
+
use tokio_stream::wrappers::UnboundedReceiverStream;
|
36
|
+
use tracing::Span;
|
37
|
+
use tracing_futures::Instrument;
|
38
|
+
|
39
|
+
use crate::worker::workflow::{
|
40
|
+
ActivationCompleteResult, ActivationOrAuto, FailRunUpdate, FulfillableActivationComplete,
|
41
|
+
GoodRunUpdate, LocalActivityRequestSink, RunAction, RunUpdateResponseKind,
|
42
|
+
};
|
43
|
+
use temporal_sdk_core_protos::TaskToken;
|
44
|
+
|
45
|
+
use crate::abstractions::dbg_panic;
|
46
|
+
#[cfg(test)]
|
47
|
+
pub(crate) use managed_wf_test::ManagedWFFunc;
|
48
|
+
|
49
|
+
type Result<T, E = WFMachinesError> = std::result::Result<T, E>;
|
50
|
+
/// What percentage of a WFT timeout we are willing to wait before sending a WFT heartbeat when
|
51
|
+
/// necessary.
|
52
|
+
const WFT_HEARTBEAT_TIMEOUT_FRACTION: f32 = 0.8;
|
53
|
+
|
54
|
+
pub(super) struct ManagedRun {
|
55
|
+
wfm: WorkflowManager,
|
56
|
+
update_tx: UnboundedSender<RunUpdateResponse>,
|
57
|
+
local_activity_request_sink: LocalActivityRequestSink,
|
58
|
+
waiting_on_la: Option<WaitingOnLAs>,
|
59
|
+
// Is set to true if the machines encounter an error and the only subsequent thing we should
|
60
|
+
// do is be evicted.
|
61
|
+
am_broken: bool,
|
62
|
+
}
|
63
|
+
|
64
|
+
/// If an activation completion needed to wait on LA completions (or heartbeat timeout) we use
|
65
|
+
/// this struct to store the data we need to finish the completion once that has happened
|
66
|
+
struct WaitingOnLAs {
|
67
|
+
wft_timeout: Duration,
|
68
|
+
/// If set, we are waiting for LAs to complete as part of a just-finished workflow activation.
|
69
|
+
/// If unset, we already had a heartbeat timeout and got a new WFT without any new work while
|
70
|
+
/// there are still incomplete LAs.
|
71
|
+
completion_dat: Option<(
|
72
|
+
CompletionDataForWFT,
|
73
|
+
oneshot::Sender<ActivationCompleteResult>,
|
74
|
+
)>,
|
75
|
+
hb_chan: UnboundedSender<Span>,
|
76
|
+
heartbeat_timeout_task: JoinHandle<()>,
|
77
|
+
}
|
78
|
+
|
79
|
+
#[derive(Debug)]
|
80
|
+
struct CompletionDataForWFT {
|
81
|
+
task_token: TaskToken,
|
82
|
+
query_responses: Vec<QueryResult>,
|
83
|
+
has_pending_query: bool,
|
84
|
+
activation_was_only_eviction: bool,
|
85
|
+
}
|
86
|
+
|
87
|
+
impl ManagedRun {
|
88
|
+
pub(super) fn new(
|
89
|
+
wfm: WorkflowManager,
|
90
|
+
update_tx: UnboundedSender<RunUpdateResponse>,
|
91
|
+
local_activity_request_sink: LocalActivityRequestSink,
|
92
|
+
) -> Self {
|
93
|
+
Self {
|
94
|
+
wfm,
|
95
|
+
update_tx,
|
96
|
+
local_activity_request_sink,
|
97
|
+
waiting_on_la: None,
|
98
|
+
am_broken: false,
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
pub(super) async fn run(self, run_actions_rx: UnboundedReceiver<RunAction>) {
|
103
|
+
let (heartbeat_tx, heartbeat_rx) = unbounded_channel();
|
104
|
+
stream::select(
|
105
|
+
UnboundedReceiverStream::new(run_actions_rx),
|
106
|
+
UnboundedReceiverStream::new(heartbeat_rx).map(|trace_span| RunAction {
|
107
|
+
action: RunActions::HeartbeatTimeout,
|
108
|
+
trace_span,
|
109
|
+
}),
|
110
|
+
)
|
111
|
+
.fold((self, heartbeat_tx), |(mut me, heartbeat_tx), action| {
|
112
|
+
let span = action.trace_span;
|
113
|
+
let action = action.action;
|
114
|
+
let mut no_wft = false;
|
115
|
+
async move {
|
116
|
+
let res = match action {
|
117
|
+
RunActions::NewIncomingWFT(wft) => me
|
118
|
+
.incoming_wft(wft)
|
119
|
+
.await
|
120
|
+
.map(RunActionOutcome::AfterNewWFT),
|
121
|
+
RunActions::ActivationCompletion(completion) => me
|
122
|
+
.completion(completion, &heartbeat_tx)
|
123
|
+
.await
|
124
|
+
.map(RunActionOutcome::AfterCompletion),
|
125
|
+
RunActions::CheckMoreWork {
|
126
|
+
want_to_evict,
|
127
|
+
has_pending_queries,
|
128
|
+
has_wft,
|
129
|
+
} => {
|
130
|
+
if !has_wft {
|
131
|
+
no_wft = true;
|
132
|
+
}
|
133
|
+
me.check_more_work(want_to_evict, has_pending_queries, has_wft)
|
134
|
+
.await
|
135
|
+
.map(RunActionOutcome::AfterCheckWork)
|
136
|
+
}
|
137
|
+
RunActions::LocalResolution(r) => me
|
138
|
+
.local_resolution(r)
|
139
|
+
.await
|
140
|
+
.map(RunActionOutcome::AfterLocalResolution),
|
141
|
+
RunActions::HeartbeatTimeout => {
|
142
|
+
let maybe_act = if me.heartbeat_timeout() {
|
143
|
+
Some(ActivationOrAuto::Autocomplete {
|
144
|
+
run_id: me.wfm.machines.run_id.clone(),
|
145
|
+
})
|
146
|
+
} else {
|
147
|
+
None
|
148
|
+
};
|
149
|
+
Ok(RunActionOutcome::AfterHeartbeatTimeout(maybe_act))
|
150
|
+
}
|
151
|
+
};
|
152
|
+
match res {
|
153
|
+
Ok(outcome) => {
|
154
|
+
me.send_update_response(outcome, no_wft);
|
155
|
+
}
|
156
|
+
Err(e) => {
|
157
|
+
error!(error=?e, "Error in run machines");
|
158
|
+
me.am_broken = true;
|
159
|
+
me.update_tx
|
160
|
+
.send(RunUpdateResponse {
|
161
|
+
kind: RunUpdateResponseKind::Fail(FailRunUpdate {
|
162
|
+
run_id: me.wfm.machines.run_id.clone(),
|
163
|
+
err: e.source,
|
164
|
+
completion_resp: e.complete_resp_chan,
|
165
|
+
}),
|
166
|
+
span: Span::current(),
|
167
|
+
})
|
168
|
+
.expect("Machine can send update");
|
169
|
+
}
|
170
|
+
}
|
171
|
+
(me, heartbeat_tx)
|
172
|
+
}
|
173
|
+
.instrument(span)
|
174
|
+
})
|
175
|
+
.await;
|
176
|
+
}
|
177
|
+
|
178
|
+
async fn incoming_wft(
|
179
|
+
&mut self,
|
180
|
+
wft: NewIncomingWFT,
|
181
|
+
) -> Result<Option<ActivationOrAuto>, RunUpdateErr> {
|
182
|
+
let activation = if let Some(h) = wft.history_update {
|
183
|
+
self.wfm.feed_history_from_server(h).await?
|
184
|
+
} else {
|
185
|
+
let r = self.wfm.get_next_activation().await?;
|
186
|
+
if r.jobs.is_empty() {
|
187
|
+
return Err(RunUpdateErr {
|
188
|
+
source: WFMachinesError::Fatal(format!(
|
189
|
+
"Machines created for {} with no jobs",
|
190
|
+
self.wfm.machines.run_id
|
191
|
+
)),
|
192
|
+
complete_resp_chan: None,
|
193
|
+
});
|
194
|
+
}
|
195
|
+
r
|
196
|
+
};
|
197
|
+
|
198
|
+
if activation.jobs.is_empty() {
|
199
|
+
if self.wfm.machines.outstanding_local_activity_count() > 0 {
|
200
|
+
// If the activation has no jobs but there are outstanding LAs, we need to restart the
|
201
|
+
// WFT heartbeat.
|
202
|
+
if let Some(ref mut lawait) = self.waiting_on_la {
|
203
|
+
if lawait.completion_dat.is_some() {
|
204
|
+
panic!("Should not have completion dat when getting new wft & empty jobs")
|
205
|
+
}
|
206
|
+
lawait.heartbeat_timeout_task.abort();
|
207
|
+
lawait.heartbeat_timeout_task = start_heartbeat_timeout_task(
|
208
|
+
lawait.hb_chan.clone(),
|
209
|
+
wft.start_time,
|
210
|
+
lawait.wft_timeout,
|
211
|
+
);
|
212
|
+
// No activation needs to be sent to lang. We just need to wait for another
|
213
|
+
// heartbeat timeout or LAs to resolve
|
214
|
+
return Ok(None);
|
215
|
+
} else {
|
216
|
+
panic!(
|
217
|
+
"Got a new WFT while there are outstanding local activities, but there \
|
218
|
+
was no waiting on LA info."
|
219
|
+
)
|
220
|
+
}
|
221
|
+
} else {
|
222
|
+
return Ok(Some(ActivationOrAuto::Autocomplete {
|
223
|
+
run_id: self.wfm.machines.run_id.clone(),
|
224
|
+
}));
|
225
|
+
}
|
226
|
+
}
|
227
|
+
|
228
|
+
Ok(Some(ActivationOrAuto::LangActivation(activation)))
|
229
|
+
}
|
230
|
+
|
231
|
+
async fn completion(
|
232
|
+
&mut self,
|
233
|
+
mut completion: RunActivationCompletion,
|
234
|
+
heartbeat_tx: &UnboundedSender<Span>,
|
235
|
+
) -> Result<Option<FulfillableActivationComplete>, RunUpdateErr> {
|
236
|
+
let resp_chan = completion
|
237
|
+
.resp_chan
|
238
|
+
.take()
|
239
|
+
.expect("Completion response channel must be populated");
|
240
|
+
|
241
|
+
let outcome = async move {
|
242
|
+
// Send commands from lang into the machines then check if the workflow run
|
243
|
+
// needs another activation and mark it if so
|
244
|
+
self.wfm.push_commands(completion.commands).await?;
|
245
|
+
// Don't bother applying the next task if we're evicting at the end of
|
246
|
+
// this activation
|
247
|
+
if !completion.activation_was_eviction {
|
248
|
+
self.wfm.apply_next_task_if_ready().await?;
|
249
|
+
}
|
250
|
+
let new_local_acts = self.wfm.drain_queued_local_activities();
|
251
|
+
|
252
|
+
let immediate_resolutions = (self.local_activity_request_sink)(new_local_acts);
|
253
|
+
for resolution in immediate_resolutions {
|
254
|
+
self.wfm
|
255
|
+
.notify_of_local_result(LocalResolution::LocalActivity(resolution))?;
|
256
|
+
}
|
257
|
+
|
258
|
+
let data = CompletionDataForWFT {
|
259
|
+
task_token: completion.task_token,
|
260
|
+
query_responses: completion.query_responses,
|
261
|
+
has_pending_query: completion.has_pending_query,
|
262
|
+
activation_was_only_eviction: completion.activation_was_only_eviction,
|
263
|
+
};
|
264
|
+
if self.wfm.machines.outstanding_local_activity_count() == 0 {
|
265
|
+
Ok((None, data, self))
|
266
|
+
} else {
|
267
|
+
let wft_timeout: Duration = self
|
268
|
+
.wfm
|
269
|
+
.machines
|
270
|
+
.get_started_info()
|
271
|
+
.and_then(|attrs| attrs.workflow_task_timeout)
|
272
|
+
.ok_or_else(|| {
|
273
|
+
WFMachinesError::Fatal(
|
274
|
+
"Workflow's start attribs were missing a well formed task timeout"
|
275
|
+
.to_string(),
|
276
|
+
)
|
277
|
+
})?;
|
278
|
+
let heartbeat_tx = heartbeat_tx.clone();
|
279
|
+
Ok((
|
280
|
+
Some((heartbeat_tx, completion.start_time, wft_timeout)),
|
281
|
+
data,
|
282
|
+
self,
|
283
|
+
))
|
284
|
+
}
|
285
|
+
}
|
286
|
+
.await;
|
287
|
+
|
288
|
+
match outcome {
|
289
|
+
Ok((None, data, me)) => Ok(Some(me.prepare_complete_resp(resp_chan, data, false))),
|
290
|
+
Ok((Some((chan, start_t, wft_timeout)), data, me)) => {
|
291
|
+
if let Some(wola) = me.waiting_on_la.as_mut() {
|
292
|
+
wola.heartbeat_timeout_task.abort();
|
293
|
+
}
|
294
|
+
me.waiting_on_la = Some(WaitingOnLAs {
|
295
|
+
wft_timeout,
|
296
|
+
completion_dat: Some((data, resp_chan)),
|
297
|
+
hb_chan: chan.clone(),
|
298
|
+
heartbeat_timeout_task: start_heartbeat_timeout_task(
|
299
|
+
chan,
|
300
|
+
start_t,
|
301
|
+
wft_timeout,
|
302
|
+
),
|
303
|
+
});
|
304
|
+
Ok(None)
|
305
|
+
}
|
306
|
+
Err(e) => Err(RunUpdateErr {
|
307
|
+
source: e,
|
308
|
+
complete_resp_chan: Some(resp_chan),
|
309
|
+
}),
|
310
|
+
}
|
311
|
+
}
|
312
|
+
|
313
|
+
async fn check_more_work(
|
314
|
+
&mut self,
|
315
|
+
want_to_evict: Option<RequestEvictMsg>,
|
316
|
+
has_pending_queries: bool,
|
317
|
+
has_wft: bool,
|
318
|
+
) -> Result<Option<ActivationOrAuto>, RunUpdateErr> {
|
319
|
+
if !has_wft {
|
320
|
+
// It doesn't make sense to do work unless we have a WFT
|
321
|
+
return Ok(None);
|
322
|
+
}
|
323
|
+
if self.wfm.machines.has_pending_jobs() && !self.am_broken {
|
324
|
+
Ok(Some(ActivationOrAuto::LangActivation(
|
325
|
+
self.wfm.get_next_activation().await?,
|
326
|
+
)))
|
327
|
+
} else {
|
328
|
+
if has_pending_queries && !self.am_broken {
|
329
|
+
return Ok(Some(ActivationOrAuto::ReadyForQueries(
|
330
|
+
self.wfm.machines.get_wf_activation(),
|
331
|
+
)));
|
332
|
+
}
|
333
|
+
if let Some(wte) = want_to_evict {
|
334
|
+
let mut act = self.wfm.machines.get_wf_activation();
|
335
|
+
// No other jobs make any sense to send if we encountered an error.
|
336
|
+
if self.am_broken {
|
337
|
+
act.jobs = vec![];
|
338
|
+
}
|
339
|
+
act.append_evict_job(RemoveFromCache {
|
340
|
+
message: wte.message,
|
341
|
+
reason: wte.reason as i32,
|
342
|
+
});
|
343
|
+
Ok(Some(ActivationOrAuto::LangActivation(act)))
|
344
|
+
} else {
|
345
|
+
Ok(None)
|
346
|
+
}
|
347
|
+
}
|
348
|
+
}
|
349
|
+
|
350
|
+
fn prepare_complete_resp(
|
351
|
+
&mut self,
|
352
|
+
resp_chan: oneshot::Sender<ActivationCompleteResult>,
|
353
|
+
data: CompletionDataForWFT,
|
354
|
+
due_to_heartbeat_timeout: bool,
|
355
|
+
) -> FulfillableActivationComplete {
|
356
|
+
let outgoing_cmds = self.wfm.get_server_commands();
|
357
|
+
let query_responses = data.query_responses;
|
358
|
+
let has_query_responses = !query_responses.is_empty();
|
359
|
+
let is_query_playback = data.has_pending_query && !has_query_responses;
|
360
|
+
let mut force_new_wft = due_to_heartbeat_timeout;
|
361
|
+
|
362
|
+
// We only actually want to send commands back to the server if there are no more
|
363
|
+
// pending activations and we are caught up on replay. We don't want to complete a wft
|
364
|
+
// if we already saw the final event in the workflow, or if we are playing back for the
|
365
|
+
// express purpose of fulfilling a query. If the activation we sent was *only* an
|
366
|
+
// eviction, and there were no commands produced during iteration, don't send that
|
367
|
+
// either.
|
368
|
+
let no_commands_and_evicting =
|
369
|
+
outgoing_cmds.commands.is_empty() && data.activation_was_only_eviction;
|
370
|
+
let should_respond = !(self.wfm.machines.has_pending_jobs()
|
371
|
+
|| outgoing_cmds.replaying
|
372
|
+
|| is_query_playback
|
373
|
+
|| no_commands_and_evicting);
|
374
|
+
// If there are pending LA resolutions, and we're responding to a query here,
|
375
|
+
// we want to make sure to force a new task, as otherwise once we tell lang about
|
376
|
+
// the LA resolution there wouldn't be any task to reply to with the result of iterating
|
377
|
+
// the workflow.
|
378
|
+
if has_query_responses && self.wfm.machines.has_pending_la_resolutions() {
|
379
|
+
force_new_wft = true;
|
380
|
+
}
|
381
|
+
let to_be_sent = ServerCommandsWithWorkflowInfo {
|
382
|
+
task_token: data.task_token,
|
383
|
+
action: ActivationAction::WftComplete {
|
384
|
+
force_new_wft,
|
385
|
+
commands: outgoing_cmds.commands,
|
386
|
+
query_responses,
|
387
|
+
},
|
388
|
+
};
|
389
|
+
|
390
|
+
let outcome = if should_respond || has_query_responses {
|
391
|
+
ActivationCompleteOutcome::ReportWFTSuccess(to_be_sent)
|
392
|
+
} else {
|
393
|
+
ActivationCompleteOutcome::DoNothing
|
394
|
+
};
|
395
|
+
FulfillableActivationComplete {
|
396
|
+
result: ActivationCompleteResult {
|
397
|
+
most_recently_processed_event: self.wfm.machines.last_processed_event as usize,
|
398
|
+
outcome,
|
399
|
+
},
|
400
|
+
resp_chan,
|
401
|
+
}
|
402
|
+
}
|
403
|
+
|
404
|
+
async fn local_resolution(
|
405
|
+
&mut self,
|
406
|
+
res: LocalResolution,
|
407
|
+
) -> Result<Option<FulfillableActivationComplete>, RunUpdateErr> {
|
408
|
+
debug!(resolution=?res, "Applying local resolution");
|
409
|
+
self.wfm.notify_of_local_result(res)?;
|
410
|
+
if self.wfm.machines.outstanding_local_activity_count() == 0 {
|
411
|
+
if let Some(mut wait_dat) = self.waiting_on_la.take() {
|
412
|
+
// Cancel the heartbeat timeout
|
413
|
+
wait_dat.heartbeat_timeout_task.abort();
|
414
|
+
if let Some((completion_dat, resp_chan)) = wait_dat.completion_dat.take() {
|
415
|
+
return Ok(Some(self.prepare_complete_resp(
|
416
|
+
resp_chan,
|
417
|
+
completion_dat,
|
418
|
+
false,
|
419
|
+
)));
|
420
|
+
}
|
421
|
+
}
|
422
|
+
}
|
423
|
+
Ok(None)
|
424
|
+
}
|
425
|
+
|
426
|
+
/// Returns `true` if autocompletion should be issued, which will actually cause us to end up
|
427
|
+
/// in [completion] again, at which point we'll start a new heartbeat timeout, which will
|
428
|
+
/// immediately trigger and thus finish the completion, forcing a new task as it should.
|
429
|
+
fn heartbeat_timeout(&mut self) -> bool {
|
430
|
+
if let Some(ref mut wait_dat) = self.waiting_on_la {
|
431
|
+
// Cancel the heartbeat timeout
|
432
|
+
wait_dat.heartbeat_timeout_task.abort();
|
433
|
+
if let Some((completion_dat, resp_chan)) = wait_dat.completion_dat.take() {
|
434
|
+
let compl = self.prepare_complete_resp(resp_chan, completion_dat, true);
|
435
|
+
// Immediately fulfill the completion since the run update will already have
|
436
|
+
// been replied to
|
437
|
+
compl.fulfill();
|
438
|
+
} else {
|
439
|
+
// Auto-reply WFT complete
|
440
|
+
return true;
|
441
|
+
}
|
442
|
+
} else {
|
443
|
+
// If a heartbeat timeout happened, we should always have been waiting on LAs
|
444
|
+
dbg_panic!("WFT heartbeat timeout fired but we were not waiting on any LAs");
|
445
|
+
}
|
446
|
+
false
|
447
|
+
}
|
448
|
+
|
449
|
+
fn send_update_response(&self, outcome: RunActionOutcome, no_wft: bool) {
|
450
|
+
let mut in_response_to_wft = false;
|
451
|
+
let (outgoing_activation, fulfillable_complete) = match outcome {
|
452
|
+
RunActionOutcome::AfterNewWFT(a) => {
|
453
|
+
in_response_to_wft = true;
|
454
|
+
(a, None)
|
455
|
+
}
|
456
|
+
RunActionOutcome::AfterCheckWork(a) => (a, None),
|
457
|
+
RunActionOutcome::AfterLocalResolution(f) => (None, f),
|
458
|
+
RunActionOutcome::AfterCompletion(f) => (None, f),
|
459
|
+
RunActionOutcome::AfterHeartbeatTimeout(a) => (a, None),
|
460
|
+
};
|
461
|
+
let mut more_pending_work = self.wfm.machines.has_pending_jobs();
|
462
|
+
// We don't want to consider there to be more local-only work to be done if there is no
|
463
|
+
// workflow task associated with the run right now. This can happen if, ex, we complete
|
464
|
+
// a local activity while waiting for server to send us the next WFT. Activating lang would
|
465
|
+
// be harmful at this stage, as there might be work returned in that next WFT which should
|
466
|
+
// be part of the next activation.
|
467
|
+
if no_wft {
|
468
|
+
more_pending_work = false;
|
469
|
+
}
|
470
|
+
self.update_tx
|
471
|
+
.send(RunUpdateResponse {
|
472
|
+
kind: RunUpdateResponseKind::Good(GoodRunUpdate {
|
473
|
+
run_id: self.wfm.machines.run_id.clone(),
|
474
|
+
outgoing_activation,
|
475
|
+
fulfillable_complete,
|
476
|
+
have_seen_terminal_event: self.wfm.machines.have_seen_terminal_event,
|
477
|
+
more_pending_work,
|
478
|
+
most_recently_processed_event_number: self.wfm.machines.last_processed_event
|
479
|
+
as usize,
|
480
|
+
in_response_to_wft,
|
481
|
+
}),
|
482
|
+
span: Span::current(),
|
483
|
+
})
|
484
|
+
.expect("Machine can send update");
|
485
|
+
}
|
486
|
+
}
|
487
|
+
|
488
|
+
fn start_heartbeat_timeout_task(
|
489
|
+
chan: UnboundedSender<Span>,
|
490
|
+
wft_start_time: Instant,
|
491
|
+
wft_timeout: Duration,
|
492
|
+
) -> JoinHandle<()> {
|
493
|
+
// The heartbeat deadline is 80% of the WFT timeout
|
494
|
+
let wft_heartbeat_deadline =
|
495
|
+
wft_start_time.add(wft_timeout.mul_f32(WFT_HEARTBEAT_TIMEOUT_FRACTION));
|
496
|
+
task::spawn(async move {
|
497
|
+
tokio::time::sleep_until(wft_heartbeat_deadline.into()).await;
|
498
|
+
let _ = chan.send(Span::current());
|
499
|
+
})
|
500
|
+
}
|
501
|
+
|
502
|
+
enum RunActionOutcome {
|
503
|
+
AfterNewWFT(Option<ActivationOrAuto>),
|
504
|
+
AfterCheckWork(Option<ActivationOrAuto>),
|
505
|
+
AfterLocalResolution(Option<FulfillableActivationComplete>),
|
506
|
+
AfterCompletion(Option<FulfillableActivationComplete>),
|
507
|
+
AfterHeartbeatTimeout(Option<ActivationOrAuto>),
|
508
|
+
}
|
509
|
+
|
510
|
+
#[derive(derive_more::DebugCustom)]
|
511
|
+
#[debug(fmt = "RunUpdateErr({:?})", source)]
|
512
|
+
struct RunUpdateErr {
|
513
|
+
source: WFMachinesError,
|
514
|
+
complete_resp_chan: Option<oneshot::Sender<ActivationCompleteResult>>,
|
515
|
+
}
|
516
|
+
|
517
|
+
impl From<WFMachinesError> for RunUpdateErr {
|
518
|
+
fn from(e: WFMachinesError) -> Self {
|
519
|
+
RunUpdateErr {
|
520
|
+
source: e,
|
521
|
+
complete_resp_chan: None,
|
522
|
+
}
|
523
|
+
}
|
524
|
+
}
|
525
|
+
|
526
|
+
/// Manages an instance of a [WorkflowMachines], which is not thread-safe, as well as other data
|
527
|
+
/// associated with that specific workflow run.
|
528
|
+
pub(crate) struct WorkflowManager {
|
529
|
+
machines: WorkflowMachines,
|
530
|
+
/// Is always `Some` in normal operation. Optional to allow for unit testing with the test
|
531
|
+
/// workflow driver, which does not need to complete activations the normal way.
|
532
|
+
command_sink: Option<Sender<Vec<WFCommand>>>,
|
533
|
+
}
|
534
|
+
|
535
|
+
impl WorkflowManager {
|
536
|
+
/// Create a new workflow manager given workflow history and execution info as would be found
|
537
|
+
/// in [PollWorkflowTaskQueueResponse]
|
538
|
+
pub fn new(
|
539
|
+
history: HistoryUpdate,
|
540
|
+
namespace: String,
|
541
|
+
workflow_id: String,
|
542
|
+
workflow_type: String,
|
543
|
+
run_id: String,
|
544
|
+
metrics: MetricsContext,
|
545
|
+
) -> Self {
|
546
|
+
let (wfb, cmd_sink) = WorkflowBridge::new();
|
547
|
+
let state_machines = WorkflowMachines::new(
|
548
|
+
namespace,
|
549
|
+
workflow_id,
|
550
|
+
workflow_type,
|
551
|
+
run_id,
|
552
|
+
history,
|
553
|
+
Box::new(wfb).into(),
|
554
|
+
metrics,
|
555
|
+
);
|
556
|
+
Self {
|
557
|
+
machines: state_machines,
|
558
|
+
command_sink: Some(cmd_sink),
|
559
|
+
}
|
560
|
+
}
|
561
|
+
|
562
|
+
#[cfg(test)]
|
563
|
+
pub const fn new_from_machines(workflow_machines: WorkflowMachines) -> Self {
|
564
|
+
Self {
|
565
|
+
machines: workflow_machines,
|
566
|
+
command_sink: None,
|
567
|
+
}
|
568
|
+
}
|
569
|
+
|
570
|
+
/// Given history that was just obtained from the server, pipe it into this workflow's machines.
|
571
|
+
///
|
572
|
+
/// Should only be called when a workflow has caught up on replay (or is just beginning). It
|
573
|
+
/// will return a workflow activation if one is needed.
|
574
|
+
async fn feed_history_from_server(
|
575
|
+
&mut self,
|
576
|
+
update: HistoryUpdate,
|
577
|
+
) -> Result<WorkflowActivation> {
|
578
|
+
self.machines.new_history_from_server(update).await?;
|
579
|
+
self.get_next_activation().await
|
580
|
+
}
|
581
|
+
|
582
|
+
/// Let this workflow know that something we've been waiting locally on has resolved, like a
|
583
|
+
/// local activity or side effect
|
584
|
+
///
|
585
|
+
/// Returns true if the resolution did anything. EX: If the activity is already canceled and
|
586
|
+
/// used the TryCancel or Abandon modes, the resolution is uninteresting.
|
587
|
+
fn notify_of_local_result(&mut self, resolved: LocalResolution) -> Result<bool> {
|
588
|
+
self.machines.local_resolution(resolved)
|
589
|
+
}
|
590
|
+
|
591
|
+
/// Fetch the next workflow activation for this workflow if one is required. Doing so will apply
|
592
|
+
/// the next unapplied workflow task if such a sequence exists in history we already know about.
|
593
|
+
///
|
594
|
+
/// Callers may also need to call [get_server_commands] after this to issue any pending commands
|
595
|
+
/// to the server.
|
596
|
+
async fn get_next_activation(&mut self) -> Result<WorkflowActivation> {
|
597
|
+
// First check if there are already some pending jobs, which can be a result of replay.
|
598
|
+
let activation = self.machines.get_wf_activation();
|
599
|
+
if !activation.jobs.is_empty() {
|
600
|
+
return Ok(activation);
|
601
|
+
}
|
602
|
+
|
603
|
+
self.machines.apply_next_wft_from_history().await?;
|
604
|
+
Ok(self.machines.get_wf_activation())
|
605
|
+
}
|
606
|
+
|
607
|
+
/// If there are no pending jobs for the workflow, apply the next workflow task and check
|
608
|
+
/// again if there are any jobs. Importantly, does not *drain* jobs.
|
609
|
+
///
|
610
|
+
/// Returns true if there are jobs (before or after applying the next WFT).
|
611
|
+
async fn apply_next_task_if_ready(&mut self) -> Result<bool> {
|
612
|
+
if self.machines.has_pending_jobs() {
|
613
|
+
return Ok(true);
|
614
|
+
}
|
615
|
+
loop {
|
616
|
+
let consumed_events = self.machines.apply_next_wft_from_history().await?;
|
617
|
+
|
618
|
+
if consumed_events == 0 || !self.machines.replaying || self.machines.has_pending_jobs()
|
619
|
+
{
|
620
|
+
// Keep applying tasks while there are events, we are still replaying, and there are
|
621
|
+
// no jobs
|
622
|
+
break;
|
623
|
+
}
|
624
|
+
}
|
625
|
+
Ok(self.machines.has_pending_jobs())
|
626
|
+
}
|
627
|
+
|
628
|
+
/// Typically called after [get_next_activation], use this to retrieve commands to be sent to
|
629
|
+
/// the server which have been generated by the machines. Does *not* drain those commands.
|
630
|
+
/// See [WorkflowMachines::get_commands].
|
631
|
+
fn get_server_commands(&self) -> OutgoingServerCommands {
|
632
|
+
OutgoingServerCommands {
|
633
|
+
commands: self.machines.get_commands(),
|
634
|
+
replaying: self.machines.replaying,
|
635
|
+
}
|
636
|
+
}
|
637
|
+
|
638
|
+
/// Remove and return all queued local activities. Once this is called, they need to be
|
639
|
+
/// dispatched for execution.
|
640
|
+
fn drain_queued_local_activities(&mut self) -> Vec<LocalActRequest> {
|
641
|
+
self.machines.drain_queued_local_activities()
|
642
|
+
}
|
643
|
+
|
644
|
+
/// Feed the workflow machines new commands issued by the executing workflow code, and iterate
|
645
|
+
/// the machines.
|
646
|
+
async fn push_commands(&mut self, cmds: Vec<WFCommand>) -> Result<()> {
|
647
|
+
if let Some(cs) = self.command_sink.as_mut() {
|
648
|
+
cs.send(cmds).map_err(|_| {
|
649
|
+
WFMachinesError::Fatal("Internal error buffering workflow commands".to_string())
|
650
|
+
})?;
|
651
|
+
}
|
652
|
+
self.machines.iterate_machines().await?;
|
653
|
+
Ok(())
|
654
|
+
}
|
655
|
+
}
|