@temporalio/core-bridge 0.16.4 → 0.18.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 (170) hide show
  1. package/Cargo.lock +339 -226
  2. package/Cargo.toml +7 -3
  3. package/common.js +50 -0
  4. package/index.d.ts +7 -0
  5. package/index.js +12 -0
  6. package/package.json +7 -4
  7. package/releases/aarch64-apple-darwin/index.node +0 -0
  8. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  9. package/{index.node → releases/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/scripts/build.js +10 -50
  14. package/sdk-core/.buildkite/docker/Dockerfile +1 -1
  15. package/sdk-core/.buildkite/docker/docker-compose.yaml +2 -2
  16. package/sdk-core/.buildkite/pipeline.yml +2 -0
  17. package/sdk-core/Cargo.toml +1 -88
  18. package/sdk-core/README.md +30 -6
  19. package/sdk-core/bridge-ffi/Cargo.toml +24 -0
  20. package/sdk-core/bridge-ffi/LICENSE.txt +23 -0
  21. package/sdk-core/bridge-ffi/build.rs +25 -0
  22. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +216 -0
  23. package/sdk-core/bridge-ffi/src/lib.rs +829 -0
  24. package/sdk-core/bridge-ffi/src/wrappers.rs +193 -0
  25. package/sdk-core/client/Cargo.toml +32 -0
  26. package/sdk-core/{src/pollers/gateway.rs → client/src/lib.rs} +101 -195
  27. package/sdk-core/client/src/metrics.rs +89 -0
  28. package/sdk-core/client/src/mocks.rs +167 -0
  29. package/sdk-core/{src/pollers → client/src}/retry.rs +172 -14
  30. package/sdk-core/core/Cargo.toml +96 -0
  31. package/sdk-core/{src → core/src}/core_tests/activity_tasks.rs +193 -37
  32. package/sdk-core/{src → core/src}/core_tests/child_workflows.rs +14 -14
  33. package/sdk-core/{src → core/src}/core_tests/determinism.rs +8 -8
  34. package/sdk-core/core/src/core_tests/local_activities.rs +328 -0
  35. package/sdk-core/{src → core/src}/core_tests/mod.rs +6 -9
  36. package/sdk-core/{src → core/src}/core_tests/queries.rs +54 -54
  37. package/sdk-core/{src → core/src}/core_tests/replay_flag.rs +8 -12
  38. package/sdk-core/{src → core/src}/core_tests/workers.rs +120 -33
  39. package/sdk-core/{src → core/src}/core_tests/workflow_cancels.rs +16 -26
  40. package/sdk-core/{src → core/src}/core_tests/workflow_tasks.rs +280 -292
  41. package/sdk-core/core/src/lib.rs +374 -0
  42. package/sdk-core/{src → core/src}/log_export.rs +3 -27
  43. package/sdk-core/core/src/pending_activations.rs +162 -0
  44. package/sdk-core/{src → core/src}/pollers/mod.rs +4 -22
  45. package/sdk-core/{src → core/src}/pollers/poll_buffer.rs +1 -1
  46. package/sdk-core/core/src/protosext/mod.rs +396 -0
  47. package/sdk-core/core/src/replay/mod.rs +210 -0
  48. package/sdk-core/core/src/retry_logic.rs +144 -0
  49. package/sdk-core/{src → core/src}/telemetry/metrics.rs +3 -58
  50. package/sdk-core/{src → core/src}/telemetry/mod.rs +8 -8
  51. package/sdk-core/{src → core/src}/telemetry/prometheus_server.rs +0 -0
  52. package/sdk-core/{src → core/src}/test_help/mod.rs +35 -83
  53. package/sdk-core/{src → core/src}/worker/activities/activity_heartbeat_manager.rs +95 -42
  54. package/sdk-core/core/src/worker/activities/local_activities.rs +973 -0
  55. package/sdk-core/{src → core/src}/worker/activities.rs +52 -33
  56. package/sdk-core/{src → core/src}/worker/dispatcher.rs +8 -6
  57. package/sdk-core/{src → core/src}/worker/mod.rs +347 -221
  58. package/sdk-core/core/src/worker/wft_delivery.rs +81 -0
  59. package/sdk-core/{src → core/src}/workflow/bridge.rs +5 -2
  60. package/sdk-core/{src → core/src}/workflow/driven_workflow.rs +17 -7
  61. package/sdk-core/{src → core/src}/workflow/history_update.rs +33 -7
  62. package/sdk-core/{src → core/src/workflow}/machines/activity_state_machine.rs +26 -26
  63. package/sdk-core/{src → core/src/workflow}/machines/cancel_external_state_machine.rs +8 -11
  64. package/sdk-core/{src → core/src/workflow}/machines/cancel_workflow_state_machine.rs +19 -21
  65. package/sdk-core/{src → core/src/workflow}/machines/child_workflow_state_machine.rs +20 -31
  66. package/sdk-core/{src → core/src/workflow}/machines/complete_workflow_state_machine.rs +3 -5
  67. package/sdk-core/{src → core/src/workflow}/machines/continue_as_new_workflow_state_machine.rs +18 -18
  68. package/sdk-core/{src → core/src/workflow}/machines/fail_workflow_state_machine.rs +5 -6
  69. package/sdk-core/core/src/workflow/machines/local_activity_state_machine.rs +1451 -0
  70. package/sdk-core/{src → core/src/workflow}/machines/mod.rs +54 -107
  71. package/sdk-core/{src → core/src/workflow}/machines/mutable_side_effect_state_machine.rs +0 -0
  72. package/sdk-core/{src → core/src/workflow}/machines/patch_state_machine.rs +29 -30
  73. package/sdk-core/{src → core/src/workflow}/machines/side_effect_state_machine.rs +0 -0
  74. package/sdk-core/{src → core/src/workflow}/machines/signal_external_state_machine.rs +17 -19
  75. package/sdk-core/{src → core/src/workflow}/machines/timer_state_machine.rs +20 -21
  76. package/sdk-core/{src → core/src/workflow}/machines/transition_coverage.rs +5 -2
  77. package/sdk-core/{src → core/src/workflow}/machines/upsert_search_attributes_state_machine.rs +0 -0
  78. package/sdk-core/core/src/workflow/machines/workflow_machines/local_acts.rs +96 -0
  79. package/sdk-core/{src → core/src/workflow}/machines/workflow_machines.rs +357 -171
  80. package/sdk-core/{src → core/src/workflow}/machines/workflow_task_state_machine.rs +1 -1
  81. package/sdk-core/{src → core/src}/workflow/mod.rs +200 -39
  82. package/sdk-core/{src → core/src}/workflow/workflow_tasks/cache_manager.rs +0 -0
  83. package/sdk-core/{src → core/src}/workflow/workflow_tasks/concurrency_manager.rs +38 -5
  84. package/sdk-core/{src → core/src}/workflow/workflow_tasks/mod.rs +317 -103
  85. package/sdk-core/{test_utils → core-api}/Cargo.toml +10 -7
  86. package/sdk-core/{src → core-api/src}/errors.rs +42 -92
  87. package/sdk-core/core-api/src/lib.rs +158 -0
  88. package/sdk-core/{src/worker/config.rs → core-api/src/worker.rs} +18 -23
  89. package/sdk-core/etc/deps.svg +156 -0
  90. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +5 -5
  91. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +3 -5
  92. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +7 -1
  93. package/sdk-core/histories/fail_wf_task.bin +0 -0
  94. package/sdk-core/histories/timer_workflow_history.bin +0 -0
  95. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +44 -13
  96. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +19 -1
  97. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +1 -1
  98. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +9 -0
  99. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +1 -0
  100. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +1 -0
  101. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +13 -0
  102. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +14 -7
  103. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +176 -18
  104. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
  105. package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +11 -0
  106. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +3 -0
  107. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +156 -7
  108. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +135 -104
  109. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
  110. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +78 -0
  111. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +205 -0
  112. package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +61 -0
  113. package/sdk-core/protos/local/{child_workflow.proto → temporal/sdk/core/child_workflow/child_workflow.proto} +1 -1
  114. package/sdk-core/protos/local/{common.proto → temporal/sdk/core/common/common.proto} +5 -3
  115. package/sdk-core/protos/local/{core_interface.proto → temporal/sdk/core/core_interface.proto} +10 -10
  116. package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
  117. package/sdk-core/protos/local/{workflow_activation.proto → temporal/sdk/core/workflow_activation/workflow_activation.proto} +35 -11
  118. package/sdk-core/protos/local/{workflow_commands.proto → temporal/sdk/core/workflow_commands/workflow_commands.proto} +55 -4
  119. package/sdk-core/protos/local/{workflow_completion.proto → temporal/sdk/core/workflow_completion/workflow_completion.proto} +3 -3
  120. package/sdk-core/sdk/Cargo.toml +32 -0
  121. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/conversions.rs +0 -0
  122. package/sdk-core/sdk/src/lib.rs +699 -0
  123. package/sdk-core/sdk/src/payload_converter.rs +11 -0
  124. package/sdk-core/sdk/src/workflow_context/options.rs +180 -0
  125. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_context.rs +201 -124
  126. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_future.rs +63 -30
  127. package/sdk-core/sdk-core-protos/Cargo.toml +10 -0
  128. package/sdk-core/sdk-core-protos/build.rs +28 -6
  129. package/sdk-core/sdk-core-protos/src/constants.rs +7 -0
  130. package/sdk-core/{src/test_help → sdk-core-protos/src}/history_builder.rs +134 -49
  131. package/sdk-core/sdk-core-protos/src/history_info.rs +216 -0
  132. package/sdk-core/sdk-core-protos/src/lib.rs +601 -168
  133. package/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
  134. package/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  135. package/sdk-core/test-utils/Cargo.toml +32 -0
  136. package/sdk-core/{src/test_help → test-utils/src}/canned_histories.rs +59 -78
  137. package/sdk-core/test-utils/src/histfetch.rs +28 -0
  138. package/sdk-core/{test_utils → test-utils}/src/lib.rs +131 -68
  139. package/sdk-core/tests/integ_tests/client_tests.rs +1 -1
  140. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +11 -7
  141. package/sdk-core/tests/integ_tests/polling_tests.rs +12 -11
  142. package/sdk-core/tests/integ_tests/queries_tests.rs +82 -78
  143. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +91 -71
  144. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +3 -4
  145. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +2 -4
  146. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -6
  147. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +4 -6
  148. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -4
  149. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +496 -0
  150. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +5 -8
  151. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +125 -0
  152. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +7 -13
  153. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +33 -5
  154. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +12 -16
  155. package/sdk-core/tests/integ_tests/workflow_tests.rs +85 -82
  156. package/sdk-core/tests/load_tests.rs +6 -6
  157. package/sdk-core/tests/main.rs +2 -2
  158. package/src/conversions.rs +24 -21
  159. package/src/errors.rs +8 -0
  160. package/src/lib.rs +323 -211
  161. package/sdk-core/protos/local/activity_result.proto +0 -46
  162. package/sdk-core/protos/local/activity_task.proto +0 -66
  163. package/sdk-core/src/core_tests/retry.rs +0 -147
  164. package/sdk-core/src/lib.rs +0 -403
  165. package/sdk-core/src/machines/local_activity_state_machine.rs +0 -117
  166. package/sdk-core/src/pending_activations.rs +0 -249
  167. package/sdk-core/src/protosext/mod.rs +0 -160
  168. package/sdk-core/src/prototype_rust_sdk.rs +0 -412
  169. package/sdk-core/src/task_token.rs +0 -20
  170. package/sdk-core/src/test_help/history_info.rs +0 -157
@@ -1,8 +1,7 @@
1
1
  use crate::{
2
2
  job_assert,
3
- pollers::{MockManualGateway, MockServerGatewayApis},
4
3
  test_help::{
5
- build_fake_core, canned_histories, fake_sg_opts, gen_assert_and_reply, mock_core,
4
+ build_fake_core, canned_histories, gen_assert_and_reply, mock_core,
6
5
  mock_core_with_opts_no_workers, mock_manual_poller, mock_poller, mock_poller_from_resps,
7
6
  poll_and_reply, MockWorker, MocksHolder, TEST_Q,
8
7
  },
@@ -11,31 +10,35 @@ use crate::{
11
10
  };
12
11
  use futures::FutureExt;
13
12
  use std::{
13
+ cell::RefCell,
14
14
  collections::{hash_map::Entry, HashMap, VecDeque},
15
+ rc::Rc,
15
16
  sync::{
16
17
  atomic::{AtomicUsize, Ordering},
17
18
  Arc,
18
19
  },
19
20
  time::Duration,
20
21
  };
22
+ use temporal_client::mocks::{fake_sg_opts, mock_gateway, mock_manual_gateway};
21
23
  use temporal_sdk_core_protos::{
22
24
  coresdk::{
23
- activity_result::{activity_result, ActivityResult},
25
+ activity_result::{activity_resolution, ActivityExecutionResult, ActivityResolution},
24
26
  activity_task::activity_task,
25
- workflow_activation::{wf_activation_job, ResolveActivity, WfActivationJob},
27
+ workflow_activation::{workflow_activation_job, ResolveActivity, WorkflowActivationJob},
26
28
  workflow_commands::{
27
29
  ActivityCancellationType, CompleteWorkflowExecution, RequestCancelActivity,
28
- ScheduleActivity, StartTimer,
30
+ ScheduleActivity,
29
31
  },
30
32
  ActivityTaskCompletion,
31
33
  },
32
34
  temporal::api::workflowservice::v1::{
33
35
  PollActivityTaskQueueResponse, RecordActivityTaskHeartbeatResponse,
34
- RespondActivityTaskCompletedResponse,
36
+ RespondActivityTaskCanceledResponse, RespondActivityTaskCompletedResponse,
37
+ RespondActivityTaskFailedResponse,
35
38
  },
36
39
  };
37
- use test_utils::fanout_tasks;
38
- use tokio::{sync::Notify, time::sleep};
40
+ use temporal_sdk_core_test_utils::{fanout_tasks, start_timer_cmd};
41
+ use tokio::{join, sync::Notify, time::sleep};
39
42
 
40
43
  #[tokio::test]
41
44
  async fn max_activities_respected() {
@@ -57,7 +60,7 @@ async fn max_activities_respected() {
57
60
  ..Default::default()
58
61
  },
59
62
  ]);
60
- let mut mock_gateway = MockServerGatewayApis::new();
63
+ let mut mock_gateway = mock_gateway();
61
64
  mock_gateway
62
65
  .expect_poll_activity_task()
63
66
  .times(3)
@@ -80,7 +83,6 @@ async fn max_activities_respected() {
80
83
  .build()
81
84
  .unwrap(),
82
85
  )
83
- .await
84
86
  .unwrap();
85
87
 
86
88
  // We allow two outstanding activities, therefore first two polls should return right away
@@ -93,7 +95,7 @@ async fn max_activities_respected() {
93
95
  core.complete_activity_task(ActivityTaskCompletion {
94
96
  task_token: r1.task_token,
95
97
  task_queue: TEST_Q.to_string(),
96
- result: Some(ActivityResult::ok(vec![1].into()))
98
+ result: Some(ActivityExecutionResult::ok(vec![1].into()))
97
99
  }).await.unwrap();
98
100
  last_finisher.store(1, Ordering::SeqCst);
99
101
  },
@@ -108,7 +110,7 @@ async fn max_activities_respected() {
108
110
 
109
111
  #[tokio::test]
110
112
  async fn activity_not_found_returns_ok() {
111
- let mut mock_gateway = MockServerGatewayApis::new();
113
+ let mut mock_gateway = mock_gateway();
112
114
  // Mock won't even be called, since we weren't tracking activity
113
115
  mock_gateway.expect_complete_activity_task().times(0);
114
116
 
@@ -121,7 +123,7 @@ async fn activity_not_found_returns_ok() {
121
123
  core.complete_activity_task(ActivityTaskCompletion {
122
124
  task_token: vec![1],
123
125
  task_queue: TEST_Q.to_string(),
124
- result: Some(ActivityResult::ok(vec![1].into())),
126
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
125
127
  })
126
128
  .await
127
129
  .unwrap();
@@ -130,7 +132,7 @@ async fn activity_not_found_returns_ok() {
130
132
 
131
133
  #[tokio::test]
132
134
  async fn heartbeats_report_cancels_only_once() {
133
- let mut mock_gateway = MockServerGatewayApis::new();
135
+ let mut mock_gateway = mock_gateway();
134
136
  mock_gateway
135
137
  .expect_record_activity_heartbeat()
136
138
  .times(2)
@@ -139,6 +141,14 @@ async fn heartbeats_report_cancels_only_once() {
139
141
  cancel_requested: true,
140
142
  })
141
143
  });
144
+ mock_gateway
145
+ .expect_complete_activity_task()
146
+ .times(1)
147
+ .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
148
+ mock_gateway
149
+ .expect_cancel_activity_task()
150
+ .times(1)
151
+ .returning(|_, _| Ok(RespondActivityTaskCanceledResponse::default()));
142
152
 
143
153
  let core = mock_core(MocksHolder::from_gateway_with_responses(
144
154
  mock_gateway,
@@ -181,22 +191,39 @@ async fn heartbeats_report_cancels_only_once() {
181
191
  // Allow heartbeat delay to elapse
182
192
  sleep(Duration::from_millis(10)).await;
183
193
  core.record_activity_heartbeat(ActivityHeartbeat {
184
- task_token: act.task_token,
194
+ task_token: act.task_token.clone(),
185
195
  task_queue: TEST_Q.to_string(),
186
196
  details: vec![vec![1_u8, 2, 3].into()],
187
197
  });
198
+ // Wait delay again to flush heartbeat
188
199
  sleep(Duration::from_millis(10)).await;
200
+ // Now complete it as cancelled
201
+ core.complete_activity_task(ActivityTaskCompletion {
202
+ task_token: act.task_token,
203
+ task_queue: TEST_Q.to_string(),
204
+ result: Some(ActivityExecutionResult::cancel_from_details(None)),
205
+ })
206
+ .await
207
+ .unwrap();
189
208
  // Since cancels always come before new tasks, if we get a new non-cancel task, we did not
190
209
  // double-issue cancels.
191
210
  let act = core.poll_activity_task(TEST_Q).await.unwrap();
192
211
  assert_matches!(
193
- act,
212
+ &act,
194
213
  ActivityTask {
195
214
  task_token,
196
215
  variant: Some(activity_task::Variant::Start(_)),
197
216
  ..
198
- } => { task_token == vec![2] }
217
+ } => { task_token == &[2] }
199
218
  );
219
+ // Complete it so shutdown goes through
220
+ core.complete_activity_task(ActivityTaskCompletion {
221
+ task_token: act.task_token,
222
+ task_queue: TEST_Q.to_string(),
223
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
224
+ })
225
+ .await
226
+ .unwrap();
200
227
  core.shutdown().await;
201
228
  }
202
229
 
@@ -223,7 +250,7 @@ async fn activity_cancel_interrupts_poll() {
223
250
  .times(2)
224
251
  .returning(move || poll_resps.pop_front().unwrap());
225
252
 
226
- let mut mock_gateway = MockManualGateway::new();
253
+ let mut mock_gateway = mock_manual_gateway();
227
254
  mock_gateway
228
255
  .expect_record_activity_heartbeat()
229
256
  .times(1)
@@ -235,6 +262,10 @@ async fn activity_cancel_interrupts_poll() {
235
262
  }
236
263
  .boxed()
237
264
  });
265
+ mock_gateway
266
+ .expect_complete_activity_task()
267
+ .times(1)
268
+ .returning(|_, _| async { Ok(RespondActivityTaskCompletedResponse::default()) }.boxed());
238
269
 
239
270
  let mock_worker = MockWorker {
240
271
  act_poller: Some(Box::from(mock_poller)),
@@ -256,7 +287,15 @@ async fn activity_cancel_interrupts_poll() {
256
287
  last_finisher.store(1, Ordering::SeqCst);
257
288
  },
258
289
  async {
259
- core.poll_activity_task(TEST_Q).await.unwrap();
290
+ let act = core.poll_activity_task(TEST_Q).await.unwrap();
291
+ // Must complete this activity for shutdown to finish
292
+ core.complete_activity_task(
293
+ ActivityTaskCompletion {
294
+ task_token: act.task_token,
295
+ task_queue: TEST_Q.to_string(),
296
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
297
+ }
298
+ ).await.unwrap();
260
299
  last_finisher.store(2, Ordering::SeqCst);
261
300
  }
262
301
  };
@@ -267,7 +306,7 @@ async fn activity_cancel_interrupts_poll() {
267
306
 
268
307
  #[tokio::test]
269
308
  async fn activity_poll_timeout_retries() {
270
- let mock_gateway = MockServerGatewayApis::new();
309
+ let mock_gateway = mock_gateway();
271
310
  let mut calls = 0;
272
311
  let mut mock_act_poller = mock_poller();
273
312
  mock_act_poller.expect_poll().times(3).returning(move || {
@@ -296,7 +335,7 @@ async fn many_concurrent_heartbeat_cancels() {
296
335
  // them after a few successful heartbeats
297
336
  const CONCURRENCY_NUM: usize = 5;
298
337
 
299
- let mut mock_gateway = MockManualGateway::new();
338
+ let mut mock_gateway = mock_manual_gateway();
300
339
  let mut poll_resps = VecDeque::from(
301
340
  (0..CONCURRENCY_NUM)
302
341
  .map(|i| {
@@ -362,7 +401,6 @@ async fn many_concurrent_heartbeat_cancels() {
362
401
  .build()
363
402
  .unwrap(),
364
403
  )
365
- .await
366
404
  .unwrap();
367
405
 
368
406
  // Poll all activities first so they are registered
@@ -397,7 +435,7 @@ async fn many_concurrent_heartbeat_cancels() {
397
435
  core.complete_activity_task(ActivityTaskCompletion {
398
436
  task_token: r.task_token.clone(),
399
437
  task_queue: TEST_Q.to_string(),
400
- result: Some(ActivityResult::cancel_from_details(None)),
438
+ result: Some(ActivityExecutionResult::cancel_from_details(None)),
401
439
  })
402
440
  .await
403
441
  .unwrap();
@@ -418,7 +456,7 @@ async fn activity_timeout_no_double_resolve() {
418
456
  NonSticky,
419
457
  &[
420
458
  gen_assert_and_reply(
421
- &job_assert!(wf_activation_job::Variant::StartWorkflow(_)),
459
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
422
460
  vec![ScheduleActivity {
423
461
  seq: activity_id,
424
462
  activity_id: activity_id.to_string(),
@@ -428,21 +466,17 @@ async fn activity_timeout_no_double_resolve() {
428
466
  .into()],
429
467
  ),
430
468
  gen_assert_and_reply(
431
- &job_assert!(wf_activation_job::Variant::SignalWorkflow(_)),
469
+ &job_assert!(workflow_activation_job::Variant::SignalWorkflow(_)),
432
470
  vec![
433
471
  RequestCancelActivity { seq: activity_id }.into(),
434
- StartTimer {
435
- seq: 2,
436
- ..Default::default()
437
- }
438
- .into(),
472
+ start_timer_cmd(2, Duration::from_secs(1)),
439
473
  ],
440
474
  ),
441
475
  gen_assert_and_reply(
442
- &job_assert!(wf_activation_job::Variant::ResolveActivity(
476
+ &job_assert!(workflow_activation_job::Variant::ResolveActivity(
443
477
  ResolveActivity {
444
- result: Some(ActivityResult {
445
- status: Some(activity_result::Status::Cancelled(..)),
478
+ result: Some(ActivityResolution {
479
+ status: Some(activity_resolution::Status::Cancelled(..)),
446
480
  }),
447
481
  ..
448
482
  }
@@ -451,8 +485,8 @@ async fn activity_timeout_no_double_resolve() {
451
485
  ),
452
486
  gen_assert_and_reply(
453
487
  &job_assert!(
454
- wf_activation_job::Variant::SignalWorkflow(_),
455
- wf_activation_job::Variant::FireTimer(_)
488
+ workflow_activation_job::Variant::SignalWorkflow(_),
489
+ workflow_activation_job::Variant::FireTimer(_)
456
490
  ),
457
491
  vec![CompleteWorkflowExecution { result: None }.into()],
458
492
  ),
@@ -465,7 +499,7 @@ async fn activity_timeout_no_double_resolve() {
465
499
 
466
500
  #[tokio::test]
467
501
  async fn only_returns_cancels_for_desired_queue() {
468
- let mut mock_gateway = MockServerGatewayApis::new();
502
+ let mut mock_gateway = mock_gateway();
469
503
  let seen_cancel = Arc::new(Notify::new());
470
504
  let sc = seen_cancel.clone();
471
505
  mock_gateway
@@ -478,6 +512,10 @@ async fn only_returns_cancels_for_desired_queue() {
478
512
  cancel_requested: true,
479
513
  })
480
514
  });
515
+ mock_gateway
516
+ .expect_cancel_activity_task()
517
+ .times(1)
518
+ .returning(|_, _| Ok(RespondActivityTaskCanceledResponse::default()));
481
519
 
482
520
  let mut w1 = MockWorker::for_queue("q1");
483
521
  w1.act_poller = Some(mock_poller_from_resps([PollActivityTaskQueueResponse {
@@ -515,12 +553,130 @@ async fn only_returns_cancels_for_desired_queue() {
515
553
  }
516
554
  };
517
555
  assert_matches!(
518
- q1_res,
556
+ &q1_res,
519
557
  ActivityTask {
520
558
  variant: Some(activity_task::Variant::Cancel(_)),
521
559
  ..
522
560
  }
523
561
  );
562
+ // act needs to complete to finalize shutdown
563
+ core.complete_activity_task(ActivityTaskCompletion {
564
+ task_token: q1_res.task_token,
565
+ task_queue: "q1".to_string(),
566
+ result: Some(ActivityExecutionResult::cancel_from_details(None)),
567
+ })
568
+ .await
569
+ .unwrap();
524
570
 
525
571
  core.shutdown().await;
526
572
  }
573
+
574
+ #[tokio::test]
575
+ async fn can_heartbeat_acts_during_shutdown() {
576
+ let mut mock_gateway = mock_gateway();
577
+ mock_gateway
578
+ .expect_record_activity_heartbeat()
579
+ .times(1)
580
+ .returning(|_, _| {
581
+ Ok(RecordActivityTaskHeartbeatResponse {
582
+ cancel_requested: false,
583
+ })
584
+ });
585
+ mock_gateway
586
+ .expect_complete_activity_task()
587
+ .times(1)
588
+ .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
589
+
590
+ let core = mock_core(MocksHolder::from_gateway_with_responses(
591
+ mock_gateway,
592
+ [],
593
+ [PollActivityTaskQueueResponse {
594
+ task_token: vec![1],
595
+ activity_id: "act1".to_string(),
596
+ heartbeat_timeout: Some(Duration::from_millis(1).into()),
597
+ ..Default::default()
598
+ }],
599
+ ));
600
+
601
+ let act = core.poll_activity_task(TEST_Q).await.unwrap();
602
+ let complete_order = RefCell::new(vec![]);
603
+ // Start shutdown before completing the activity
604
+ let shutdown_fut = async {
605
+ core.shutdown().await;
606
+ complete_order.borrow_mut().push(1);
607
+ };
608
+ let complete_fut = async {
609
+ core.record_activity_heartbeat(ActivityHeartbeat {
610
+ task_token: act.task_token.clone(),
611
+ task_queue: TEST_Q.to_string(),
612
+ details: vec![vec![1_u8, 2, 3].into()],
613
+ });
614
+ core.complete_activity_task(ActivityTaskCompletion {
615
+ task_token: act.task_token,
616
+ task_queue: TEST_Q.to_string(),
617
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
618
+ })
619
+ .await
620
+ .unwrap();
621
+ complete_order.borrow_mut().push(2);
622
+ };
623
+ join!(shutdown_fut, complete_fut);
624
+ assert_eq!(&complete_order.into_inner(), &[2, 1])
625
+ }
626
+
627
+ /// Verifies that if a user has tried to record a heartbeat and then immediately after failed the
628
+ /// activity, that we flush those details before reporting the failure completion.
629
+ #[tokio::test]
630
+ async fn complete_act_with_fail_flushes_heartbeat() {
631
+ let last_hb = 50;
632
+ let mut mock_gateway = mock_gateway();
633
+ let last_seen_payload = Rc::new(RefCell::new(None));
634
+ let lsp = last_seen_payload.clone();
635
+ mock_gateway
636
+ .expect_record_activity_heartbeat()
637
+ // Two times b/c we always record the first heartbeat, and we'll flush the last
638
+ .times(2)
639
+ .returning_st(move |_, payload| {
640
+ *lsp.borrow_mut() = payload;
641
+ Ok(RecordActivityTaskHeartbeatResponse {
642
+ cancel_requested: false,
643
+ })
644
+ });
645
+ mock_gateway
646
+ .expect_fail_activity_task()
647
+ .times(1)
648
+ .returning(|_, _| Ok(RespondActivityTaskFailedResponse::default()));
649
+
650
+ let core = mock_core(MocksHolder::from_gateway_with_responses(
651
+ mock_gateway,
652
+ [],
653
+ [PollActivityTaskQueueResponse {
654
+ task_token: vec![1],
655
+ activity_id: "act1".to_string(),
656
+ heartbeat_timeout: Some(Duration::from_secs(10).into()),
657
+ ..Default::default()
658
+ }],
659
+ ));
660
+
661
+ let act = core.poll_activity_task(TEST_Q).await.unwrap();
662
+ // Record a bunch of heartbeats
663
+ for i in 1..=last_hb {
664
+ core.record_activity_heartbeat(ActivityHeartbeat {
665
+ task_token: act.task_token.clone(),
666
+ task_queue: TEST_Q.to_string(),
667
+ details: vec![vec![i].into()],
668
+ });
669
+ }
670
+ core.complete_activity_task(ActivityTaskCompletion {
671
+ task_token: act.task_token.clone(),
672
+ task_queue: TEST_Q.to_string(),
673
+ result: Some(ActivityExecutionResult::fail("Ahh".into())),
674
+ })
675
+ .await
676
+ .unwrap();
677
+ core.shutdown().await;
678
+
679
+ // Verify the last seen call to record a heartbeat had the last detail payload
680
+ let last_seen_payload = &last_seen_payload.take().unwrap().payloads[0];
681
+ assert_eq!(last_seen_payload.data, &[last_hb]);
682
+ }
@@ -1,15 +1,15 @@
1
1
  use crate::{
2
- pollers::MockServerGatewayApis,
3
- prototype_rust_sdk::{
4
- ChildWorkflowOptions, TestRustWorker, WfContext, WorkflowFunction, WorkflowResult,
5
- },
2
+ replay::DEFAULT_WORKFLOW_TYPE,
6
3
  test_help::{
7
- build_mock_pollers, canned_histories, mock_core, MockPollCfg, ResponseType,
8
- DEFAULT_WORKFLOW_TYPE, TEST_Q,
4
+ build_mock_pollers, canned_histories, mock_core, MockPollCfg, ResponseType, TEST_Q,
9
5
  },
10
6
  workflow::managed_wf::ManagedWFFunc,
11
7
  };
12
8
  use std::sync::Arc;
9
+ use temporal_client::mocks::mock_gateway;
10
+ use temporal_sdk::{
11
+ ChildWorkflowOptions, TestRustWorker, WfContext, WorkflowFunction, WorkflowResult,
12
+ };
13
13
  use temporal_sdk_core_protos::coresdk::child_workflow::{
14
14
  child_workflow_result, ChildWorkflowCancellationType,
15
15
  };
@@ -25,13 +25,13 @@ async fn signal_child_workflow(#[case] serial: bool) {
25
25
  let wf_id = "fakeid";
26
26
  let wf_type = DEFAULT_WORKFLOW_TYPE;
27
27
  let t = canned_histories::single_child_workflow_signaled("child-id-1", SIGNAME);
28
- let mock = MockServerGatewayApis::new();
28
+ let mock = mock_gateway();
29
29
  let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::AllHistory], mock);
30
30
  let mock = build_mock_pollers(mh);
31
31
  let core = mock_core(mock);
32
32
  let mut worker = TestRustWorker::new(Arc::new(core), TEST_Q.to_string(), None);
33
33
 
34
- let wf = move |mut ctx: WfContext| async move {
34
+ let wf = move |ctx: WfContext| async move {
35
35
  let child = ctx.child_workflow(ChildWorkflowOptions {
36
36
  workflow_id: "child-id-1".to_string(),
37
37
  workflow_type: "child".to_string(),
@@ -39,16 +39,16 @@ async fn signal_child_workflow(#[case] serial: bool) {
39
39
  });
40
40
 
41
41
  let start_res = child
42
- .start(&mut ctx)
42
+ .start(&ctx)
43
43
  .await
44
44
  .into_started()
45
45
  .expect("Child should get started");
46
46
  let (sigres, res) = if serial {
47
- let sigres = start_res.signal(&mut ctx, SIGNAME, b"Hi!").await;
47
+ let sigres = start_res.signal(&ctx, SIGNAME, b"Hi!").await;
48
48
  let res = start_res.result().await;
49
49
  (sigres, res)
50
50
  } else {
51
- let sigfut = start_res.signal(&mut ctx, SIGNAME, b"Hi!");
51
+ let sigfut = start_res.signal(&ctx, SIGNAME, b"Hi!");
52
52
  let resfut = start_res.result();
53
53
  join!(sigfut, resfut)
54
54
  };
@@ -65,7 +65,7 @@ async fn signal_child_workflow(#[case] serial: bool) {
65
65
  worker.run_until_done().await.unwrap();
66
66
  }
67
67
 
68
- async fn parent_cancels_child_wf(mut ctx: WfContext) -> WorkflowResult<()> {
68
+ async fn parent_cancels_child_wf(ctx: WfContext) -> WorkflowResult<()> {
69
69
  let child = ctx.child_workflow(ChildWorkflowOptions {
70
70
  workflow_id: "child-id-1".to_string(),
71
71
  workflow_type: "child".to_string(),
@@ -74,11 +74,11 @@ async fn parent_cancels_child_wf(mut ctx: WfContext) -> WorkflowResult<()> {
74
74
  });
75
75
 
76
76
  let start_res = child
77
- .start(&mut ctx)
77
+ .start(&ctx)
78
78
  .await
79
79
  .into_started()
80
80
  .expect("Child should get started");
81
- let cancel_fut = start_res.cancel(&mut ctx);
81
+ let cancel_fut = start_res.cancel(&ctx);
82
82
  let resfut = start_res.result();
83
83
  let (cancel_res, res) = join!(cancel_fut, resfut);
84
84
  cancel_res.expect("cancel result is ok");
@@ -1,9 +1,7 @@
1
1
  use crate::{
2
- pollers::MockServerGatewayApis,
3
- prototype_rust_sdk::{TestRustWorker, WfContext, WorkflowResult},
2
+ replay::DEFAULT_WORKFLOW_TYPE,
4
3
  test_help::{
5
- build_mock_pollers, canned_histories, mock_core, MockPollCfg, ResponseType,
6
- DEFAULT_WORKFLOW_TYPE, TEST_Q,
4
+ build_mock_pollers, canned_histories, mock_core, MockPollCfg, ResponseType, TEST_Q,
7
5
  },
8
6
  };
9
7
  use std::{
@@ -13,10 +11,12 @@ use std::{
13
11
  },
14
12
  time::Duration,
15
13
  };
14
+ use temporal_client::mocks::mock_gateway;
15
+ use temporal_sdk::{TestRustWorker, WfContext, WorkflowResult};
16
16
  use temporal_sdk_core_protos::temporal::api::enums::v1::WorkflowTaskFailedCause;
17
17
 
18
18
  static DID_FAIL: AtomicBool = AtomicBool::new(false);
19
- pub async fn timer_wf_fails_once(mut ctx: WfContext) -> WorkflowResult<()> {
19
+ pub async fn timer_wf_fails_once(ctx: WfContext) -> WorkflowResult<()> {
20
20
  ctx.timer(Duration::from_secs(1)).await;
21
21
  if DID_FAIL
22
22
  .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
@@ -34,7 +34,7 @@ async fn test_wf_task_rejected_properly() {
34
34
  let wf_id = "fakeid";
35
35
  let wf_type = DEFAULT_WORKFLOW_TYPE;
36
36
  let t = canned_histories::workflow_fails_with_failure_after_timer("1");
37
- let mock = MockServerGatewayApis::new();
37
+ let mock = mock_gateway();
38
38
  let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 2], mock);
39
39
  // We should see one wft failure which has unspecified cause, since panics don't have a defined
40
40
  // type.
@@ -63,7 +63,7 @@ async fn test_wf_task_rejected_properly_due_to_nondeterminism(#[case] use_cache:
63
63
  let wf_id = "fakeid";
64
64
  let wf_type = DEFAULT_WORKFLOW_TYPE;
65
65
  let t = canned_histories::single_timer_wf_completes("1");
66
- let mock = MockServerGatewayApis::new();
66
+ let mock = mock_gateway();
67
67
  let mut mh = MockPollCfg::from_resp_batches(
68
68
  wf_id,
69
69
  t,
@@ -85,7 +85,7 @@ async fn test_wf_task_rejected_properly_due_to_nondeterminism(#[case] use_cache:
85
85
  let mut worker = TestRustWorker::new(Arc::new(core), TEST_Q.to_string(), None);
86
86
 
87
87
  let started_count: &'static _ = Box::leak(Box::new(AtomicUsize::new(0)));
88
- worker.register_wf(wf_type.to_owned(), move |mut ctx: WfContext| async move {
88
+ worker.register_wf(wf_type.to_owned(), move |ctx: WfContext| async move {
89
89
  // The workflow is replaying all of history, so the when it schedules an extra timer it
90
90
  // should not have, it causes a nondeterminism error.
91
91
  if started_count.fetch_add(1, Ordering::Relaxed) == 0 {