@temporalio/core-bridge 1.5.2 → 1.7.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 (194) hide show
  1. package/Cargo.lock +304 -112
  2. package/lib/index.d.ts +8 -6
  3. package/lib/index.js.map +1 -1
  4. package/package.json +9 -4
  5. package/releases/aarch64-apple-darwin/index.node +0 -0
  6. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  7. package/releases/x86_64-apple-darwin/index.node +0 -0
  8. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  9. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  10. package/sdk-core/.buildkite/docker/Dockerfile +2 -2
  11. package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
  12. package/sdk-core/.buildkite/pipeline.yml +2 -4
  13. package/sdk-core/.cargo/config.toml +5 -2
  14. package/sdk-core/.github/workflows/heavy.yml +29 -0
  15. package/sdk-core/Cargo.toml +1 -1
  16. package/sdk-core/README.md +20 -10
  17. package/sdk-core/client/src/lib.rs +215 -39
  18. package/sdk-core/client/src/metrics.rs +17 -8
  19. package/sdk-core/client/src/raw.rs +4 -4
  20. package/sdk-core/client/src/retry.rs +32 -20
  21. package/sdk-core/core/Cargo.toml +25 -12
  22. package/sdk-core/core/src/abstractions/take_cell.rs +28 -0
  23. package/sdk-core/core/src/abstractions.rs +204 -14
  24. package/sdk-core/core/src/core_tests/activity_tasks.rs +143 -50
  25. package/sdk-core/core/src/core_tests/child_workflows.rs +6 -5
  26. package/sdk-core/core/src/core_tests/determinism.rs +165 -2
  27. package/sdk-core/core/src/core_tests/local_activities.rs +431 -43
  28. package/sdk-core/core/src/core_tests/queries.rs +34 -16
  29. package/sdk-core/core/src/core_tests/workers.rs +8 -5
  30. package/sdk-core/core/src/core_tests/workflow_tasks.rs +588 -55
  31. package/sdk-core/core/src/ephemeral_server/mod.rs +113 -12
  32. package/sdk-core/core/src/internal_flags.rs +155 -0
  33. package/sdk-core/core/src/lib.rs +16 -9
  34. package/sdk-core/core/src/protosext/mod.rs +1 -1
  35. package/sdk-core/core/src/replay/mod.rs +16 -27
  36. package/sdk-core/core/src/telemetry/log_export.rs +1 -1
  37. package/sdk-core/core/src/telemetry/metrics.rs +69 -35
  38. package/sdk-core/core/src/telemetry/mod.rs +60 -21
  39. package/sdk-core/core/src/telemetry/prometheus_server.rs +19 -13
  40. package/sdk-core/core/src/test_help/mod.rs +73 -14
  41. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +119 -160
  42. package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  43. package/sdk-core/core/src/worker/activities/local_activities.rs +379 -129
  44. package/sdk-core/core/src/worker/activities.rs +350 -175
  45. package/sdk-core/core/src/worker/client/mocks.rs +22 -2
  46. package/sdk-core/core/src/worker/client.rs +18 -2
  47. package/sdk-core/core/src/worker/mod.rs +183 -64
  48. package/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
  49. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -5
  50. package/sdk-core/core/src/worker/workflow/history_update.rs +916 -277
  51. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +216 -183
  52. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +9 -12
  53. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +7 -9
  54. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +160 -87
  55. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +13 -14
  56. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +7 -9
  57. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +14 -17
  58. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +242 -110
  59. package/sdk-core/core/src/worker/workflow/machines/mod.rs +27 -19
  60. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +9 -11
  61. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +321 -206
  62. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +13 -18
  63. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +20 -29
  64. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
  65. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +257 -51
  66. package/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +6 -17
  67. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +310 -150
  68. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +17 -20
  69. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +31 -15
  70. package/sdk-core/core/src/worker/workflow/managed_run.rs +1052 -380
  71. package/sdk-core/core/src/worker/workflow/mod.rs +598 -390
  72. package/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
  73. package/sdk-core/core/src/worker/workflow/wft_extraction.rs +137 -0
  74. package/sdk-core/core/src/worker/workflow/wft_poller.rs +1 -4
  75. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
  76. package/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
  77. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +469 -718
  78. package/sdk-core/core-api/Cargo.toml +2 -1
  79. package/sdk-core/core-api/src/errors.rs +1 -34
  80. package/sdk-core/core-api/src/lib.rs +19 -9
  81. package/sdk-core/core-api/src/telemetry.rs +4 -6
  82. package/sdk-core/core-api/src/worker.rs +19 -1
  83. package/sdk-core/etc/deps.svg +115 -140
  84. package/sdk-core/etc/regen-depgraph.sh +5 -0
  85. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +86 -61
  86. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +29 -71
  87. package/sdk-core/histories/ends_empty_wft_complete.bin +0 -0
  88. package/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
  89. package/sdk-core/histories/old_change_marker_format.bin +0 -0
  90. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +2 -1
  91. package/sdk-core/protos/api_upstream/Makefile +6 -6
  92. package/sdk-core/protos/api_upstream/build/go.mod +7 -0
  93. package/sdk-core/protos/api_upstream/build/go.sum +5 -0
  94. package/sdk-core/protos/api_upstream/build/tools.go +29 -0
  95. package/sdk-core/protos/api_upstream/go.mod +6 -0
  96. package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +9 -2
  97. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +7 -26
  98. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +13 -2
  99. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +3 -2
  100. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +3 -7
  101. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +3 -2
  102. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +8 -8
  103. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +25 -2
  104. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +2 -2
  105. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +2 -2
  106. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +2 -2
  107. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +2 -2
  108. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +2 -2
  109. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +24 -19
  110. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +2 -2
  111. package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +2 -2
  112. package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -2
  113. package/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +2 -2
  114. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +49 -26
  115. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +4 -2
  116. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +5 -2
  117. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +2 -2
  118. package/sdk-core/protos/api_upstream/temporal/api/protocol/v1/message.proto +57 -0
  119. package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +2 -2
  120. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +2 -2
  121. package/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +2 -2
  122. package/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  123. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -2
  124. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +71 -6
  125. package/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +2 -2
  126. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +2 -2
  127. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +64 -28
  128. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +4 -4
  129. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +7 -8
  130. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +10 -7
  131. package/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +19 -30
  132. package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  133. package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  134. package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +8 -0
  135. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +67 -60
  136. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +85 -84
  137. package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +9 -3
  138. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +2 -2
  139. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +2 -2
  140. package/sdk-core/sdk/Cargo.toml +5 -4
  141. package/sdk-core/sdk/src/lib.rs +108 -26
  142. package/sdk-core/sdk/src/workflow_context/options.rs +7 -1
  143. package/sdk-core/sdk/src/workflow_context.rs +24 -17
  144. package/sdk-core/sdk/src/workflow_future.rs +16 -15
  145. package/sdk-core/sdk-core-protos/Cargo.toml +5 -2
  146. package/sdk-core/sdk-core-protos/build.rs +36 -2
  147. package/sdk-core/sdk-core-protos/src/history_builder.rs +138 -106
  148. package/sdk-core/sdk-core-protos/src/history_info.rs +10 -1
  149. package/sdk-core/sdk-core-protos/src/lib.rs +272 -87
  150. package/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
  151. package/sdk-core/test-utils/Cargo.toml +3 -1
  152. package/sdk-core/test-utils/src/canned_histories.rs +106 -296
  153. package/sdk-core/test-utils/src/histfetch.rs +1 -1
  154. package/sdk-core/test-utils/src/lib.rs +82 -23
  155. package/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
  156. package/sdk-core/test-utils/src/workflows.rs +29 -0
  157. package/sdk-core/tests/fuzzy_workflow.rs +130 -0
  158. package/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
  159. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  160. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +10 -5
  161. package/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
  162. package/sdk-core/tests/integ_tests/polling_tests.rs +4 -47
  163. package/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
  164. package/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
  165. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +161 -72
  166. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  167. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
  168. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +80 -3
  169. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
  170. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
  171. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +94 -200
  172. package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +2 -4
  173. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +34 -28
  174. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +76 -7
  175. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  176. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
  177. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
  178. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
  179. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +7 -8
  180. package/sdk-core/tests/integ_tests/workflow_tests.rs +13 -14
  181. package/sdk-core/tests/main.rs +3 -13
  182. package/sdk-core/tests/runner.rs +75 -36
  183. package/sdk-core/tests/wf_input_replay.rs +32 -0
  184. package/src/conversions.rs +14 -8
  185. package/src/runtime.rs +9 -8
  186. package/ts/index.ts +8 -6
  187. package/sdk-core/bridge-ffi/Cargo.toml +0 -24
  188. package/sdk-core/bridge-ffi/LICENSE.txt +0 -23
  189. package/sdk-core/bridge-ffi/build.rs +0 -25
  190. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +0 -224
  191. package/sdk-core/bridge-ffi/src/lib.rs +0 -746
  192. package/sdk-core/bridge-ffi/src/wrappers.rs +0 -221
  193. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +0 -210
  194. package/sdk-core/sdk/src/conversions.rs +0 -8
