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,580 @@
|
|
1
|
+
use crate::{
|
2
|
+
worker::{activities::PendingActivityCancel, client::WorkerClient},
|
3
|
+
TaskToken,
|
4
|
+
};
|
5
|
+
use futures::StreamExt;
|
6
|
+
use std::{
|
7
|
+
collections::{hash_map::Entry, HashMap},
|
8
|
+
sync::Arc,
|
9
|
+
time::{self, Duration, Instant},
|
10
|
+
};
|
11
|
+
use temporal_sdk_core_protos::{
|
12
|
+
coresdk::{activity_task::ActivityCancelReason, ActivityHeartbeat, IntoPayloadsExt},
|
13
|
+
temporal::api::{
|
14
|
+
common::v1::Payload, workflowservice::v1::RecordActivityTaskHeartbeatResponse,
|
15
|
+
},
|
16
|
+
};
|
17
|
+
use tokio::{
|
18
|
+
sync::{
|
19
|
+
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
20
|
+
Mutex, Notify,
|
21
|
+
},
|
22
|
+
task::JoinHandle,
|
23
|
+
};
|
24
|
+
use tokio_util::sync::CancellationToken;
|
25
|
+
|
26
|
+
/// Used to supply new heartbeat events to the activity heartbeat manager, or to send a shutdown
|
27
|
+
/// request.
|
28
|
+
pub(crate) struct ActivityHeartbeatManager {
|
29
|
+
/// Cancellations that have been received when heartbeating are queued here and can be consumed
|
30
|
+
/// by [fetch_cancellations]
|
31
|
+
incoming_cancels: Mutex<UnboundedReceiver<PendingActivityCancel>>,
|
32
|
+
shutdown_token: CancellationToken,
|
33
|
+
/// Used during `shutdown` to await until all inflight requests are sent.
|
34
|
+
join_handle: Mutex<Option<JoinHandle<()>>>,
|
35
|
+
heartbeat_tx: UnboundedSender<HeartbeatAction>,
|
36
|
+
}
|
37
|
+
|
38
|
+
#[derive(Debug)]
|
39
|
+
enum HeartbeatAction {
|
40
|
+
SendHeartbeat(ValidActivityHeartbeat),
|
41
|
+
Evict {
|
42
|
+
token: TaskToken,
|
43
|
+
on_complete: Arc<Notify>,
|
44
|
+
},
|
45
|
+
CompleteReport(TaskToken),
|
46
|
+
CompleteThrottle(TaskToken),
|
47
|
+
}
|
48
|
+
|
49
|
+
#[derive(Debug)]
|
50
|
+
pub struct ValidActivityHeartbeat {
|
51
|
+
pub task_token: TaskToken,
|
52
|
+
pub details: Vec<Payload>,
|
53
|
+
pub throttle_interval: time::Duration,
|
54
|
+
}
|
55
|
+
|
56
|
+
#[derive(Debug)]
|
57
|
+
enum HeartbeatExecutorAction {
|
58
|
+
/// Heartbeats are throttled for this task token, sleep until duration or wait to be cancelled
|
59
|
+
Sleep(TaskToken, Duration, CancellationToken),
|
60
|
+
/// Report heartbeat to the server
|
61
|
+
Report {
|
62
|
+
task_token: TaskToken,
|
63
|
+
details: Vec<Payload>,
|
64
|
+
},
|
65
|
+
}
|
66
|
+
|
67
|
+
/// Errors thrown when heartbeating
|
68
|
+
#[derive(thiserror::Error, Debug)]
|
69
|
+
pub enum ActivityHeartbeatError {
|
70
|
+
/// Heartbeat referenced an activity that we don't think exists. It may have completed already.
|
71
|
+
#[error("Heartbeat has been sent for activity that either completed or never started on this worker.")]
|
72
|
+
UnknownActivity,
|
73
|
+
/// There was a set heartbeat timeout, but it was not parseable. A valid timeout is requried
|
74
|
+
/// to heartbeat.
|
75
|
+
#[error("Unable to parse activity heartbeat timeout.")]
|
76
|
+
InvalidHeartbeatTimeout,
|
77
|
+
/// Core is shutting down and thus new heartbeats are not accepted
|
78
|
+
#[error("New heartbeat requests are not accepted while shutting down")]
|
79
|
+
ShuttingDown,
|
80
|
+
}
|
81
|
+
|
82
|
+
/// Manages activity heartbeating for a worker. Allows sending new heartbeats or requesting and
|
83
|
+
/// awaiting for the shutdown. When shutdown is requested, signal gets sent to all processors, which
|
84
|
+
/// allows them to complete gracefully.
|
85
|
+
impl ActivityHeartbeatManager {
|
86
|
+
/// Records a new heartbeat, the first call will result in an immediate call to the server,
|
87
|
+
/// while rapid successive calls would accumulate for up to `delay` and then latest heartbeat
|
88
|
+
/// details will be sent to the server.
|
89
|
+
///
|
90
|
+
/// It is important that this function is never called with a task token equal to one given
|
91
|
+
/// to [Self::evict] after it has been called, doing so will cause a memory leak, as there is
|
92
|
+
/// no longer an efficient way to forget about that task token.
|
93
|
+
pub(super) fn record(
|
94
|
+
&self,
|
95
|
+
hb: ActivityHeartbeat,
|
96
|
+
throttle_interval: Duration,
|
97
|
+
) -> Result<(), ActivityHeartbeatError> {
|
98
|
+
if self.shutdown_token.is_cancelled() {
|
99
|
+
return Err(ActivityHeartbeatError::ShuttingDown);
|
100
|
+
}
|
101
|
+
self.heartbeat_tx
|
102
|
+
.send(HeartbeatAction::SendHeartbeat(ValidActivityHeartbeat {
|
103
|
+
task_token: TaskToken(hb.task_token),
|
104
|
+
details: hb.details,
|
105
|
+
throttle_interval,
|
106
|
+
}))
|
107
|
+
.expect("Receive half of the heartbeats event channel must not be dropped");
|
108
|
+
|
109
|
+
Ok(())
|
110
|
+
}
|
111
|
+
|
112
|
+
/// Tell the heartbeat manager we are done forever with a certain task, so it may be forgotten.
|
113
|
+
/// This will also force-flush the most recently provided details.
|
114
|
+
/// Record *should* not be called with the same TaskToken after calling this.
|
115
|
+
pub(super) async fn evict(&self, task_token: TaskToken) {
|
116
|
+
let completed = Arc::new(Notify::new());
|
117
|
+
let _ = self.heartbeat_tx.send(HeartbeatAction::Evict {
|
118
|
+
token: task_token,
|
119
|
+
on_complete: completed.clone(),
|
120
|
+
});
|
121
|
+
completed.notified().await;
|
122
|
+
}
|
123
|
+
|
124
|
+
/// Returns a future that resolves any time there is a new activity cancel that must be
|
125
|
+
/// dispatched to lang
|
126
|
+
pub(super) async fn next_pending_cancel(&self) -> Option<PendingActivityCancel> {
|
127
|
+
self.incoming_cancels.lock().await.recv().await
|
128
|
+
}
|
129
|
+
|
130
|
+
// TODO: Can own self now!
|
131
|
+
/// Initiates shutdown procedure by stopping lifecycle loop and awaiting for all in-flight
|
132
|
+
/// heartbeat requests to be flushed to the server.
|
133
|
+
pub(super) async fn shutdown(&self) {
|
134
|
+
self.shutdown_token.cancel();
|
135
|
+
let mut handle = self.join_handle.lock().await;
|
136
|
+
if let Some(h) = handle.take() {
|
137
|
+
let handle_r = h.await;
|
138
|
+
if let Err(e) = handle_r {
|
139
|
+
if !e.is_cancelled() {
|
140
|
+
error!(
|
141
|
+
"Unexpected error joining heartbeating tasks during shutdown: {:?}",
|
142
|
+
e
|
143
|
+
)
|
144
|
+
}
|
145
|
+
}
|
146
|
+
}
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
#[derive(Debug)]
|
151
|
+
struct ActivityHeartbeatState {
|
152
|
+
/// If None and throttle interval is over, untrack this task token
|
153
|
+
last_recorded_details: Option<Vec<Payload>>,
|
154
|
+
/// True if we've queued up a request to record against server, but it hasn't yet completed
|
155
|
+
is_record_in_flight: bool,
|
156
|
+
last_send_requested: Instant,
|
157
|
+
throttle_interval: Duration,
|
158
|
+
throttled_cancellation_token: Option<CancellationToken>,
|
159
|
+
}
|
160
|
+
|
161
|
+
impl ActivityHeartbeatState {
|
162
|
+
/// Get duration to sleep by subtracting `throttle_interval` by elapsed time since
|
163
|
+
/// `last_send_requested`
|
164
|
+
fn get_throttle_sleep_duration(&self) -> Duration {
|
165
|
+
let time_since_last_sent = self.last_send_requested.elapsed();
|
166
|
+
|
167
|
+
if time_since_last_sent > Duration::ZERO && self.throttle_interval > time_since_last_sent {
|
168
|
+
self.throttle_interval - time_since_last_sent
|
169
|
+
} else {
|
170
|
+
Duration::ZERO
|
171
|
+
}
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
#[derive(Debug)]
|
176
|
+
struct HeartbeatStreamState {
|
177
|
+
tt_to_state: HashMap<TaskToken, ActivityHeartbeatState>,
|
178
|
+
tt_needs_flush: HashMap<TaskToken, Arc<Notify>>,
|
179
|
+
incoming_hbs: UnboundedReceiver<HeartbeatAction>,
|
180
|
+
/// Token that can be used to cancel the entire stream.
|
181
|
+
/// Requests to the server are not cancelled with this token.
|
182
|
+
cancellation_token: CancellationToken,
|
183
|
+
}
|
184
|
+
|
185
|
+
impl HeartbeatStreamState {
|
186
|
+
fn new() -> (Self, UnboundedSender<HeartbeatAction>, CancellationToken) {
|
187
|
+
let (heartbeat_tx, incoming_hbs) = unbounded_channel();
|
188
|
+
let cancellation_token = CancellationToken::new();
|
189
|
+
(
|
190
|
+
Self {
|
191
|
+
cancellation_token: cancellation_token.clone(),
|
192
|
+
tt_to_state: Default::default(),
|
193
|
+
tt_needs_flush: Default::default(),
|
194
|
+
incoming_hbs,
|
195
|
+
},
|
196
|
+
heartbeat_tx,
|
197
|
+
cancellation_token,
|
198
|
+
)
|
199
|
+
}
|
200
|
+
|
201
|
+
/// Record a heartbeat received from lang
|
202
|
+
fn record(&mut self, hb: ValidActivityHeartbeat) -> Option<HeartbeatExecutorAction> {
|
203
|
+
match self.tt_to_state.entry(hb.task_token.clone()) {
|
204
|
+
Entry::Vacant(e) => {
|
205
|
+
let state = ActivityHeartbeatState {
|
206
|
+
throttle_interval: hb.throttle_interval,
|
207
|
+
last_send_requested: Instant::now(),
|
208
|
+
// Don't record here because we already flush out these details.
|
209
|
+
// None is used to mark that after throttling we can stop tracking this task
|
210
|
+
// token.
|
211
|
+
last_recorded_details: None,
|
212
|
+
is_record_in_flight: true,
|
213
|
+
throttled_cancellation_token: None,
|
214
|
+
};
|
215
|
+
e.insert(state);
|
216
|
+
Some(HeartbeatExecutorAction::Report {
|
217
|
+
task_token: hb.task_token,
|
218
|
+
details: hb.details,
|
219
|
+
})
|
220
|
+
}
|
221
|
+
Entry::Occupied(mut o) => {
|
222
|
+
let state = o.get_mut();
|
223
|
+
state.last_recorded_details = Some(hb.details);
|
224
|
+
None
|
225
|
+
}
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
/// Heartbeat report to server completed
|
230
|
+
fn handle_report_completed(&mut self, tt: TaskToken) -> Option<HeartbeatExecutorAction> {
|
231
|
+
if let Some(not) = self.tt_needs_flush.remove(&tt) {
|
232
|
+
not.notify_one();
|
233
|
+
}
|
234
|
+
if let Some(st) = self.tt_to_state.get_mut(&tt) {
|
235
|
+
st.is_record_in_flight = false;
|
236
|
+
let cancellation_token = self.cancellation_token.child_token();
|
237
|
+
st.throttled_cancellation_token = Some(cancellation_token.clone());
|
238
|
+
// Always sleep for simplicity even if the duration is 0
|
239
|
+
Some(HeartbeatExecutorAction::Sleep(
|
240
|
+
tt.clone(),
|
241
|
+
st.get_throttle_sleep_duration(),
|
242
|
+
cancellation_token,
|
243
|
+
))
|
244
|
+
} else {
|
245
|
+
None
|
246
|
+
}
|
247
|
+
}
|
248
|
+
|
249
|
+
/// Throttling completed, report or stop tracking task token
|
250
|
+
fn handle_throttle_completed(&mut self, tt: TaskToken) -> Option<HeartbeatExecutorAction> {
|
251
|
+
match self.tt_to_state.entry(tt.clone()) {
|
252
|
+
Entry::Occupied(mut e) => {
|
253
|
+
let state = e.get_mut();
|
254
|
+
if let Some(details) = state.last_recorded_details.take() {
|
255
|
+
// Delete the recorded details before reporting
|
256
|
+
// Reset the cancellation token and schedule another report
|
257
|
+
state.throttled_cancellation_token = None;
|
258
|
+
state.last_send_requested = Instant::now();
|
259
|
+
state.is_record_in_flight = true;
|
260
|
+
Some(HeartbeatExecutorAction::Report {
|
261
|
+
task_token: tt,
|
262
|
+
details,
|
263
|
+
})
|
264
|
+
} else {
|
265
|
+
// Nothing to report, forget this task token
|
266
|
+
e.remove();
|
267
|
+
None
|
268
|
+
}
|
269
|
+
}
|
270
|
+
Entry::Vacant(_) => None,
|
271
|
+
}
|
272
|
+
}
|
273
|
+
|
274
|
+
/// Activity should not be tracked anymore, cancel throttle timer if running.
|
275
|
+
///
|
276
|
+
/// Will return a report action if there are recorded details present, to ensure we flush the
|
277
|
+
/// latest details before we cease tracking this activity.
|
278
|
+
fn evict(
|
279
|
+
&mut self,
|
280
|
+
tt: TaskToken,
|
281
|
+
on_complete: Arc<Notify>,
|
282
|
+
) -> Option<HeartbeatExecutorAction> {
|
283
|
+
if let Some(state) = self.tt_to_state.remove(&tt) {
|
284
|
+
if let Some(cancel_tok) = state.throttled_cancellation_token {
|
285
|
+
cancel_tok.cancel();
|
286
|
+
}
|
287
|
+
if let Some(last_deets) = state.last_recorded_details {
|
288
|
+
self.tt_needs_flush.insert(tt.clone(), on_complete);
|
289
|
+
return Some(HeartbeatExecutorAction::Report {
|
290
|
+
task_token: tt,
|
291
|
+
details: last_deets,
|
292
|
+
});
|
293
|
+
} else if state.is_record_in_flight {
|
294
|
+
self.tt_needs_flush.insert(tt, on_complete);
|
295
|
+
return None;
|
296
|
+
}
|
297
|
+
}
|
298
|
+
// Since there's nothing to flush immediately report back that eviction is finished
|
299
|
+
on_complete.notify_one();
|
300
|
+
None
|
301
|
+
}
|
302
|
+
}
|
303
|
+
|
304
|
+
impl ActivityHeartbeatManager {
|
305
|
+
/// Creates a new instance of an activity heartbeat manager and returns a handle to the user,
|
306
|
+
/// which allows to send new heartbeats and initiate the shutdown.
|
307
|
+
pub fn new(client: Arc<dyn WorkerClient>) -> Self {
|
308
|
+
let (heartbeat_stream_state, heartbeat_tx_source, shutdown_token) =
|
309
|
+
HeartbeatStreamState::new();
|
310
|
+
let (cancels_tx, cancels_rx) = unbounded_channel();
|
311
|
+
let heartbeat_tx = heartbeat_tx_source.clone();
|
312
|
+
|
313
|
+
let join_handle = tokio::spawn(
|
314
|
+
// The stream of incoming heartbeats uses unfold to carry state across each item in the
|
315
|
+
// stream. The closure checks if, for any given activity, we should heartbeat or not
|
316
|
+
// depending on its delay and when we last issued a heartbeat for it.
|
317
|
+
futures::stream::unfold(heartbeat_stream_state, move |mut hb_states| {
|
318
|
+
async move {
|
319
|
+
let hb = tokio::select! {
|
320
|
+
biased;
|
321
|
+
|
322
|
+
_ = hb_states.cancellation_token.cancelled() => {
|
323
|
+
return None
|
324
|
+
}
|
325
|
+
hb = hb_states.incoming_hbs.recv() => match hb {
|
326
|
+
None => return None,
|
327
|
+
Some(hb) => hb,
|
328
|
+
}
|
329
|
+
};
|
330
|
+
|
331
|
+
Some((
|
332
|
+
match hb {
|
333
|
+
HeartbeatAction::SendHeartbeat(hb) => hb_states.record(hb),
|
334
|
+
HeartbeatAction::CompleteReport(tt) => hb_states.handle_report_completed(tt),
|
335
|
+
HeartbeatAction::CompleteThrottle(tt) => hb_states.handle_throttle_completed(tt),
|
336
|
+
HeartbeatAction::Evict{ token, on_complete } => hb_states.evict(token, on_complete),
|
337
|
+
},
|
338
|
+
hb_states,
|
339
|
+
))
|
340
|
+
}
|
341
|
+
})
|
342
|
+
// Filters out `None`s
|
343
|
+
.filter_map(|opt| async { opt })
|
344
|
+
.for_each_concurrent(None, move |action| {
|
345
|
+
let heartbeat_tx = heartbeat_tx_source.clone();
|
346
|
+
let sg = client.clone();
|
347
|
+
let cancels_tx = cancels_tx.clone();
|
348
|
+
async move {
|
349
|
+
match action {
|
350
|
+
HeartbeatExecutorAction::Sleep(tt, duration, cancellation_token) => {
|
351
|
+
tokio::select! {
|
352
|
+
_ = cancellation_token.cancelled() => (),
|
353
|
+
_ = tokio::time::sleep(duration) => {
|
354
|
+
let _ = heartbeat_tx.send(HeartbeatAction::CompleteThrottle(tt));
|
355
|
+
},
|
356
|
+
};
|
357
|
+
}
|
358
|
+
HeartbeatExecutorAction::Report { task_token: tt, details } => {
|
359
|
+
match sg
|
360
|
+
.record_activity_heartbeat(tt.clone(), details.into_payloads())
|
361
|
+
.await
|
362
|
+
{
|
363
|
+
Ok(RecordActivityTaskHeartbeatResponse { cancel_requested }) => {
|
364
|
+
if cancel_requested {
|
365
|
+
cancels_tx
|
366
|
+
.send(PendingActivityCancel::new(
|
367
|
+
tt.clone(),
|
368
|
+
ActivityCancelReason::Cancelled,
|
369
|
+
))
|
370
|
+
.expect(
|
371
|
+
"Receive half of heartbeat cancels not blocked",
|
372
|
+
);
|
373
|
+
}
|
374
|
+
}
|
375
|
+
// Send cancels for any activity that learns its workflow already
|
376
|
+
// finished (which is one thing not found implies - other reasons
|
377
|
+
// would seem equally valid).
|
378
|
+
Err(s) if s.code() == tonic::Code::NotFound => {
|
379
|
+
debug!(task_token = %tt,
|
380
|
+
"Activity not found when recording heartbeat");
|
381
|
+
cancels_tx
|
382
|
+
.send(PendingActivityCancel::new(
|
383
|
+
tt.clone(),
|
384
|
+
ActivityCancelReason::NotFound,
|
385
|
+
))
|
386
|
+
.expect("Receive half of heartbeat cancels not blocked");
|
387
|
+
}
|
388
|
+
Err(e) => {
|
389
|
+
warn!("Error when recording heartbeat: {:?}", e);
|
390
|
+
}
|
391
|
+
};
|
392
|
+
let _ = heartbeat_tx.send(HeartbeatAction::CompleteReport(tt));
|
393
|
+
}
|
394
|
+
}
|
395
|
+
}
|
396
|
+
}),
|
397
|
+
);
|
398
|
+
|
399
|
+
Self {
|
400
|
+
incoming_cancels: Mutex::new(cancels_rx),
|
401
|
+
join_handle: Mutex::new(Some(join_handle)),
|
402
|
+
shutdown_token,
|
403
|
+
heartbeat_tx,
|
404
|
+
}
|
405
|
+
}
|
406
|
+
}
|
407
|
+
|
408
|
+
#[cfg(test)]
|
409
|
+
mod test {
|
410
|
+
use super::*;
|
411
|
+
|
412
|
+
use crate::worker::client::mocks::mock_workflow_client;
|
413
|
+
use std::time::Duration;
|
414
|
+
use temporal_sdk_core_protos::temporal::api::{
|
415
|
+
common::v1::Payload, workflowservice::v1::RecordActivityTaskHeartbeatResponse,
|
416
|
+
};
|
417
|
+
use tokio::time::sleep;
|
418
|
+
|
419
|
+
/// Ensure that heartbeats that are sent with a small `throttle_interval` are aggregated and sent roughly once
|
420
|
+
/// every 1/2 of the heartbeat timeout.
|
421
|
+
#[tokio::test]
|
422
|
+
async fn process_heartbeats_and_shutdown() {
|
423
|
+
let mut mock_client = mock_workflow_client();
|
424
|
+
mock_client
|
425
|
+
.expect_record_activity_heartbeat()
|
426
|
+
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
427
|
+
.times(2);
|
428
|
+
let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
|
429
|
+
let fake_task_token = vec![1, 2, 3];
|
430
|
+
// Send 2 heartbeat requests for 20ms apart.
|
431
|
+
// The first heartbeat should be sent right away, and
|
432
|
+
// the second should be throttled until 50ms have passed.
|
433
|
+
for i in 0_u8..2 {
|
434
|
+
record_heartbeat(&hm, fake_task_token.clone(), i, Duration::from_millis(50));
|
435
|
+
sleep(Duration::from_millis(20)).await;
|
436
|
+
}
|
437
|
+
// sleep again to let heartbeats be flushed
|
438
|
+
sleep(Duration::from_millis(20)).await;
|
439
|
+
hm.shutdown().await;
|
440
|
+
}
|
441
|
+
|
442
|
+
#[tokio::test]
|
443
|
+
async fn send_heartbeats_less_frequently_than_throttle_interval() {
|
444
|
+
let mut mock_client = mock_workflow_client();
|
445
|
+
mock_client
|
446
|
+
.expect_record_activity_heartbeat()
|
447
|
+
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
448
|
+
.times(3);
|
449
|
+
let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
|
450
|
+
let fake_task_token = vec![1, 2, 3];
|
451
|
+
// Heartbeats always get sent if recorded less frequently than the throttle interval
|
452
|
+
for i in 0_u8..3 {
|
453
|
+
record_heartbeat(&hm, fake_task_token.clone(), i, Duration::from_millis(10));
|
454
|
+
sleep(Duration::from_millis(20)).await;
|
455
|
+
}
|
456
|
+
// sleep again to let heartbeats be flushed
|
457
|
+
hm.shutdown().await;
|
458
|
+
}
|
459
|
+
|
460
|
+
/// Ensure that heartbeat can be called from a tight loop without any throttle_interval, resulting in two
|
461
|
+
/// interactions with the server - one immediately and one after 500ms after the throttle_interval.
|
462
|
+
#[tokio::test]
|
463
|
+
async fn process_tight_loop_and_shutdown() {
|
464
|
+
let mut mock_client = mock_workflow_client();
|
465
|
+
mock_client
|
466
|
+
.expect_record_activity_heartbeat()
|
467
|
+
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
468
|
+
.times(1);
|
469
|
+
let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
|
470
|
+
let fake_task_token = vec![1, 2, 3];
|
471
|
+
// Send a whole bunch of heartbeats very fast. We should still only send one total.
|
472
|
+
for i in 0_u8..50 {
|
473
|
+
record_heartbeat(&hm, fake_task_token.clone(), i, Duration::from_millis(2000));
|
474
|
+
// Let it propagate
|
475
|
+
sleep(Duration::from_millis(10)).await;
|
476
|
+
}
|
477
|
+
hm.shutdown().await;
|
478
|
+
}
|
479
|
+
|
480
|
+
/// This test reports one heartbeat and waits for the throttle_interval to elapse before sending another
|
481
|
+
#[tokio::test]
|
482
|
+
async fn report_heartbeat_after_timeout() {
|
483
|
+
let mut mock_client = mock_workflow_client();
|
484
|
+
mock_client
|
485
|
+
.expect_record_activity_heartbeat()
|
486
|
+
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
487
|
+
.times(2);
|
488
|
+
let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
|
489
|
+
let fake_task_token = vec![1, 2, 3];
|
490
|
+
record_heartbeat(&hm, fake_task_token.clone(), 0, Duration::from_millis(100));
|
491
|
+
sleep(Duration::from_millis(500)).await;
|
492
|
+
record_heartbeat(&hm, fake_task_token, 1, Duration::from_millis(100));
|
493
|
+
// Let it propagate
|
494
|
+
sleep(Duration::from_millis(50)).await;
|
495
|
+
hm.shutdown().await;
|
496
|
+
}
|
497
|
+
|
498
|
+
#[tokio::test]
|
499
|
+
async fn evict_works() {
|
500
|
+
let mut mock_client = mock_workflow_client();
|
501
|
+
mock_client
|
502
|
+
.expect_record_activity_heartbeat()
|
503
|
+
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
504
|
+
.times(2);
|
505
|
+
let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
|
506
|
+
let fake_task_token = vec![1, 2, 3];
|
507
|
+
record_heartbeat(&hm, fake_task_token.clone(), 0, Duration::from_millis(100));
|
508
|
+
// Let it propagate
|
509
|
+
sleep(Duration::from_millis(10)).await;
|
510
|
+
hm.evict(fake_task_token.clone().into()).await;
|
511
|
+
record_heartbeat(&hm, fake_task_token, 0, Duration::from_millis(100));
|
512
|
+
// Let it propagate
|
513
|
+
sleep(Duration::from_millis(10)).await;
|
514
|
+
// We know it works b/c otherwise we would have only called record 1 time w/o sleep
|
515
|
+
hm.shutdown().await;
|
516
|
+
}
|
517
|
+
|
518
|
+
#[tokio::test]
|
519
|
+
async fn evict_immediate_after_record() {
|
520
|
+
let mut mock_client = mock_workflow_client();
|
521
|
+
mock_client
|
522
|
+
.expect_record_activity_heartbeat()
|
523
|
+
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
524
|
+
.times(1);
|
525
|
+
let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
|
526
|
+
let fake_task_token = vec![1, 2, 3];
|
527
|
+
record_heartbeat(&hm, fake_task_token.clone(), 0, Duration::from_millis(100));
|
528
|
+
hm.evict(fake_task_token.clone().into()).await;
|
529
|
+
hm.shutdown().await;
|
530
|
+
}
|
531
|
+
|
532
|
+
/// Recording new heartbeats after shutdown is not allowed, and will result in error.
|
533
|
+
#[tokio::test]
|
534
|
+
async fn record_after_shutdown() {
|
535
|
+
let mut mock_client = mock_workflow_client();
|
536
|
+
mock_client
|
537
|
+
.expect_record_activity_heartbeat()
|
538
|
+
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
539
|
+
.times(0);
|
540
|
+
let hm = ActivityHeartbeatManager::new(Arc::new(mock_client));
|
541
|
+
hm.shutdown().await;
|
542
|
+
match hm.record(
|
543
|
+
ActivityHeartbeat {
|
544
|
+
task_token: vec![1, 2, 3],
|
545
|
+
details: vec![Payload {
|
546
|
+
// payload doesn't matter in this case, as it shouldn't get sent anyways.
|
547
|
+
..Default::default()
|
548
|
+
}],
|
549
|
+
},
|
550
|
+
Duration::from_millis(1000),
|
551
|
+
) {
|
552
|
+
Ok(_) => {
|
553
|
+
unreachable!("heartbeat should not be recorded after the shutdown");
|
554
|
+
}
|
555
|
+
Err(e) => {
|
556
|
+
matches!(e, ActivityHeartbeatError::ShuttingDown);
|
557
|
+
}
|
558
|
+
}
|
559
|
+
}
|
560
|
+
|
561
|
+
fn record_heartbeat(
|
562
|
+
hm: &ActivityHeartbeatManager,
|
563
|
+
task_token: Vec<u8>,
|
564
|
+
payload_data: u8,
|
565
|
+
throttle_interval: Duration,
|
566
|
+
) {
|
567
|
+
hm.record(
|
568
|
+
ActivityHeartbeat {
|
569
|
+
task_token,
|
570
|
+
details: vec![Payload {
|
571
|
+
metadata: Default::default(),
|
572
|
+
data: vec![payload_data],
|
573
|
+
}],
|
574
|
+
},
|
575
|
+
// Mimic the same delay we would apply in activity task manager
|
576
|
+
throttle_interval,
|
577
|
+
)
|
578
|
+
.expect("hearbeat recording should not fail");
|
579
|
+
}
|
580
|
+
}
|