@temporalio/core-bridge 1.8.6 → 1.9.0

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.
Files changed (213) hide show
  1. package/Cargo.lock +670 -594
  2. package/Cargo.toml +2 -1
  3. package/lib/errors.js +6 -6
  4. package/lib/errors.js.map +1 -1
  5. package/lib/index.d.ts +17 -44
  6. package/lib/index.js.map +1 -1
  7. package/package.json +5 -6
  8. package/releases/aarch64-apple-darwin/index.node +0 -0
  9. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  10. package/releases/x86_64-apple-darwin/index.node +0 -0
  11. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  12. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  13. package/sdk-core/.github/workflows/heavy.yml +4 -0
  14. package/sdk-core/.github/workflows/per-pr.yml +96 -0
  15. package/sdk-core/ARCHITECTURE.md +1 -1
  16. package/sdk-core/Cargo.toml +10 -0
  17. package/sdk-core/LICENSE.txt +0 -2
  18. package/sdk-core/README.md +37 -21
  19. package/sdk-core/client/Cargo.toml +7 -4
  20. package/sdk-core/client/src/lib.rs +274 -142
  21. package/sdk-core/client/src/metrics.rs +68 -57
  22. package/sdk-core/client/src/raw.rs +191 -45
  23. package/sdk-core/client/src/retry.rs +20 -0
  24. package/sdk-core/client/src/worker_registry/mod.rs +264 -0
  25. package/sdk-core/client/src/workflow_handle/mod.rs +2 -1
  26. package/sdk-core/core/Cargo.toml +17 -19
  27. package/sdk-core/core/src/core_tests/activity_tasks.rs +4 -2
  28. package/sdk-core/core/src/core_tests/child_workflows.rs +7 -7
  29. package/sdk-core/core/src/core_tests/mod.rs +1 -0
  30. package/sdk-core/core/src/core_tests/queries.rs +42 -1
  31. package/sdk-core/core/src/core_tests/replay_flag.rs +29 -39
  32. package/sdk-core/core/src/core_tests/updates.rs +73 -0
  33. package/sdk-core/core/src/core_tests/workflow_tasks.rs +52 -1
  34. package/sdk-core/core/src/ephemeral_server/mod.rs +34 -11
  35. package/sdk-core/core/src/internal_flags.rs +7 -1
  36. package/sdk-core/core/src/lib.rs +19 -36
  37. package/sdk-core/core/src/protosext/mod.rs +12 -4
  38. package/sdk-core/core/src/protosext/protocol_messages.rs +102 -0
  39. package/sdk-core/core/src/replay/mod.rs +99 -48
  40. package/sdk-core/core/src/telemetry/log_export.rs +161 -28
  41. package/sdk-core/core/src/telemetry/metrics.rs +869 -248
  42. package/sdk-core/core/src/telemetry/mod.rs +153 -257
  43. package/sdk-core/core/src/telemetry/prometheus_server.rs +36 -31
  44. package/sdk-core/core/src/test_help/mod.rs +64 -5
  45. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +12 -2
  46. package/sdk-core/core/src/worker/activities.rs +276 -10
  47. package/sdk-core/core/src/worker/client/mocks.rs +18 -0
  48. package/sdk-core/core/src/worker/client.rs +16 -3
  49. package/sdk-core/core/src/worker/mod.rs +45 -28
  50. package/sdk-core/core/src/worker/slot_provider.rs +175 -0
  51. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +27 -34
  52. package/sdk-core/core/src/worker/workflow/history_update.rs +5 -2
  53. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +71 -95
  54. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +34 -22
  55. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +50 -34
  56. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +106 -92
  57. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +22 -21
  58. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +386 -499
  59. package/sdk-core/core/src/worker/workflow/machines/mod.rs +12 -2
  60. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +33 -26
  61. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +198 -215
  62. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +67 -63
  63. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +88 -119
  64. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +3 -1
  65. package/sdk-core/core/src/worker/workflow/machines/update_state_machine.rs +411 -0
  66. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +27 -26
  67. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +319 -94
  68. package/sdk-core/core/src/worker/workflow/managed_run.rs +179 -132
  69. package/sdk-core/core/src/worker/workflow/mod.rs +129 -58
  70. package/sdk-core/core/src/worker/workflow/run_cache.rs +16 -26
  71. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +2 -2
  72. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +48 -43
  73. package/sdk-core/core-api/Cargo.toml +8 -7
  74. package/sdk-core/core-api/src/lib.rs +4 -12
  75. package/sdk-core/core-api/src/telemetry/metrics.rs +334 -0
  76. package/sdk-core/core-api/src/telemetry.rs +53 -42
  77. package/sdk-core/core-api/src/worker.rs +7 -0
  78. package/sdk-core/{.buildkite/docker → docker}/docker-compose.yaml +1 -1
  79. package/sdk-core/etc/dynamic-config.yaml +11 -1
  80. package/sdk-core/fsm/LICENSE.txt +0 -2
  81. package/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +1 -1
  82. package/sdk-core/fsm/rustfsm_procmacro/LICENSE.txt +0 -2
  83. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +1 -3
  84. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +2 -2
  85. package/sdk-core/fsm/rustfsm_trait/LICENSE.txt +0 -2
  86. package/sdk-core/sdk/Cargo.toml +2 -2
  87. package/sdk-core/sdk/src/lib.rs +85 -7
  88. package/sdk-core/sdk/src/workflow_context/options.rs +4 -0
  89. package/sdk-core/sdk/src/workflow_context.rs +43 -15
  90. package/sdk-core/sdk/src/workflow_future.rs +334 -204
  91. package/sdk-core/sdk-core-protos/Cargo.toml +3 -3
  92. package/sdk-core/sdk-core-protos/build.rs +14 -14
  93. package/sdk-core/sdk-core-protos/protos/api_upstream/.buildkite/Dockerfile +2 -0
  94. package/sdk-core/sdk-core-protos/protos/api_upstream/Makefile +99 -0
  95. package/sdk-core/sdk-core-protos/protos/api_upstream/api-linter.yaml +56 -0
  96. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.gen.yaml +20 -0
  97. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.lock +11 -0
  98. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.yaml +18 -0
  99. package/sdk-core/sdk-core-protos/protos/api_upstream/google/api/annotations.proto +31 -0
  100. package/sdk-core/sdk-core-protos/protos/api_upstream/google/api/http.proto +379 -0
  101. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/any.proto +162 -0
  102. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/descriptor.proto +1212 -0
  103. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/duration.proto +115 -0
  104. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/empty.proto +51 -0
  105. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/timestamp.proto +144 -0
  106. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/wrappers.proto +123 -0
  107. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/batch/v1/message.proto +12 -9
  108. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/command/v1/message.proto +11 -13
  109. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/common/v1/message.proto +33 -4
  110. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
  111. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/reset.proto +4 -4
  112. package/sdk-core/{protos/api_upstream/build/tools.go → sdk-core-protos/protos/api_upstream/temporal/api/export/v1/message.proto} +22 -6
  113. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/filter/v1/message.proto +2 -4
  114. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/history/v1/message.proto +21 -23
  115. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/namespace/v1/message.proto +2 -4
  116. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/operatorservice/v1/request_response.proto +2 -0
  117. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -0
  118. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/replication/v1/message.proto +1 -3
  119. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/schedule/v1/message.proto +36 -20
  120. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +13 -0
  121. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +66 -0
  122. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -4
  123. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/update/v1/message.proto +1 -1
  124. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/version/v1/message.proto +2 -3
  125. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/workflow/v1/message.proto +24 -22
  126. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/workflowservice/v1/request_response.proto +84 -32
  127. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/workflowservice/v1/service.proto +205 -47
  128. package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +57 -0
  129. package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +27 -0
  130. package/sdk-core/sdk-core-protos/src/history_builder.rs +67 -2
  131. package/sdk-core/sdk-core-protos/src/history_info.rs +1 -1
  132. package/sdk-core/sdk-core-protos/src/lib.rs +76 -3
  133. package/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  134. package/sdk-core/test-utils/Cargo.toml +6 -1
  135. package/sdk-core/test-utils/src/canned_histories.rs +3 -57
  136. package/sdk-core/test-utils/src/interceptors.rs +46 -0
  137. package/sdk-core/test-utils/src/lib.rs +106 -38
  138. package/sdk-core/tests/integ_tests/metrics_tests.rs +110 -15
  139. package/sdk-core/tests/integ_tests/queries_tests.rs +174 -3
  140. package/sdk-core/tests/integ_tests/update_tests.rs +908 -0
  141. package/sdk-core/tests/integ_tests/visibility_tests.rs +4 -4
  142. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +44 -1
  143. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -1
  144. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  145. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -4
  146. package/sdk-core/tests/integ_tests/workflow_tests/eager.rs +61 -0
  147. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +27 -2
  148. package/sdk-core/tests/integ_tests/workflow_tests.rs +142 -3
  149. package/sdk-core/tests/main.rs +2 -1
  150. package/sdk-core/tests/runner.rs +15 -2
  151. package/src/conversions.rs +107 -96
  152. package/src/helpers.rs +74 -0
  153. package/src/runtime.rs +29 -15
  154. package/src/worker.rs +14 -61
  155. package/ts/index.ts +23 -54
  156. package/sdk-core/.buildkite/docker/Dockerfile +0 -9
  157. package/sdk-core/.buildkite/docker/build.sh +0 -5
  158. package/sdk-core/.buildkite/docker/docker-compose-ci.yaml +0 -27
  159. package/sdk-core/.buildkite/pipeline.yml +0 -57
  160. package/sdk-core/.github/workflows/semgrep.yml +0 -25
  161. package/sdk-core/client/LICENSE.txt +0 -23
  162. package/sdk-core/core/LICENSE.txt +0 -23
  163. package/sdk-core/core/src/worker/workflow/bridge.rs +0 -35
  164. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +0 -215
  165. package/sdk-core/core-api/LICENSE.txt +0 -23
  166. package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +0 -2
  167. package/sdk-core/protos/api_upstream/Makefile +0 -80
  168. package/sdk-core/protos/api_upstream/api-linter.yaml +0 -40
  169. package/sdk-core/protos/api_upstream/buf.yaml +0 -9
  170. package/sdk-core/protos/api_upstream/build/go.mod +0 -7
  171. package/sdk-core/protos/api_upstream/build/go.sum +0 -5
  172. package/sdk-core/protos/api_upstream/go.mod +0 -6
  173. package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +0 -141
  174. package/sdk-core/sdk/LICENSE.txt +0 -23
  175. package/sdk-core/sdk-core-protos/LICENSE.txt +0 -23
  176. /package/sdk-core/{.buildkite/docker → docker}/docker-compose-telem.yaml +0 -0
  177. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.buildkite/docker-compose.yml +0 -0
  178. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.buildkite/pipeline.yml +0 -0
  179. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/CODEOWNERS +0 -0
  180. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  181. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/workflows/publish-docs.yml +0 -0
  182. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/workflows/trigger-api-go-update.yml +0 -0
  183. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/LICENSE +0 -0
  184. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/README.md +0 -0
  185. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/batch_operation.proto +0 -0
  186. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/command_type.proto +0 -0
  187. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/common.proto +0 -0
  188. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/event_type.proto +0 -0
  189. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/namespace.proto +0 -0
  190. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/query.proto +0 -0
  191. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/schedule.proto +0 -0
  192. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/task_queue.proto +0 -0
  193. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/update.proto +0 -0
  194. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/workflow.proto +0 -0
  195. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/errordetails/v1/message.proto +0 -0
  196. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/failure/v1/message.proto +0 -0
  197. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/protocol/v1/message.proto +0 -0
  198. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/query/v1/message.proto +0 -0
  199. /package/sdk-core/{protos → sdk-core-protos/protos}/google/rpc/status.proto +0 -0
  200. /package/sdk-core/{protos → sdk-core-protos/protos}/grpc/health/v1/health.proto +0 -0
  201. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/activity_result/activity_result.proto +0 -0
  202. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/activity_task/activity_task.proto +0 -0
  203. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/child_workflow/child_workflow.proto +0 -0
  204. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/common/common.proto +0 -0
  205. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/core_interface.proto +0 -0
  206. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/external_data/external_data.proto +0 -0
  207. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +0 -0
  208. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/Makefile +0 -0
  209. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/api-linter.yaml +0 -0
  210. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/buf.yaml +0 -0
  211. /package/sdk-core/{protos/api_upstream → sdk-core-protos/protos/testsrv_upstream}/dependencies/gogoproto/gogo.proto +0 -0
  212. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +0 -0
  213. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/temporal/api/testservice/v1/service.proto +0 -0