@@ -4,7 +4,7 @@ use crate::{
4
4
  build_fake_worker, build_mock_pollers, canned_histories, gen_assert_and_reply,
5
5
  mock_manual_poller, mock_poller, mock_poller_from_resps, mock_worker, poll_and_reply,
6
6
  single_hist_mock_sg, test_worker_cfg, MockPollCfg, MockWorkerInputs, MocksHolder,
7
- ResponseType, WorkflowCachingPolicy, TEST_Q,
7
+ QueueResponse, ResponseType, WorkerExt, WorkflowCachingPolicy, TEST_Q,
8
8
  },
9
9
  worker::client::mocks::{mock_manual_workflow_client, mock_workflow_client},
10
10
  ActivityHeartbeat, Worker, WorkerConfigBuilder,
@@ -13,7 +13,8 @@ use futures::FutureExt;
13
13
  use itertools::Itertools;
14
14
  use std::{
15
15
  cell::RefCell,
16
- collections::{hash_map::Entry, HashMap, VecDeque},
16
+ collections::{hash_map::Entry, HashMap, HashSet, VecDeque},
17
+ future,
17
18
  rc::Rc,
18
19
  sync::{
19
20
  atomic::{AtomicUsize, Ordering},
@@ -23,15 +24,17 @@ use std::{
23
24
  };
24
25
  use temporal_client::WorkflowOptions;
25
26
  use temporal_sdk::{ActivityOptions, WfContext};
26
- use temporal_sdk_core_api::{errors::CompleteActivityError, Worker as WorkerTrait};
27
- use temporal_sdk_core_protos::temporal::api::command::v1::ScheduleActivityTaskCommandAttributes;
27
+ use temporal_sdk_core_api::{
28
+ errors::{CompleteActivityError, PollActivityError},
29
+ Worker as WorkerTrait,
30
+ };
28
31
  use temporal_sdk_core_protos::{
29
32
  coresdk::{
30
33
  activity_result::{
31
34
  activity_execution_result, activity_resolution, ActivityExecutionResult,
32
35
  ActivityResolution, Success,
33
36
  },
34
- activity_task::{activity_task, ActivityTask},
37
+ activity_task::{activity_task, ActivityCancelReason, ActivityTask, Cancel},
35
38
  workflow_activation::{workflow_activation_job, ResolveActivity, WorkflowActivationJob},
36
39
  workflow_commands::{
37
40
  ActivityCancellationType, CompleteWorkflowExecution, RequestCancelActivity,
@@ -41,8 +44,11 @@ use temporal_sdk_core_protos::{
41
44
  ActivityTaskCompletion,
42
45
  },
43
46
  temporal::api::{
44
- command::v1::command::Attributes,
47
+ command::v1::{command::Attributes, ScheduleActivityTaskCommandAttributes},
45
48
  enums::v1::EventType,
49
+ history::v1::{
50
+ history_event::Attributes as EventAttributes, ActivityTaskScheduledEventAttributes,
51
+ },
46
52
  workflowservice::v1::{
47
53
  PollActivityTaskQueueResponse, RecordActivityTaskHeartbeatResponse,
48
54
  RespondActivityTaskCanceledResponse, RespondActivityTaskCompletedResponse,
@@ -53,11 +59,10 @@ use temporal_sdk_core_protos::{
53
59
  };
54
60
  use temporal_sdk_core_test_utils::{fanout_tasks, start_timer_cmd, TestWorker};
55
61
  use tokio::{sync::Barrier, time::sleep};
62
+ use tokio_util::sync::CancellationToken;
56
63
 
57
- #[tokio::test]
58
- async fn max_activities_respected() {
59
- let _task_q = "q";
60
- let mut tasks = VecDeque::from(vec![
64
+ fn three_tasks() -> VecDeque<PollActivityTaskQueueResponse> {
65
+ VecDeque::from(vec![
61
66
  PollActivityTaskQueueResponse {
62
67
  task_token: vec![1],
63
68
  activity_id: "act1".to_string(),
@@ -73,7 +78,13 @@ async fn max_activities_respected() {
73
78
  activity_id: "act3".to_string(),
74
79
  ..Default::default()
75
80
  },
76
- ]);
81
+ ])
82
+ }
83
+
84
+ #[tokio::test]
85
+ async fn max_activities_respected() {
86
+ let _task_q = "q";
87
+ let mut tasks = three_tasks();
77
88
  let mut mock_client = mock_workflow_client();
78
89
  mock_client
79
90
  .expect_poll_activity_task()
@@ -122,7 +133,7 @@ async fn activity_not_found_returns_ok() {
122
133
  })
123
134
  .await
124
135
  .unwrap();
125
- core.shutdown().await;
136
+ core.drain_activity_poller_and_shutdown().await;
126
137
  }
127
138
 
128
139
  #[tokio::test]
@@ -218,12 +229,14 @@ async fn heartbeats_report_cancels_only_once() {
218
229
  })
219
230
  .await
220
231
  .unwrap();
221
- core.shutdown().await;
232
+ core.drain_activity_poller_and_shutdown().await;
222
233
  }
223
234
 
224
235
  #[tokio::test]
225
236
  async fn activity_cancel_interrupts_poll() {
226
237
  let mut mock_poller = mock_manual_poller();
238
+ let shutdown_token = CancellationToken::new();
239
+ let shutdown_token_clone = shutdown_token.clone();
227
240
  let mut poll_resps = VecDeque::from(vec![
228
241
  async {
229
242
  Some(Ok(PollActivityTaskQueueResponse {
@@ -238,10 +251,15 @@ async fn activity_cancel_interrupts_poll() {
238
251
  Some(Ok(Default::default()))
239
252
  }
240
253
  .boxed(),
254
+ async move {
255
+ shutdown_token.cancelled().await;
256
+ None
257
+ }
258
+ .boxed(),
241
259
  ]);
242
260
  mock_poller
243
261
  .expect_poll()
244
- .times(2)
262
+ .times(3)
245
263
  .returning(move || poll_resps.pop_front().unwrap());
246
264
 
247
265
  let mut mock_client = mock_manual_workflow_client();
@@ -290,11 +308,12 @@ async fn activity_cancel_interrupts_poll() {
290
308
  }
291
309
  ).await.unwrap();
292
310
  last_finisher.store(2, Ordering::SeqCst);
311
+ shutdown_token_clone.cancel();
293
312
  }
294
313
  };
295
314
  // So that we know we blocked
296
315
  assert_eq!(last_finisher.load(Ordering::Acquire), 2);
297
- core.shutdown().await;
316
+ core.drain_activity_poller_and_shutdown().await;
298
317
  }
299
318
 
300
319
  #[tokio::test]
@@ -343,13 +362,10 @@ async fn many_concurrent_heartbeat_cancels() {
343
362
  })
344
363
  .collect::<Vec<_>>(),
345
364
  );
346
- // Because the mock is so fast, it's possible it can return before the cancel channel in
347
- // the activity task poll selector. So, the final poll when there are no more tasks must
348
- // take a while.
349
365
  poll_resps.push_back(
350
366
  async {
351
- sleep(Duration::from_secs(10)).await;
352
- unreachable!("Long poll")
367
+ future::pending::<()>().await;
368
+ unreachable!()
353
369
  }
354
370
  .boxed(),
355
371
  );
@@ -432,7 +448,7 @@ async fn many_concurrent_heartbeat_cancels() {
432
448
  })
433
449
  .await;
434
450
 
435
- worker.shutdown().await;
451
+ worker.drain_activity_poller_and_shutdown().await;
436
452
  }
437
453
 
438
454
  #[tokio::test]
@@ -484,7 +500,7 @@ async fn activity_timeout_no_double_resolve() {
484
500
  )
485
501
  .await;
486
502
 
487
- core.shutdown().await;
503
+ core.drain_pollers_and_shutdown().await;
488
504
  }
489
505
 
490
506
  #[tokio::test]
@@ -530,7 +546,7 @@ async fn can_heartbeat_acts_during_shutdown() {
530
546
  })
531
547
  .await
532
548
  .unwrap();
533
- shutdown_fut.await;
549
+ core.drain_activity_poller_and_shutdown().await;
534
550
  }
535
551
 
536
552
  /// Verifies that if a user has tried to record a heartbeat and then immediately after failed the
@@ -581,7 +597,7 @@ async fn complete_act_with_fail_flushes_heartbeat() {
581
597
  })
582
598
  .await
583
599
  .unwrap();
584
- core.shutdown().await;
600
+ core.drain_activity_poller_and_shutdown().await;
585
601
 
586
602
  // Verify the last seen call to record a heartbeat had the last detail payload
587
603
  let last_seen_payload = &last_seen_payload.take().unwrap().payloads[0];
@@ -654,6 +670,7 @@ async fn no_eager_activities_requested_when_worker_options_disable_remote_activi
654
670
  Ok(RespondWorkflowTaskCompletedResponse {
655
671
  workflow_task: None,
656
672
  activity_tasks: vec![],
673
+ reset_history_event_id: 0,
657
674
  })
658
675
  });
659
676
  let mut mock = single_hist_mock_sg(wfid, t, [1], mock, true);
@@ -686,7 +703,7 @@ async fn no_eager_activities_requested_when_worker_options_disable_remote_activi
686
703
  .await
687
704
  .unwrap();
688
705
 
689
- core.shutdown().await;
706
+ core.drain_pollers_and_shutdown().await;
690
707
 
691
708
  assert_eq!(num_eager_requested.load(Ordering::Relaxed), 0);