@@ -0,0 +1,908 @@
1
+ use anyhow::{anyhow, bail};
2
+ use assert_matches::assert_matches;
3
+ use futures_util::{future, future::join_all, StreamExt};
4
+ use lazy_static::lazy_static;
5
+ use std::{
6
+ sync::atomic::{AtomicBool, AtomicUsize, Ordering},
7
+ time::Duration,
8
+ };
9
+ use temporal_client::WorkflowClientTrait;
10
+ use temporal_sdk::{ActContext, ActivityOptions, LocalActivityOptions, UpdateContext, WfContext};
11
+ use temporal_sdk_core::replay::HistoryForReplay;
12
+ use temporal_sdk_core_api::Worker;
13
+ use temporal_sdk_core_protos::{
14
+ coresdk::{
15
+ activity_result::ActivityExecutionResult,
16
+ workflow_activation::{
17
+ remove_from_cache::EvictionReason, workflow_activation_job, WorkflowActivationJob,
18
+ },
19
+ workflow_commands::{
20
+ update_response, CompleteWorkflowExecution, ScheduleLocalActivity, UpdateResponse,
21
+ },
22
+ workflow_completion::WorkflowActivationCompletion,
23
+ ActivityTaskCompletion, AsJsonPayloadExt, IntoPayloadsExt,
24
+ },
25
+ temporal::api::{
26
+ enums::v1::{EventType, UpdateWorkflowExecutionLifecycleStage},
27
+ update,
28
+ update::v1::WaitPolicy,
29
+ },
30
+ };
31
+ use temporal_sdk_core_test_utils::{
32
+ init_core_and_create_wf, init_core_replay_preloaded, start_timer_cmd, CoreWfStarter,
33
+ WorkerTestHelpers,
34
+ };
35
+ use tokio::{join, sync::Barrier};
36
+
37
+ #[rstest::rstest]
38
+ #[tokio::test]
39
+ async fn update_workflow(#[values(true, false)] will_fail: bool) {
40
+ let mut starter = init_core_and_create_wf("update_workflow").await;
41
+ let core = starter.get_worker().await;
42
+ let client = starter.get_client().await;
43
+ let workflow_id = starter.get_task_queue().to_string();
44
+
45
+ let update_id = "some_update";
46
+ // Task is completed with no commands
47
+ let res = core.poll_workflow_activation().await.unwrap();
48
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(res.run_id.clone()))
49
+ .await
50
+ .unwrap();
51
+
52
+ // Send the update to the server
53
+ let update_task = async {
54
+ client
55
+ .update_workflow_execution(
56
+ workflow_id.to_string(),
57
+ res.run_id.to_string(),
58
+ update_id.to_string(),
59
+ WaitPolicy {
60
+ lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
61
+ },
62
+ Some("hi".into()),
63
+ )
64
+ .await
65
+ .unwrap()
66
+ };
67
+
68
+ let processing_task = _do_update_workflow(will_fail, core.as_ref());
69
+ let (ur, _) = join!(update_task, processing_task);
70
+
71
+ let v = ur.outcome.unwrap().value.unwrap();
72
+ if will_fail {
73
+ assert_matches!(v, update::v1::outcome::Value::Failure(_));
74
+ } else {
75
+ assert_matches!(v, update::v1::outcome::Value::Success(_));
76
+ }
77
+
78
+ // Make sure replay works
79
+ let history = client
80
+ .get_workflow_execution_history(workflow_id.clone(), None, vec![])
81
+ .await
82
+ .unwrap()
83
+ .history
84
+ .unwrap();
85
+ let with_id = HistoryForReplay::new(history, workflow_id.clone());
86
+ let replay_worker = init_core_replay_preloaded(&workflow_id, [with_id]);
87
+ _do_update_workflow(will_fail, replay_worker.as_ref()).await;
88
+ }
89
+
90
+ async fn _do_update_workflow(will_fail: bool, core: &dyn Worker) {
91
+ let res = core.poll_workflow_activation().await.unwrap();
92
+ // On replay, the first activation has update & start workflow, but on first execution, it does
93
+ // not - can happen if update is waiting on some condition.
94
+ let pid = assert_matches!(
95
+ &res.jobs[0],
96
+ WorkflowActivationJob {
97
+ variant: Some(workflow_activation_job::Variant::DoUpdate(d)),
98
+ } => &d.protocol_instance_id
99
+ );
100
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
101
+ res.run_id,
102
+ vec![
103
+ UpdateResponse {
104
+ protocol_instance_id: pid.to_string(),
105
+ response: Some(update_response::Response::Accepted(())),
106
+ }
107
+ .into(),
108
+ start_timer_cmd(1, Duration::from_millis(1)),
109
+ ],
110
+ ))
111
+ .await
112
+ .unwrap();
113
+
114
+ let response = if will_fail {
115
+ UpdateResponse {
116
+ protocol_instance_id: pid.to_string(),
117
+ response: Some(update_response::Response::Rejected(
118
+ "uh oh spaghettios!".into(),
119
+ )),
120
+ }
121
+ } else {
122
+ UpdateResponse {
123
+ protocol_instance_id: pid.to_string(),
124
+ response: Some(update_response::Response::Completed("done!".into())),
125
+ }
126
+ };
127
+ let res = core.poll_workflow_activation().await.unwrap();
128
+ // Timer fires
129
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
130
+ res.run_id,
131
+ vec![
132
+ response.into(),
133
+ CompleteWorkflowExecution { result: None }.into(),
134
+ ],
135
+ ))
136
+ .await
137
+ .unwrap();
138
+ }
139
+
140
+ #[tokio::test]
141
+ async fn update_rejection() {
142
+ let mut starter = init_core_and_create_wf("update_workflow").await;
143
+ let core = starter.get_worker().await;
144
+ let client = starter.get_client().await;
145
+ let workflow_id = starter.get_task_queue().to_string();
146
+
147
+ let update_id = "some_update";
148
+ let res = core.poll_workflow_activation().await.unwrap();
149
+ // Task is completed with no commands
150
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
151
+ res.run_id.clone(),
152
+ vec![],
153
+ ))
154
+ .await
155
+ .unwrap();
156
+
157
+ // Send the update to the server
158
+ let update_task = async {
159
+ client
160
+ .update_workflow_execution(
161
+ workflow_id.to_string(),
162
+ res.run_id.to_string(),
163
+ update_id.to_string(),
164
+ WaitPolicy {
165
+ lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
166
+ },
167
+ Some("hi".into()),
168
+ )
169
+ .await
170
+ .unwrap();
171
+ };
172
+
173
+ let processing_task = async {
174
+ let res = core.poll_workflow_activation().await.unwrap();
175
+ let pid = assert_matches!(
176
+ res.jobs.as_slice(),
177
+ [WorkflowActivationJob {
178
+ variant: Some(workflow_activation_job::Variant::DoUpdate(d)),
179
+ }] => &d.protocol_instance_id
180
+ );
181
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
182
+ res.run_id,
183
+ vec![
184
+ UpdateResponse {
185
+ protocol_instance_id: pid.to_string(),
186
+ response: Some(update_response::Response::Rejected("bah humbug".into())),
187
+ }
188
+ .into(),
189
+ start_timer_cmd(1, Duration::from_millis(1)),
190
+ ],
191
+ ))
192
+ .await
193
+ .unwrap();
194
+
195
+ let res = core.poll_workflow_activation().await.unwrap();
196
+ core.complete_execution(&res.run_id).await;
197
+ };
198
+ join!(update_task, processing_task);
199
+ let history = client
200
+ .get_workflow_execution_history(workflow_id, None, vec![])
201
+ .await
202
+ .unwrap()
203
+ .history
204
+ .unwrap();
205
+ let has_update_event = history.events.iter().any(|e| {
206
+ matches!(
207
+ e.event_type(),
208
+ EventType::WorkflowExecutionUpdateAccepted | EventType::WorkflowExecutionUpdateRejected
209
+ )
210
+ });
211
+ assert!(!has_update_event);
212
+ }
213
+
214
+ #[rstest::rstest]
215
+ #[tokio::test]
216
+ async fn update_insta_complete(#[values(true, false)] accept_first: bool) {
217
+ let mut starter = init_core_and_create_wf("update_workflow").await;
218
+ let core = starter.get_worker().await;
219
+ let client = starter.get_client().await;
220
+ let workflow_id = starter.get_task_queue().to_string();
221
+
222
+ let update_id = "some_update";
223
+ let res = core.poll_workflow_activation().await.unwrap();
224
+ // Task is completed with no commands
225
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
226
+ res.run_id.clone(),
227
+ vec![],
228
+ ))
229
+ .await
230
+ .unwrap();
231
+
232
+ // Send the update to the server
233
+ let (update_task, stop_wait_update) = future::abortable(async {
234
+ client
235
+ .update_workflow_execution(
236
+ workflow_id.to_string(),
237
+ res.run_id.to_string(),
238
+ update_id.to_string(),
239
+ WaitPolicy {
240
+ lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
241
+ },
242
+ Some("hi".into()),
243
+ )
244
+ .await
245
+ .unwrap();
246
+ });
247
+
248
+ let processing_task = async {
249
+ let res = core.poll_workflow_activation().await.unwrap();
250
+ let pid = assert_matches!(
251
+ res.jobs.as_slice(),
252
+ [WorkflowActivationJob {
253
+ variant: Some(workflow_activation_job::Variant::DoUpdate(d)),
254
+ }] => &d.protocol_instance_id
255
+ );
256
+ let mut cmds = vec![
257
+ UpdateResponse {
258
+ protocol_instance_id: pid.to_string(),
259
+ response: Some(update_response::Response::Completed("done!".into())),
260
+ }
261
+ .into(),
262
+ start_timer_cmd(1, Duration::from_millis(1)),
263
+ ];
264
+ if accept_first {
265
+ cmds.insert(
266
+ 0,
267
+ UpdateResponse {
268
+ protocol_instance_id: pid.to_string(),
269
+ response: Some(update_response::Response::Accepted(())),
270
+ }
271
+ .into(),
272
+ )
273
+ };
274
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
275
+ res.run_id, cmds,
276
+ ))
277
+ .await
278
+ .unwrap();
279
+
280
+ if !accept_first {
281
+ // evicted, because lang must send accept first before completing
282
+ let res = core.poll_workflow_activation().await.unwrap();
283
+ let cause = assert_matches!(
284
+ res.jobs.as_slice(),
285
+ [WorkflowActivationJob {
286
+ variant: Some(workflow_activation_job::Variant::RemoveFromCache(c))
287
+ }] => c
288
+ );
289
+ assert_eq!(cause.reason(), EvictionReason::Nondeterminism);
290
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(res.run_id))
291
+ .await
292
+ .unwrap();
293
+ // Need to do this b/c currently server doesn't properly terminate update client call
294
+ // if workflow completes
295
+ stop_wait_update.abort();
296
+ }
297
+
298
+ let res = core.poll_workflow_activation().await.unwrap();
299
+ core.complete_execution(&res.run_id).await;
300
+ };
301
+ let _ = join!(update_task, processing_task);
302
+ }
303
+
304
+ #[tokio::test]
305
+ async fn update_complete_after_accept_without_new_task() {
306
+ let mut starter = init_core_and_create_wf("update_workflow").await;
307
+ let core = starter.get_worker().await;
308
+ let client = starter.get_client().await;
309
+ let workflow_id = starter.get_task_queue().to_string();
310
+
311
+ let update_id = "some_update";
312
+ let res = core.poll_workflow_activation().await.unwrap();
313
+ // Task is completed with no commands
314
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
315
+ res.run_id.clone(),
316
+ vec![],
317
+ ))
318
+ .await
319
+ .unwrap();
320
+
321
+ // Send the update to the server
322
+ let update_task = async {
323
+ client
324
+ .update_workflow_execution(
325
+ workflow_id.to_string(),
326
+ res.run_id.to_string(),
327
+ update_id.to_string(),
328
+ WaitPolicy {
329
+ lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
330
+ },
331
+ Some("hi".into()),
332
+ )
333
+ .await
334
+ .unwrap();
335
+ };
336
+
337
+ let act_polling = async {
338
+ let task = core.poll_activity_task().await.unwrap();
339
+ core.complete_activity_task(ActivityTaskCompletion {
340
+ task_token: task.task_token,
341
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
342
+ })
343
+ .await
344
+ .unwrap();
345
+ };
346
+
347
+ let processing_task = async {
348
+ let res = core.poll_workflow_activation().await.unwrap();
349
+ let pid = assert_matches!(
350
+ res.jobs.as_slice(),
351
+ [WorkflowActivationJob {
352
+ variant: Some(workflow_activation_job::Variant::DoUpdate(d)),
353
+ }] => &d.protocol_instance_id
354
+ );
355
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
356
+ res.run_id,
357
+ vec![
358
+ UpdateResponse {
359
+ protocol_instance_id: pid.to_string(),
360
+ response: Some(update_response::Response::Accepted(())),
361
+ }
362
+ .into(),
363
+ ScheduleLocalActivity {
364
+ seq: 1,
365
+ activity_id: "1".to_string(),
366
+ activity_type: "test_act".to_string(),
367
+ start_to_close_timeout: Some(prost_dur!(from_secs(30))),
368
+ ..Default::default()
369
+ }
370
+ .into(),
371
+ ],
372
+ ))
373
+ .await
374
+ .unwrap();
375
+
376
+ let res = core.poll_workflow_activation().await.unwrap();
377
+ assert_matches!(
378
+ res.jobs.as_slice(),
379
+ [WorkflowActivationJob {
380
+ variant: Some(workflow_activation_job::Variant::ResolveActivity(_)),
381
+ }]
382
+ );
383
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
384
+ res.run_id,
385
+ vec![
386
+ UpdateResponse {
387
+ protocol_instance_id: pid.to_string(),
388
+ response: Some(update_response::Response::Completed("done!".into())),
389
+ }
390
+ .into(),
391
+ CompleteWorkflowExecution { result: None }.into(),
392
+ ],
393
+ ))
394
+ .await
395
+ .unwrap();
396
+ };
397
+ join!(update_task, act_polling, processing_task);
398
+ }
399
+
400
+ #[tokio::test]
401
+ async fn update_speculative_wft() {
402
+ let mut starter = init_core_and_create_wf("update_workflow").await;
403
+ let core = starter.get_worker().await;
404
+ let client = starter.get_client().await;
405
+ let workflow_id = starter.get_task_queue().to_string();
406
+
407
+ let update_id = "some_update";
408
+
409
+ // Send update after the timer has fired
410
+ let barr = Barrier::new(2);
411
+ let sender_task = async {
412
+ barr.wait().await;
413
+ client
414
+ .update_workflow_execution(
415
+ workflow_id.to_string(),
416
+ "".to_string(),
417
+ update_id.to_string(),
418
+ WaitPolicy {
419
+ lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
420
+ },
421
+ Some("hi".into()),
422
+ )
423
+ .await
424
+ .unwrap();
425
+ barr.wait().await;
426
+ client
427
+ .signal_workflow_execution(
428
+ workflow_id.to_string(),
429
+ "".to_string(),
430
+ "hi".into(),
431
+ None,
432
+ None,
433
+ )
434
+ .await
435
+ .unwrap();
436
+ };
437
+
438
+ let processing_task = async {
439
+ let res = core.poll_workflow_activation().await.unwrap();
440
+ assert_matches!(
441
+ res.jobs.as_slice(),
442
+ [WorkflowActivationJob {
443
+ variant: Some(workflow_activation_job::Variant::StartWorkflow(_)),
444
+ }]
445
+ );
446
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(res.run_id))
447
+ .await
448
+ .unwrap();
449
+ barr.wait().await;
450
+
451
+ let res = core.poll_workflow_activation().await.unwrap();
452
+ let pid = assert_matches!(
453
+ res.jobs.as_slice(),
454
+ [WorkflowActivationJob {
455
+ variant: Some(workflow_activation_job::Variant::DoUpdate(d)),
456
+ }] => &d.protocol_instance_id
457
+ );
458
+ // Reject the update
459
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
460
+ res.run_id,
461
+ vec![UpdateResponse {
462
+ protocol_instance_id: pid.to_string(),
463
+ response: Some(update_response::Response::Rejected("nope!".into())),
464
+ }
465
+ .into()],
466
+ ))
467
+ .await
468
+ .unwrap();
469
+ // Send the signal
470
+ barr.wait().await;
471
+ // Without proper implementation of last-started-wft reset, we'd get stuck here b/c we'd
472
+ // skip over the signal event.
473
+ let res = core.poll_workflow_activation().await.unwrap();
474
+ assert_matches!(
475
+ res.jobs.as_slice(),
476
+ [WorkflowActivationJob {
477
+ variant: Some(workflow_activation_job::Variant::SignalWorkflow(_)),
478
+ }]
479
+ );
480
+ core.complete_execution(&res.run_id).await;
481
+ };
482
+ join!(sender_task, processing_task);
483
+ }
484
+
485
+ #[tokio::test]
486
+ async fn update_with_local_acts() {
487
+ let wf_name = "update_with_local_acts";
488
+ let mut starter = CoreWfStarter::new(wf_name);
489
+ // Short task timeout to get activities to heartbeat without taking ages
490
+ starter.workflow_options.task_timeout = Some(Duration::from_secs(1));
491
+ let mut worker = starter.worker().await;
492
+ let client = starter.get_client().await;
493
+ worker.register_wf(wf_name.to_owned(), move |ctx: WfContext| async move {
494
+ ctx.update_handler(
495
+ "update",
496
+ |_: &_, _: ()| Ok(()),
497
+ move |ctx: UpdateContext, _: ()| async move {
498
+ ctx.wf_ctx
499
+ .local_activity(LocalActivityOptions {
500
+ activity_type: "echo_activity".to_string(),
501
+ input: "hi!".as_json_payload().expect("serializes fine"),
502
+ ..Default::default()
503
+ })
504
+ .await;
505
+ Ok("hi")
506
+ },
507
+ );
508
+ let mut sig = ctx.make_signal_channel("done");
509
+ sig.next().await;
510
+ Ok(().into())
511
+ });
512
+ worker.register_activity(
513
+ "echo_activity",
514
+ |_ctx: ActContext, echo_me: String| async move {
515
+ // Sleep so we'll heartbeat
516
+ tokio::time::sleep(Duration::from_secs(3)).await;
517
+ Ok(echo_me)
518
+ },
519
+ );
520
+
521
+ let run_id = starter.start_with_worker(wf_name, &mut worker).await;
522
+ let wf_id = starter.get_task_queue().to_string();
523
+ let update = async {
524
+ // make sure update has a chance to get registered
525
+ tokio::time::sleep(Duration::from_millis(100)).await;
526
+ // do a handful at once
527
+ let updates = (1..=5).map(|_| {
528
+ client.update_workflow_execution(
529
+ wf_id.clone(),
530
+ "".to_string(),
531
+ "update".to_string(),
532
+ WaitPolicy {
533
+ lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
534
+ },
535
+ [().as_json_payload().unwrap()].into_payloads(),
536
+ )
537
+ });
538
+ for res in join_all(updates).await {
539
+ assert!(res.unwrap().outcome.unwrap().is_success());
540
+ }
541
+ client
542
+ .signal_workflow_execution(
543
+ wf_id.clone(),
544
+ "".to_string(),
545
+ "done".to_string(),
546
+ None,
547
+ None,
548
+ )
549
+ .await
550
+ .unwrap();
551
+ };
552
+ let run = async {
553
+ worker.run_until_done().await.unwrap();
554
+ };
555
+ join!(update, run);
556
+ starter
557
+ .fetch_history_and_replay(wf_id, run_id, worker.inner_mut())
558
+ .await
559
+ .unwrap();
560
+ }
561
+
562
+ #[tokio::test]
563
+ async fn update_rejection_sdk() {
564
+ let wf_name = "update_rejection_sdk";
565
+ let mut starter = CoreWfStarter::new(wf_name);
566
+ starter.worker_config.no_remote_activities(true);
567
+ let mut worker = starter.worker().await;
568
+ let client = starter.get_client().await;
569
+ worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
570
+ ctx.update_handler(
571
+ "update",
572
+ |_: &_, _: ()| Err(anyhow!("ahhhhh noooo")),
573
+ move |_: UpdateContext, _: ()| async { Ok("hi") },
574
+ );
575
+ ctx.timer(Duration::from_secs(1)).await;
576
+ Ok(().into())
577
+ });
578
+
579
+ let run_id = starter.start_with_worker(wf_name, &mut worker).await;
580
+ let wf_id = starter.get_task_queue().to_string();
581
+ let update = async {
582
+ let res = client
583
+ .update_workflow_execution(
584
+ wf_id.clone(),
585
+ "".to_string(),
586
+ "update".to_string(),
587
+ WaitPolicy {
588
+ lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
589
+ },
590
+ [().as_json_payload().unwrap()].into_payloads(),
591
+ )
592
+ .await
593
+ .unwrap();
594
+ assert!(!res.outcome.unwrap().is_success());
595
+ };
596
+ let run = async {
597
+ worker.run_until_done().await.unwrap();
598
+ };
599
+ join!(update, run);
600
+ starter
601
+ .fetch_history_and_replay(wf_id, run_id, worker.inner_mut())
602
+ .await
603
+ .unwrap();
604
+ }
605
+
606
+ #[tokio::test]
607
+ async fn update_fail_sdk() {
608
+ let wf_name = "update_fail_sdk";
609
+ let mut starter = CoreWfStarter::new(wf_name);
610
+ starter.worker_config.no_remote_activities(true);
611
+ let mut worker = starter.worker().await;
612
+ let client = starter.get_client().await;
613
+ worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
614
+ ctx.update_handler(
615
+ "update",
616
+ |_: &_, _: ()| Ok(()),
617
+ move |_: UpdateContext, _: ()| async { Err::<(), _>(anyhow!("nooooo")) },
618
+ );
619
+ ctx.timer(Duration::from_secs(1)).await;
620
+ Ok(().into())
621
+ });
622
+
623
+ let run_id = starter.start_with_worker(wf_name, &mut worker).await;
624
+ let wf_id = starter.get_task_queue().to_string();
625
+ let update = async {
626
+ let res = client
627
+ .update_workflow_execution(
628
+ wf_id.clone(),
629
+ "".to_string(),
630
+ "update".to_string(),
631
+ WaitPolicy {
632
+ lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
633
+ },
634
+ [().as_json_payload().unwrap()].into_payloads(),
635
+ )
636
+ .await
637
+ .unwrap();
638
+ assert!(!res.outcome.unwrap().is_success());
639
+ };
640
+ let run = async {
641
+ worker.run_until_done().await.unwrap();
642
+ };
643
+ join!(update, run);
644
+ starter
645
+ .fetch_history_and_replay(wf_id, run_id, worker.inner_mut())
646
+ .await
647
+ .unwrap();
648
+ }
649
+
650
+ #[tokio::test]
651
+ async fn update_timer_sequence() {
652
+ let wf_name = "update_timer_sequence";
653
+ let mut starter = CoreWfStarter::new(wf_name);
654
+ starter.worker_config.no_remote_activities(true);
655
+ let mut worker = starter.worker().await;
656
+ let client = starter.get_client().await;
657
+ worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
658
+ ctx.update_handler(
659
+ "update",
660
+ |_: &_, _: ()| Ok(()),
661
+ move |ctx: UpdateContext, _: ()| async move {
662
+ ctx.wf_ctx.timer(Duration::from_millis(1)).await;
663
+ ctx.wf_ctx.timer(Duration::from_millis(1)).await;
664
+ Ok("done")
665
+ },
666
+ );
667
+ ctx.timer(Duration::from_secs(1)).await;
668
+ Ok(().into())
669
+ });
670
+
671
+ let run_id = starter.start_with_worker(wf_name, &mut worker).await;
672
+ let wf_id = starter.get_task_queue().to_string();
673
+ let update = async {
674
+ let res = client
675
+ .update_workflow_execution(
676
+ wf_id.clone(),
677
+ "".to_string(),
678
+ "update".to_string(),
679
+ WaitPolicy {
680
+ lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
681
+ },
682
+ [().as_json_payload().unwrap()].into_payloads(),
683
+ )
684
+ .await
685
+ .unwrap();
686
+ assert!(res.outcome.unwrap().is_success());
687
+ };
688
+ let run = async {
689
+ worker.run_until_done().await.unwrap();
690
+ };
691
+ join!(update, run);
692
+ starter
693
+ .fetch_history_and_replay(wf_id, run_id, worker.inner_mut())
694
+ .await
695
+ .unwrap();
696
+ }
697
+
698
+ #[tokio::test]
699
+ async fn task_failure_during_validation() {
700
+ let wf_name = "task_failure_during_validation";
701
+ let mut starter = CoreWfStarter::new(wf_name);
702
+ starter.worker_config.no_remote_activities(true);
703
+ starter.workflow_options.task_timeout = Some(Duration::from_secs(1));
704
+ let mut worker = starter.worker().await;
705
+ let client = starter.get_client().await;
706
+ static FAILCT: AtomicUsize = AtomicUsize::new(0);
707
+ worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
708
+ ctx.update_handler(
709
+ "update",
710
+ |_: &_, _: ()| {
711
+ if FAILCT.fetch_add(1, Ordering::Relaxed) < 2 {
712
+ panic!("ahhhhhh");
713
+ }
714
+ Ok(())
715
+ },
716
+ move |_: UpdateContext, _: ()| async move { Ok("done") },
717
+ );
718
+ ctx.timer(Duration::from_secs(1)).await;
719
+ Ok(().into())
720
+ });
721
+
722
+ let run_id = starter.start_with_worker(wf_name, &mut worker).await;
723
+ let wf_id = starter.get_task_queue().to_string();
724
+ let update = async {
725
+ let res = client
726
+ .update_workflow_execution(
727
+ wf_id.clone(),
728
+ "".to_string(),
729
+ "update".to_string(),
730
+ WaitPolicy {
731
+ lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
732
+ },
733
+ [().as_json_payload().unwrap()].into_payloads(),
734
+ )
735
+ .await
736
+ .unwrap();
737
+ assert!(res.outcome.unwrap().is_success());
738
+ };
739
+ let run = async {
740
+ worker.run_until_done().await.unwrap();
741
+ };
742
+ join!(update, run);
743
+ starter
744
+ .fetch_history_and_replay(wf_id.clone(), run_id, worker.inner_mut())
745
+ .await
746
+ .unwrap();
747
+ // Verify we did not spam task failures. There should only be one.
748
+ let history = client
749
+ .get_workflow_execution_history(wf_id, None, vec![])
750
+ .await
751
+ .unwrap()
752
+ .history
753
+ .unwrap();
754
+ assert_eq!(
755
+ history
756
+ .events
757
+ .iter()
758
+ .filter(|he| he.event_type() == EventType::WorkflowTaskFailed)
759
+ .count(),
760
+ 1
761
+ );
762
+ }
763
+
764
+ #[tokio::test]
765
+ async fn task_failure_after_update() {
766
+ let wf_name = "task_failure_after_update";
767
+ let mut starter = CoreWfStarter::new(wf_name);
768
+ starter.worker_config.no_remote_activities(true);
769
+ starter.workflow_options.task_timeout = Some(Duration::from_secs(1));
770
+ let mut worker = starter.worker().await;
771
+ let client = starter.get_client().await;
772
+ static FAILCT: AtomicUsize = AtomicUsize::new(0);
773
+ worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
774
+ ctx.update_handler(
775
+ "update",
776
+ |_: &_, _: ()| Ok(()),
777
+ move |_: UpdateContext, _: ()| async move { Ok("done") },
778
+ );
779
+ ctx.timer(Duration::from_millis(1)).await;
780
+ if FAILCT.fetch_add(1, Ordering::Relaxed) < 1 {
781
+ panic!("ahhhhhh");
782
+ }
783
+ Ok(().into())
784
+ });
785
+
786
+ let run_id = starter.start_with_worker(wf_name, &mut worker).await;
787
+ let wf_id = starter.get_task_queue().to_string();
788
+ let update = async {
789
+ let res = client
790
+ .update_workflow_execution(
791
+ wf_id.clone(),
792
+ "".to_string(),
793
+ "update".to_string(),
794
+ WaitPolicy {
795
+ lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
796
+ },
797
+ [().as_json_payload().unwrap()].into_payloads(),
798
+ )
799
+ .await
800
+ .unwrap();
801
+ assert!(res.outcome.unwrap().is_success());
802
+ };
803
+ let run = async {
804
+ worker.run_until_done().await.unwrap();
805
+ };
806
+ join!(update, run);
807
+ starter
808
+ .fetch_history_and_replay(wf_id.clone(), run_id, worker.inner_mut())
809
+ .await
810
+ .unwrap();
811
+ }
812
+
813
+ #[tokio::test]
814
+ async fn worker_restarted_in_middle_of_update() {
815
+ let wf_name = "worker_restarted_in_middle_of_update";
816
+ let mut starter = CoreWfStarter::new(wf_name);
817
+ let mut worker = starter.worker().await;
818
+ let client = starter.get_client().await;
819
+
820
+ lazy_static! {
821
+ static ref BARR: Barrier = Barrier::new(2);
822
+ }
823
+ static ACT_RAN: AtomicBool = AtomicBool::new(false);
824
+ worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
825
+ ctx.update_handler(
826
+ "update",
827
+ |_: &_, _: ()| Ok(()),
828
+ move |ctx: UpdateContext, _: ()| async move {
829
+ ctx.wf_ctx
830
+ .activity(ActivityOptions {
831
+ activity_type: "blocks".to_string(),
832
+ input: "hi!".as_json_payload().expect("serializes fine"),
833
+ start_to_close_timeout: Some(Duration::from_secs(2)),
834
+ ..Default::default()
835
+ })
836
+ .await;
837
+ Ok(())
838
+ },
839
+ );
840
+ let mut sig = ctx.make_signal_channel("done");
841
+ sig.next().await;
842
+ Ok(().into())
843
+ });
844
+ worker.register_activity("blocks", |_ctx: ActContext, echo_me: String| async move {
845
+ BARR.wait().await;
846
+ if !ACT_RAN.fetch_or(true, Ordering::Relaxed) {
847
+ // On first run fail the task so we'll get retried on the new worker
848
+ bail!("Fail first time");
849
+ }
850
+ Ok(echo_me)
851
+ });
852
+
853
+ let run_id = starter.start_with_worker(wf_name, &mut worker).await;
854
+ let wf_id = starter.get_task_queue().to_string();
855
+ let update = async {
856
+ let res = client
857
+ .update_workflow_execution(
858
+ wf_id.clone(),
859
+ "".to_string(),
860
+ "update".to_string(),
861
+ WaitPolicy {
862
+ lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
863
+ },
864
+ [().as_json_payload().unwrap()].into_payloads(),
865
+ )
866
+ .await
867
+ .unwrap();
868
+ assert!(res.outcome.unwrap().is_success());
869
+ client
870
+ .signal_workflow_execution(
871
+ wf_id.clone(),
872
+ "".to_string(),
873
+ "done".to_string(),
874
+ None,
875
+ None,
876
+ )
877
+ .await
878
+ .unwrap();
879
+ };
880
+ let core_worker = starter.get_worker().await;
881
+ let mut second_starter = starter.clone_no_worker();
882
+ let stopper = async {
883
+ // Wait for the activity to start
884
+ BARR.wait().await;
885
+ core_worker.initiate_shutdown();
886
+ // Allow it to start again, the second time
887
+ BARR.wait().await;
888
+ // Poke the workflow off the sticky queue to get it to complete faster than WFT timeout
889
+ client
890
+ .reset_sticky_task_queue(wf_id.clone(), run_id.clone())
891
+ .await
892
+ .unwrap();
893
+ };
894
+ let run = async {
895
+ // This run attempt will get shut down
896
+ worker.inner_mut().run().await.unwrap();
897
+ // Start up a new worker
898
+ let new_worker = second_starter.get_worker().await;
899
+ // Replace with new core and run again
900
+ worker.inner_mut().with_new_core_worker(new_worker);
901
+ worker.run_until_done().await.unwrap();
902
+ };
903
+ join!(update, run, stopper);
904
+ starter
905
+ .fetch_history_and_replay(wf_id, run_id, worker.inner_mut())
906
+ .await
907
+ .unwrap();
908
+ }