692
709
  }
@@ -703,7 +720,7 @@ async fn activity_tasks_from_completion_are_delivered() {
703
720
  t.add_by_type(EventType::WorkflowExecutionStarted);
704
721
  t.add_full_wf_task();
705
722
  let act_same_queue_scheduled_ids = (1..4)
706
- .map(|i| t.add_activity_task_scheduled(format!("act_id_{}_same_queue", i)))
723
+ .map(|i| t.add_activity_task_scheduled(format!("act_id_{i}_same_queue")))
707
724
  .collect_vec();
708
725
  t.add_activity_task_scheduled("act_id_same_queue_not_eager");
709
726
  t.add_activity_task_scheduled("act_id_different_queue");
@@ -742,21 +759,19 @@ async fn activity_tasks_from_completion_are_delivered() {
742
759
  activity_tasks: (1..4)
743
760
  .map(|i| PollActivityTaskQueueResponse {
744
761
  task_token: vec![i],
745
- activity_id: format!("act_id_{}_same_queue", i),
762
+ activity_id: format!("act_id_{i}_same_queue"),
746
763
  ..Default::default()
747
764
  })
748
765
  .collect_vec(),
766
+ reset_history_event_id: 0,
749
767
  })
750
768
  });
751
769
  mock.expect_complete_activity_task()
752
770
  .times(3)
753
771
  .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
754
772
  let mut mock = single_hist_mock_sg(wfid, t, [1], mock, true);
755
- let mut mock_poller = mock_manual_poller();
756
- mock_poller
757
- .expect_poll()
758
- .returning(|| futures::future::pending().boxed());
759
- mock.set_act_poller(Box::new(mock_poller));
773
+ let act_tasks: Vec<QueueResponse<PollActivityTaskQueueResponse>> = vec![];
774
+ mock.set_act_poller(mock_poller_from_resps(act_tasks));
760
775
  mock.worker_cfg(|wc| wc.max_cached_workflows = 2);
761
776
  let core = mock_worker(mock);
762
777
 
@@ -766,7 +781,7 @@ async fn activity_tasks_from_completion_are_delivered() {
766
781
  .map(|seq| {
767
782
  ScheduleActivity {
768
783
  seq,
769
- activity_id: format!("act_id_{}_same_queue", seq),
784
+ activity_id: format!("act_id_{seq}_same_queue"),
770
785
  task_queue: TEST_Q.to_string(),
771
786
  cancellation_type: ActivityCancellationType::TryCancel as i32,
772
787
  ..Default::default()
@@ -815,7 +830,7 @@ async fn activity_tasks_from_completion_are_delivered() {
815
830
  .unwrap();
816
831
  }
817
832
 
818
- core.shutdown().await;
833
+ core.drain_pollers_and_shutdown().await;
819
834
 
820
835
  // Verify only a single eager activity was scheduled (the one on our worker's task queue)
821
836
  assert_eq!(num_eager_requested.load(Ordering::Relaxed), 3);
@@ -827,11 +842,23 @@ async fn activity_tasks_from_completion_reserve_slots() {
827
842
  let mut t = TestHistoryBuilder::default();
828
843
  t.add_by_type(EventType::WorkflowExecutionStarted);
829
844
  t.add_full_wf_task();
830
- let schedid = t.add_activity_task_scheduled("1");
845
+ let schedid = t.add(EventAttributes::ActivityTaskScheduledEventAttributes(
846
+ ActivityTaskScheduledEventAttributes {
847
+ activity_id: "1".to_string(),
848
+ activity_type: Some("act1".into()),
849
+ ..Default::default()
850
+ },
851
+ ));
831
852
  let startid = t.add_activity_task_started(schedid);
832
853
  t.add_activity_task_completed(schedid, startid, b"hi".into());
833
854
  t.add_full_wf_task();
834
- let schedid = t.add_activity_task_scheduled("2");
855
+ let schedid = t.add(EventAttributes::ActivityTaskScheduledEventAttributes(
856
+ ActivityTaskScheduledEventAttributes {
857
+ activity_id: "2".to_string(),
858
+ activity_type: Some("act2".into()),
859
+ ..Default::default()
860
+ },
861
+ ));
835
862
  let startid = t.add_activity_task_started(schedid);
836
863
  t.add_activity_task_completed(schedid, startid, b"hi".into());
837
864
  t.add_full_wf_task();
@@ -901,19 +928,25 @@ async fn activity_tasks_from_completion_reserve_slots() {
901
928
  // First poll for activities twice, occupying both slots
902
929
  let at1 = core.poll_activity_task().await.unwrap();
903
930
  let at2 = core.poll_activity_task().await.unwrap();
904
-
905
- worker.register_wf(DEFAULT_WORKFLOW_TYPE, move |ctx: WfContext| async move {
906
- ctx.activity(ActivityOptions {
907
- activity_type: "act1".to_string(),
908
- ..Default::default()
909
- })
910
- .await;
911
- ctx.activity(ActivityOptions {
912
- activity_type: "act2".to_string(),
913
- ..Default::default()
914
- })
915
- .await;
916
- Ok(().into())
931
+ let workflow_complete_token = CancellationToken::new();
932
+ let workflow_complete_token_clone = workflow_complete_token.clone();
933
+
934
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, move |ctx: WfContext| {
935
+ let complete_token = workflow_complete_token.clone();
936
+ async move {
937
+ ctx.activity(ActivityOptions {
938
+ activity_type: "act1".to_string(),
939
+ ..Default::default()
940
+ })
941
+ .await;
942
+ ctx.activity(ActivityOptions {
943
+ activity_type: "act2".to_string(),
944
+ ..Default::default()
945
+ })
946
+ .await;
947
+ complete_token.cancel();
948
+ Ok(().into())
949
+ }
917
950
  });
918
951
 
919
952
  worker
@@ -940,6 +973,13 @@ async fn activity_tasks_from_completion_reserve_slots() {
940
973
  .await
941
974
  .unwrap();
942
975
  barr.wait().await;
976
+ // Wait for workflow to complete in order for all eager activities to be requested before shutting down.
977
+ // After shutdown, no eager activities slots can be allocated.
978
+ workflow_complete_token_clone.cancelled().await;
979
+ core.initiate_shutdown();
980
+ // Even though this test requests eager activity tasks, none are returned in poll responses.
981
+ let err = core.poll_activity_task().await.unwrap_err();
982
+ assert_matches!(err, PollActivityError::ShutDown);
943
983
  };
944
984
  // This wf poll should *not* set the flag that it wants tasks back since both slots are
945
985
  // occupied
@@ -973,7 +1013,7 @@ async fn retryable_net_error_exhaustion_is_nonfatal() {
973
1013
  })
974
1014
  .await
975
1015
  .unwrap();
976
- core.shutdown().await;
1016
+ core.drain_activity_poller_and_shutdown().await;
977
1017
  }
978
1018
 
979
1019
  #[tokio::test]
@@ -1011,3 +1051,56 @@ async fn cant_complete_activity_with_unset_result_payload() {
1011
1051
  Err(CompleteActivityError::MalformedActivityCompletion { .. })
1012
1052
  )
1013
1053
  }
1054
+
1055
+ #[tokio::test]
1056
+ async fn graceful_shutdown() {
1057
+ let _task_q = "q";
1058
+ let mut tasks = three_tasks();
1059
+ let mut mock_client = mock_workflow_client();
1060
+ mock_client
1061
+ .expect_poll_activity_task()
1062
+ .times(3)
1063
+ .returning(move |_, _| Ok(tasks.pop_front().unwrap()));
1064
+ // They shall all be reported as failed
1065
+ mock_client
1066
+ .expect_fail_activity_task()
1067
+ .times(3)
1068
+ .returning(|_, _| Ok(Default::default()));
1069
+
1070
+ let worker = Worker::new_test(
1071
+ test_worker_cfg()
1072
+ .graceful_shutdown_period(Duration::from_millis(500))
1073
+ .build()
1074
+ .unwrap(),
1075
+ mock_client,
1076
+ );
1077
+
1078
+ let _1 = worker.poll_activity_task().await.unwrap();
1079
+ let _2 = worker.poll_activity_task().await.unwrap();
1080
+ let _3 = worker.poll_activity_task().await.unwrap();
1081
+
1082
+ worker.initiate_shutdown();
1083
+ let expected_tts = HashSet::from([vec![1], vec![2], vec![3]]);
1084
+ let mut seen_tts = HashSet::new();
1085
+ for _ in 1..=3 {
1086
+ let cancel = worker.poll_activity_task().await.unwrap();
1087
+ assert_matches!(
1088
+ cancel.variant,
1089
+ Some(activity_task::Variant::Cancel(Cancel {
1090
+ reason: r
1091
+ })) if r == ActivityCancelReason::WorkerShutdown as i32
1092
+ );
1093
+ seen_tts.insert(cancel.task_token);
1094
+ }
1095
+ assert_eq!(expected_tts, seen_tts);
1096
+ for tt in seen_tts {
1097
+ worker
1098
+ .complete_activity_task(ActivityTaskCompletion {
1099
+ task_token: tt,
1100
+ result: Some(ActivityExecutionResult::cancel_from_details(None)),
1101
+ })
1102
+ .await
1103
+ .unwrap();
1104
+ }
1105
+ worker.drain_pollers_and_shutdown().await;
1106
+ }
@@ -88,11 +88,12 @@ async fn parent_cancels_child_wf(ctx: WfContext) -> WorkflowResult<()> {
88
88
  .await
89
89
  .into_started()
90
90
  .expect("Child should get started");
91
- let cancel_fut = start_res.cancel(&ctx);
92
- let resfut = start_res.result();
93
- let (cancel_res, res) = join!(cancel_fut, resfut);
94
- cancel_res.expect("cancel result is ok");
95
- let stat = res.status.expect("child wf result is ok");
91
+ start_res.cancel(&ctx);
92
+ let stat = start_res
93
+ .result()
94
+ .await
95
+ .status
96
+ .expect("child wf result is ok");
96
97
  assert_matches!(stat, child_workflow_result::Status::Cancelled(_));
97
98
  Ok(().into())
98
99
  }
@@ -1,4 +1,5 @@
1
1
  use crate::{
2
+ internal_flags::CoreInternalFlags,
2
3
  replay::DEFAULT_WORKFLOW_TYPE,
3
4
  test_help::{canned_histories, mock_sdk, mock_sdk_cfg, MockPollCfg, ResponseType},
4
5
  worker::client::mocks::mock_workflow_client,
@@ -8,8 +9,13 @@ use std::{
8
9
  time::Duration,
9
10
  };
10
11
  use temporal_client::WorkflowOptions;
11
- use temporal_sdk::{WfContext, WorkflowResult};
12
- use temporal_sdk_core_protos::temporal::api::enums::v1::WorkflowTaskFailedCause;
12
+ use temporal_sdk::{
13
+ ActivityOptions, ChildWorkflowOptions, LocalActivityOptions, WfContext, WorkflowResult,
14
+ };
15
+ use temporal_sdk_core_protos::{
16
+ temporal::api::{enums::v1::WorkflowTaskFailedCause, failure::v1::Failure},
17
+ DEFAULT_ACTIVITY_TYPE,
18
+ };
13
19
 
14
20
  static DID_FAIL: AtomicBool = AtomicBool::new(false);
15
21
  pub async fn timer_wf_fails_once(ctx: WfContext) -> WorkflowResult<()> {
@@ -105,3 +111,160 @@ async fn test_wf_task_rejected_properly_due_to_nondeterminism(#[case] use_cache:
105
111
  // timer and proceed without restarting
106
112
  assert_eq!(2, started_count.load(Ordering::Relaxed));
107
113
  }
114
+
115
+ #[rstest::rstest]
116
+ #[tokio::test]
117
+ async fn activity_id_or_type_change_is_nondeterministic(
118
+ #[values(true, false)] use_cache: bool,
119
+ #[values(true, false)] id_change: bool,
120
+ #[values(true, false)] local_act: bool,
121
+ ) {
122
+ let wf_id = "fakeid";
123
+ let wf_type = DEFAULT_WORKFLOW_TYPE;
124
+ let mut t = if local_act {
125
+ canned_histories::single_local_activity("1")
126
+ } else {
127
+ canned_histories::single_activity("1")
128
+ };
129
+ t.set_flags_first_wft(&[CoreInternalFlags::IdAndTypeDeterminismChecks as u32], &[]);
130
+ let mock = mock_workflow_client();
131
+ let mut mh = MockPollCfg::from_resp_batches(
132
+ wf_id,
133
+ t,
134
+ // Two polls are needed, since the first will fail
135
+ [ResponseType::AllHistory, ResponseType::AllHistory],
136
+ mock,
137
+ );
138
+ // We should see one wft failure which has nondeterminism cause
139
+ mh.num_expected_fails = 1;
140
+ mh.expect_fail_wft_matcher = Box::new(move |_, cause, f| {
141
+ let should_contain = if id_change {
142
+ "does not match activity id"
143
+ } else {
144
+ "does not match activity type"
145
+ };
146
+ matches!(cause, WorkflowTaskFailedCause::NonDeterministicError)
147
+ && matches!(f, Some(Failure {
148
+ message,
149
+ ..
150
+ }) if message.contains(should_contain))
151
+ });
152
+ let mut worker = mock_sdk_cfg(mh, |cfg| {
153
+ if use_cache {
154
+ cfg.max_cached_workflows = 2;
155
+ }
156
+ });
157
+
158
+ worker.register_wf(wf_type.to_owned(), move |ctx: WfContext| async move {
159
+ if local_act {
160
+ ctx.local_activity(if id_change {
161
+ LocalActivityOptions {
162
+ activity_id: Some("I'm bad and wrong!".to_string()),
163
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
164
+ ..Default::default()
165
+ }
166
+ } else {
167
+ LocalActivityOptions {
168
+ activity_type: "not the default act type".to_string(),
169
+ ..Default::default()
170
+ }
171
+ })
172
+ .await;
173
+ } else {
174
+ ctx.activity(if id_change {
175
+ ActivityOptions {
176
+ activity_id: Some("I'm bad and wrong!".to_string()),
177
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
178
+ ..Default::default()
179
+ }
180
+ } else {
181
+ ActivityOptions {
182
+ activity_type: "not the default act type".to_string(),
183
+ ..Default::default()
184
+ }
185
+ })
186
+ .await;
187
+ }
188
+ Ok(().into())
189
+ });
190
+
191
+ worker
192
+ .submit_wf(
193
+ wf_id.to_owned(),
194
+ wf_type.to_owned(),
195
+ vec![],
196
+ WorkflowOptions::default(),
197
+ )
198
+ .await
199
+ .unwrap();
200
+ worker.run_until_done().await.unwrap();
201
+ }
202
+
203
+ #[rstest::rstest]
204
+ #[tokio::test]
205
+ async fn child_wf_id_or_type_change_is_nondeterministic(
206
+ #[values(true, false)] use_cache: bool,
207
+ #[values(true, false)] id_change: bool,
208
+ ) {
209
+ let wf_id = "fakeid";
210
+ let wf_type = DEFAULT_WORKFLOW_TYPE;
211
+ let mut t = canned_histories::single_child_workflow("1");
212
+ t.set_flags_first_wft(&[CoreInternalFlags::IdAndTypeDeterminismChecks as u32], &[]);
213
+ let mock = mock_workflow_client();
214
+ let mut mh = MockPollCfg::from_resp_batches(
215
+ wf_id,
216
+ t,
217
+ // Two polls are needed, since the first will fail
218
+ [ResponseType::AllHistory, ResponseType::AllHistory],
219
+ mock,
220
+ );
221
+ // We should see one wft failure which has nondeterminism cause
222
+ mh.num_expected_fails = 1;
223
+ mh.expect_fail_wft_matcher = Box::new(move |_, cause, f| {
224
+ let should_contain = if id_change {
225
+ "does not match child workflow id"
226
+ } else {
227
+ "does not match child workflow type"
228
+ };
229
+ matches!(cause, WorkflowTaskFailedCause::NonDeterministicError)
230
+ && matches!(f, Some(Failure {
231
+ message,
232
+ ..
233
+ }) if message.contains(should_contain))
234
+ });
235
+ let mut worker = mock_sdk_cfg(mh, |cfg| {
236
+ if use_cache {
237
+ cfg.max_cached_workflows = 2;
238
+ }
239
+ });
240
+
241
+ worker.register_wf(wf_type.to_owned(), move |ctx: WfContext| async move {
242
+ ctx.child_workflow(if id_change {
243
+ ChildWorkflowOptions {
244
+ workflow_id: "I'm bad and wrong!".to_string(),
245
+ workflow_type: DEFAULT_ACTIVITY_TYPE.to_string(),
246
+ ..Default::default()
247
+ }
248
+ } else {
249
+ ChildWorkflowOptions {
250
+ workflow_id: "1".to_string(),
251
+ workflow_type: "not the child wf type".to_string(),
252
+ ..Default::default()
253
+ }
254
+ })
255
+ .start(&ctx)
256
+ .await;
257
+ Ok(().into())
258
+ });
259
+
260
+ worker
261
+ .submit_wf(
262
+ wf_id.to_owned(),
263
+ wf_type.to_owned(),
264
+ vec![],
265
+ WorkflowOptions::default(),
266
+ )
267
+ .await
268
+ .unwrap();
269
+ worker.run_until_done().await.unwrap();
270
+ }