@temporalio/core-bridge 1.1.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/Cargo.lock +786 -54
  2. package/Cargo.toml +2 -2
  3. package/common.js +7 -3
  4. package/index.d.ts +110 -3
  5. package/index.js +2 -6
  6. package/package.json +3 -3
  7. package/releases/aarch64-apple-darwin/index.node +0 -0
  8. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  9. package/releases/x86_64-apple-darwin/index.node +0 -0
  10. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  11. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  12. package/scripts/build.js +4 -3
  13. package/sdk-core/.buildkite/docker/Dockerfile +2 -1
  14. package/sdk-core/ARCHITECTURE.md +2 -2
  15. package/sdk-core/README.md +12 -0
  16. package/sdk-core/bridge-ffi/Cargo.toml +2 -2
  17. package/sdk-core/client/Cargo.toml +6 -4
  18. package/sdk-core/client/src/lib.rs +338 -215
  19. package/sdk-core/client/src/raw.rs +352 -106
  20. package/sdk-core/client/src/retry.rs +159 -133
  21. package/sdk-core/client/src/workflow_handle/mod.rs +1 -1
  22. package/sdk-core/core/Cargo.toml +18 -9
  23. package/sdk-core/core/src/core_tests/activity_tasks.rs +63 -23
  24. package/sdk-core/core/src/core_tests/child_workflows.rs +125 -3
  25. package/sdk-core/core/src/core_tests/local_activities.rs +6 -6
  26. package/sdk-core/core/src/core_tests/workers.rs +3 -2
  27. package/sdk-core/core/src/core_tests/workflow_tasks.rs +70 -2
  28. package/sdk-core/core/src/ephemeral_server/mod.rs +499 -0
  29. package/sdk-core/core/src/lib.rs +60 -26
  30. package/sdk-core/core/src/pollers/poll_buffer.rs +4 -4
  31. package/sdk-core/core/src/replay/mod.rs +3 -3
  32. package/sdk-core/core/src/retry_logic.rs +10 -9
  33. package/sdk-core/core/src/telemetry/mod.rs +10 -7
  34. package/sdk-core/core/src/test_help/mod.rs +18 -8
  35. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +10 -10
  36. package/sdk-core/core/src/worker/activities/local_activities.rs +13 -13
  37. package/sdk-core/core/src/worker/activities.rs +6 -12
  38. package/sdk-core/core/src/worker/client.rs +193 -64
  39. package/sdk-core/core/src/worker/mod.rs +14 -19
  40. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -0
  41. package/sdk-core/core/src/worker/workflow/history_update.rs +5 -5
  42. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +133 -85
  43. package/sdk-core/core/src/worker/workflow/machines/mod.rs +3 -2
  44. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +160 -105
  45. package/sdk-core/core/src/worker/workflow/managed_run.rs +2 -1
  46. package/sdk-core/core/src/worker/workflow/mod.rs +59 -58
  47. package/sdk-core/core/src/worker/workflow/run_cache.rs +5 -3
  48. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +7 -5
  49. package/sdk-core/core-api/Cargo.toml +2 -2
  50. package/sdk-core/core-api/src/errors.rs +3 -11
  51. package/sdk-core/core-api/src/worker.rs +7 -0
  52. package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +1 -1
  53. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +1 -1
  54. package/sdk-core/protos/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +2 -6
  55. package/sdk-core/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +29 -0
  56. package/sdk-core/protos/api_upstream/Makefile +2 -2
  57. package/sdk-core/protos/api_upstream/buf.yaml +1 -0
  58. package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +86 -0
  59. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
  60. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +46 -0
  61. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +7 -0
  62. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +14 -0
  63. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +51 -0
  64. package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +18 -0
  65. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +57 -1
  66. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +1 -3
  67. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -2
  68. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +11 -0
  69. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +23 -0
  70. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +46 -0
  71. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +1 -0
  72. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +172 -0
  73. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +30 -0
  74. package/sdk-core/protos/grpc/health/v1/health.proto +63 -0
  75. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +18 -15
  76. package/sdk-core/protos/testsrv_upstream/Makefile +80 -0
  77. package/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
  78. package/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
  79. package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
  80. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
  81. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
  82. package/sdk-core/sdk/Cargo.toml +2 -2
  83. package/sdk-core/sdk/src/lib.rs +2 -2
  84. package/sdk-core/sdk/src/workflow_context/options.rs +36 -8
  85. package/sdk-core/sdk/src/workflow_context.rs +30 -6
  86. package/sdk-core/sdk/src/workflow_future.rs +4 -4
  87. package/sdk-core/sdk-core-protos/Cargo.toml +5 -5
  88. package/sdk-core/sdk-core-protos/build.rs +9 -1
  89. package/sdk-core/sdk-core-protos/src/history_builder.rs +6 -1
  90. package/sdk-core/sdk-core-protos/src/lib.rs +93 -32
  91. package/sdk-core/test-utils/Cargo.toml +3 -3
  92. package/sdk-core/test-utils/src/canned_histories.rs +58 -0
  93. package/sdk-core/test-utils/src/lib.rs +14 -10
  94. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +141 -0
  95. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +55 -5
  96. package/sdk-core/tests/integ_tests/polling_tests.rs +1 -1
  97. package/sdk-core/tests/integ_tests/queries_tests.rs +4 -4
  98. package/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
  99. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -10
  100. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  101. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +14 -14
  102. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +1 -1
  103. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +12 -12
  104. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +12 -1
  105. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +3 -3
  106. package/sdk-core/tests/integ_tests/workflow_tests.rs +19 -4
  107. package/sdk-core/tests/load_tests.rs +2 -1
  108. package/sdk-core/tests/main.rs +10 -0
  109. package/src/conversions.rs +138 -91
  110. package/src/helpers.rs +190 -0
  111. package/src/lib.rs +10 -912
  112. package/src/runtime.rs +436 -0
  113. package/src/testing.rs +67 -0
  114. package/src/worker.rs +465 -0
@@ -1,5 +1,5 @@
1
1
  use crate::{
2
- advance_fut, job_assert,
2
+ advance_fut, job_assert, prost_dur,
3
3
  test_help::{
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,
@@ -23,6 +23,7 @@ use std::{
23
23
  use temporal_client::WorkflowOptions;
24
24
  use temporal_sdk::{ActivityOptions, WfContext};
25
25
  use temporal_sdk_core_api::{errors::CompleteActivityError, Worker as WorkerTrait};
26
+ use temporal_sdk_core_protos::temporal::api::command::v1::ScheduleActivityTaskCommandAttributes;
26
27
  use temporal_sdk_core_protos::{
27
28
  coresdk::{
28
29
  activity_result::{
@@ -149,14 +150,14 @@ async fn heartbeats_report_cancels_only_once() {
149
150
  PollActivityTaskQueueResponse {
150
151
  task_token: vec![1],
151
152
  activity_id: "act1".to_string(),
152
- heartbeat_timeout: Some(Duration::from_millis(1).into()),
153
+ heartbeat_timeout: Some(prost_dur!(from_millis(1))),
153
154
  ..Default::default()
154
155
  }
155
156
  .into(),
156
157
  PollActivityTaskQueueResponse {
157
158
  task_token: vec![2],
158
159
  activity_id: "act2".to_string(),
159
- heartbeat_timeout: Some(Duration::from_millis(1).into()),
160
+ heartbeat_timeout: Some(prost_dur!(from_millis(1))),
160
161
  ..Default::default()
161
162
  }
162
163
  .into(),
@@ -226,7 +227,7 @@ async fn activity_cancel_interrupts_poll() {
226
227
  async {
227
228
  Some(Ok(PollActivityTaskQueueResponse {
228
229
  task_token: vec![1],
229
- heartbeat_timeout: Some(Duration::from_secs(1).into()),
230
+ heartbeat_timeout: Some(prost_dur!(from_secs(1))),
230
231
  ..Default::default()
231
232
  }))
232
233
  }
@@ -263,7 +264,7 @@ async fn activity_cancel_interrupts_poll() {
263
264
  act_poller: Some(Box::from(mock_poller)),
264
265
  ..Default::default()
265
266
  };
266
- let core = mock_worker(MocksHolder::from_mock_worker(mock_client.into(), mw));
267
+ let core = mock_worker(MocksHolder::from_mock_worker(mock_client, mw));
267
268
  let last_finisher = AtomicUsize::new(0);
268
269
  // Perform first poll to get the activity registered
269
270
  let act = core.poll_activity_task().await.unwrap();
@@ -315,7 +316,7 @@ async fn activity_poll_timeout_retries() {
315
316
  act_poller: Some(Box::from(mock_act_poller)),
316
317
  ..Default::default()
317
318
  };
318
- let core = mock_worker(MocksHolder::from_mock_worker(mock_client.into(), mw));
319
+ let core = mock_worker(MocksHolder::from_mock_worker(mock_client, mw));
319
320
  let r = core.poll_activity_task().await.unwrap();
320
321
  assert_matches!(r.task_token.as_slice(), b"hello!");
321
322
  }
@@ -333,7 +334,7 @@ async fn many_concurrent_heartbeat_cancels() {
333
334
  async move {
334
335
  Ok(PollActivityTaskQueueResponse {
335
336
  task_token: i.to_be_bytes().to_vec(),
336
- heartbeat_timeout: Some(Duration::from_millis(200).into()),
337
+ heartbeat_timeout: Some(prost_dur!(from_millis(200))),
337
338
  ..Default::default()
338
339
  })
339
340
  }
@@ -506,7 +507,7 @@ async fn can_heartbeat_acts_during_shutdown() {
506
507
  [PollActivityTaskQueueResponse {
507
508
  task_token: vec![1],
508
509
  activity_id: "act1".to_string(),
509
- heartbeat_timeout: Some(Duration::from_millis(1).into()),
510
+ heartbeat_timeout: Some(prost_dur!(from_millis(1))),
510
511
  ..Default::default()
511
512
  }
512
513
  .into()],
@@ -559,7 +560,7 @@ async fn complete_act_with_fail_flushes_heartbeat() {
559
560
  [PollActivityTaskQueueResponse {
560
561
  task_token: vec![1],
561
562
  activity_id: "act1".to_string(),
562
- heartbeat_timeout: Some(Duration::from_secs(10).into()),
563
+ heartbeat_timeout: Some(prost_dur!(from_secs(10))),
563
564
  ..Default::default()
564
565
  }
565
566
  .into()],
@@ -620,21 +621,46 @@ async fn activity_tasks_from_completion_are_delivered() {
620
621
  let mut t = TestHistoryBuilder::default();
621
622
  t.add_by_type(EventType::WorkflowExecutionStarted);
622
623
  t.add_full_wf_task();
623
- let schedid = t.add_activity_task_scheduled("act_id");
624
- let startid = t.add_activity_task_started(schedid);
625
- t.add_activity_task_completed(schedid, startid, b"hi".into());
624
+ let act_same_queue_sched_id = t.add_activity_task_scheduled("act_id_same_queue");
625
+ let act_different_queue_sched_id = t.add_activity_task_scheduled("act_id_different_queue");
626
+ let act_same_queue_start_id = t.add_activity_task_started(act_same_queue_sched_id);
627
+ t.add_activity_task_completed(
628
+ act_same_queue_sched_id,
629
+ act_same_queue_start_id,
630
+ b"hi".into(),
631
+ );
626
632
  t.add_full_wf_task();
633
+ t.add_activity_task_cancel_requested(act_different_queue_sched_id);
627
634
  t.add_workflow_execution_completed();
628
635
 
636
+ let num_eager_requested = Arc::new(AtomicUsize::new(0));
637
+ // Clone it to move into the callback below
638
+ let num_eager_requested_clone = num_eager_requested.clone();
639
+
629
640
  let mut mock = mock_workflow_client();
630
641
  mock.expect_complete_workflow_task()
631
642
  .times(1)
632
- .returning(move |_| {
643
+ .returning(move |req| {
644
+ // Store the number of eager activities requested to be checked below
645
+ let count = req
646
+ .commands
647
+ .into_iter()
648
+ .filter(|c| match c.attributes {
649
+ Some(Attributes::ScheduleActivityTaskCommandAttributes(
650
+ ScheduleActivityTaskCommandAttributes {
651
+ request_eager_execution,
652
+ ..
653
+ },
654
+ )) => request_eager_execution,
655
+ _ => false,
656
+ })
657
+ .count();
658
+ num_eager_requested_clone.store(count, Ordering::Relaxed);
633
659
  Ok(RespondWorkflowTaskCompletedResponse {
634
660
  workflow_task: None,
635
661
  activity_tasks: vec![PollActivityTaskQueueResponse {
636
662
  task_token: vec![1],
637
- activity_id: "act1".to_string(),
663
+ activity_id: "act_id_same_queue".to_string(),
638
664
  ..Default::default()
639
665
  }],
640
666
  })
@@ -652,15 +678,26 @@ async fn activity_tasks_from_completion_are_delivered() {
652
678
  let core = mock_worker(mock);
653
679
 
654
680
  let wf_task = core.poll_workflow_activation().await.unwrap();
655
- core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
681
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
656
682
  wf_task.run_id,
657
- ScheduleActivity {
658
- seq: 1,
659
- activity_id: "act_id".to_string(),
660
- cancellation_type: ActivityCancellationType::TryCancel as i32,
661
- ..Default::default()
662
- }
663
- .into(),
683
+ vec![
684
+ ScheduleActivity {
685
+ seq: 1,
686
+ activity_id: "act_id_same_queue".to_string(),
687
+ task_queue: TEST_Q.to_string(),
688
+ cancellation_type: ActivityCancellationType::TryCancel as i32,
689
+ ..Default::default()
690
+ }
691
+ .into(),
692
+ ScheduleActivity {
693
+ seq: 2,
694
+ activity_id: "act_id_different_queue".to_string(),
695
+ task_queue: "different_queue".to_string(),
696
+ cancellation_type: ActivityCancellationType::Abandon as i32,
697
+ ..Default::default()
698
+ }
699
+ .into(),
700
+ ],
664
701
  ))
665
702
  .await
666
703
  .unwrap();
@@ -677,6 +714,9 @@ async fn activity_tasks_from_completion_are_delivered() {
677
714
  .unwrap();
678
715
 
679
716
  core.shutdown().await;
717
+
718
+ // Verify only a single eager activity was scheduled (the one on our worker's task queue)
719
+ assert_eq!(num_eager_requested.load(Ordering::Relaxed), 1);
680
720
  }
681
721
 
682
722
  #[tokio::test]
@@ -818,7 +858,7 @@ async fn retryable_net_error_exhaustion_is_nonfatal() {
818
858
  [PollActivityTaskQueueResponse {
819
859
  task_token: vec![1],
820
860
  activity_id: "act1".to_string(),
821
- heartbeat_timeout: Some(Duration::from_secs(10).into()),
861
+ heartbeat_timeout: Some(prost_dur!(from_secs(10))),
822
862
  ..Default::default()
823
863
  }
824
864
  .into()],
@@ -1,12 +1,20 @@
1
1
  use crate::{
2
2
  replay::DEFAULT_WORKFLOW_TYPE,
3
- test_help::{canned_histories, mock_sdk, MockPollCfg, ResponseType},
3
+ test_help::{
4
+ canned_histories, mock_sdk, mock_worker, single_hist_mock_sg, MockPollCfg, ResponseType,
5
+ },
4
6
  worker::{client::mocks::mock_workflow_client, ManagedWFFunc},
5
7
  };
6
8
  use temporal_client::WorkflowOptions;
7
9
  use temporal_sdk::{ChildWorkflowOptions, Signal, WfContext, WorkflowFunction, WorkflowResult};
8
- use temporal_sdk_core_protos::coresdk::child_workflow::{
9
- child_workflow_result, ChildWorkflowCancellationType,
10
+ use temporal_sdk_core_api::Worker;
11
+ use temporal_sdk_core_protos::coresdk::{
12
+ child_workflow::{child_workflow_result, ChildWorkflowCancellationType},
13
+ workflow_activation::{workflow_activation_job, WorkflowActivationJob},
14
+ workflow_commands::{
15
+ CancelChildWorkflowExecution, CompleteWorkflowExecution, StartChildWorkflowExecution,
16
+ },
17
+ workflow_completion::WorkflowActivationCompletion,
10
18
  };
11
19
  use tokio::join;
12
20
 
@@ -97,3 +105,117 @@ async fn cancel_child_workflow() {
97
105
  wfm.process_all_activations().await.unwrap();
98
106
  wfm.shutdown().await.unwrap();
99
107
  }
108
+
109
+ #[rstest::rstest]
110
+ #[case::abandon(ChildWorkflowCancellationType::Abandon)]
111
+ #[case::try_cancel(ChildWorkflowCancellationType::TryCancel)]
112
+ #[case::wait_cancel_completed(ChildWorkflowCancellationType::WaitCancellationCompleted)]
113
+ #[tokio::test]
114
+ async fn cancel_child_workflow_lang_thinks_not_started_but_is(
115
+ #[case] cancellation_type: ChildWorkflowCancellationType,
116
+ ) {
117
+ // Since signal handlers always run first, it's possible lang might try to cancel
118
+ // a child workflow it thinks isn't started, but we've told it is in the same activation.
119
+ // It would be annoying for lang to have to peek ahead at jobs to be consistent in that case.
120
+ let t = match cancellation_type {
121
+ ChildWorkflowCancellationType::Abandon => {
122
+ canned_histories::single_child_workflow_abandon_cancelled("child-id-1")
123
+ }
124
+ ChildWorkflowCancellationType::TryCancel => {
125
+ canned_histories::single_child_workflow_try_cancelled("child-id-1")
126
+ }
127
+ _ => canned_histories::single_child_workflow_cancelled("child-id-1"),
128
+ };
129
+ let mock = mock_workflow_client();
130
+ let mock = single_hist_mock_sg("fakeid", t, [ResponseType::AllHistory], mock, true);
131
+ let core = mock_worker(mock);
132
+ let act = core.poll_workflow_activation().await.unwrap();
133
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
134
+ act.run_id,
135
+ StartChildWorkflowExecution {
136
+ seq: 1,
137
+ cancellation_type: cancellation_type as i32,
138
+ ..Default::default()
139
+ }
140
+ .into(),
141
+ ))
142
+ .await
143
+ .unwrap();
144
+ let act = core.poll_workflow_activation().await.unwrap();
145
+ assert_matches!(
146
+ act.jobs.as_slice(),
147
+ [WorkflowActivationJob {
148
+ variant: Some(workflow_activation_job::Variant::ResolveChildWorkflowExecutionStart(_)),
149
+ }]
150
+ );
151
+ // Issue the cancel command
152
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
153
+ act.run_id,
154
+ CancelChildWorkflowExecution {
155
+ child_workflow_seq: 1,
156
+ }
157
+ .into(),
158
+ ))
159
+ .await
160
+ .unwrap();
161
+ let act = core.poll_workflow_activation().await.unwrap();
162
+ // Make sure that a resolve for the "request cancel external workflow" command does *not* appear
163
+ // since lang didn't actually issue one. The only job should be resolving the child workflow.
164
+ assert_matches!(
165
+ act.jobs.as_slice(),
166
+ [WorkflowActivationJob {
167
+ variant: Some(workflow_activation_job::Variant::ResolveChildWorkflowExecution(_)),
168
+ }]
169
+ );
170
+ // Request cancel external is technically fallible, but the only reasons relate to targeting
171
+ // a not-found workflow, which couldn't happen in this case.
172
+ }
173
+
174
+ #[tokio::test]
175
+ async fn cancel_already_complete_child_ignored() {
176
+ let t = canned_histories::single_child_workflow("child-id-1");
177
+ let mock = mock_workflow_client();
178
+ let mock = single_hist_mock_sg("fakeid", t, [ResponseType::AllHistory], mock, true);
179
+ let core = mock_worker(mock);
180
+ let act = core.poll_workflow_activation().await.unwrap();
181
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
182
+ act.run_id,
183
+ StartChildWorkflowExecution {
184
+ seq: 1,
185
+ ..Default::default()
186
+ }
187
+ .into(),
188
+ ))
189
+ .await
190
+ .unwrap();
191
+ let act = core.poll_workflow_activation().await.unwrap();
192
+ assert_matches!(
193
+ act.jobs.as_slice(),
194
+ [WorkflowActivationJob {
195
+ variant: Some(workflow_activation_job::Variant::ResolveChildWorkflowExecutionStart(_)),
196
+ }]
197
+ );
198
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(act.run_id))
199
+ .await
200
+ .unwrap();
201
+ let act = core.poll_workflow_activation().await.unwrap();
202
+ assert_matches!(
203
+ act.jobs.as_slice(),
204
+ [WorkflowActivationJob {
205
+ variant: Some(workflow_activation_job::Variant::ResolveChildWorkflowExecution(_)),
206
+ }]
207
+ );
208
+ // Try to cancel post-completion, it should be ignored. Also complete the wf.
209
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
210
+ act.run_id,
211
+ vec![
212
+ CancelChildWorkflowExecution {
213
+ child_workflow_seq: 1,
214
+ }
215
+ .into(),
216
+ CompleteWorkflowExecution { result: None }.into(),
217
+ ],
218
+ ))
219
+ .await
220
+ .unwrap();
221
+ }
@@ -1,4 +1,5 @@
1
1
  use crate::{
2
+ prost_dur,
2
3
  replay::{default_wes_attribs, TestHistoryBuilder, DEFAULT_WORKFLOW_TYPE},
3
4
  test_help::{
4
5
  hist_to_poll_resp, mock_sdk, mock_sdk_cfg, mock_worker, single_hist_mock_sg, MockPollCfg,
@@ -162,7 +163,7 @@ async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
162
163
  let mut t = TestHistoryBuilder::default();
163
164
  let wft_timeout = Duration::from_millis(200);
164
165
  let mut wes_short_wft_timeout = default_wes_attribs();
165
- wes_short_wft_timeout.workflow_task_timeout = Some(wft_timeout.into());
166
+ wes_short_wft_timeout.workflow_task_timeout = Some(wft_timeout.try_into().unwrap());
166
167
  t.add(
167
168
  EventType::WorkflowExecutionStarted,
168
169
  wes_short_wft_timeout.into(),
@@ -247,7 +248,7 @@ async fn local_act_fail_and_retry(#[case] eventually_pass: bool) {
247
248
  activity_type: "echo".to_string(),
248
249
  input: "hi".as_json_payload().expect("serializes fine"),
249
250
  retry_policy: RetryPolicy {
250
- initial_interval: Some(Duration::from_millis(50).into()),
251
+ initial_interval: Some(prost_dur!(from_millis(50))),
251
252
  backoff_coefficient: 1.2,
252
253
  maximum_interval: None,
253
254
  maximum_attempts: 5,
@@ -328,10 +329,10 @@ async fn local_act_retry_long_backoff_uses_timer() {
328
329
  activity_type: "echo".to_string(),
329
330
  input: "hi".as_json_payload().expect("serializes fine"),
330
331
  retry_policy: RetryPolicy {
331
- initial_interval: Some(Duration::from_millis(65).into()),
332
+ initial_interval: Some(prost_dur!(from_millis(65))),
332
333
  // This will make the second backoff 65 seconds, plenty to use timer
333
334
  backoff_coefficient: 1_000.,
334
- maximum_interval: Some(Duration::from_secs(600).into()),
335
+ maximum_interval: Some(prost_dur!(from_secs(600))),
335
336
  maximum_attempts: 3,
336
337
  non_retryable_error_types: vec![],
337
338
  },
@@ -399,11 +400,10 @@ async fn local_act_null_result() {
399
400
 
400
401
  #[tokio::test]
401
402
  async fn query_during_wft_heartbeat_doesnt_accidentally_fail_to_continue_heartbeat() {
402
- crate::telemetry::test_telem_console();
403
403
  let wfid = "fake_wf_id";
404
404
  let mut t = TestHistoryBuilder::default();
405
405
  let mut wes_short_wft_timeout = default_wes_attribs();
406
- wes_short_wft_timeout.workflow_task_timeout = Some(Duration::from_millis(200).into());
406
+ wes_short_wft_timeout.workflow_task_timeout = Some(prost_dur!(from_millis(200)));
407
407
  t.add(
408
408
  EventType::WorkflowExecutionStarted,
409
409
  wes_short_wft_timeout.into(),
@@ -1,4 +1,5 @@
1
1
  use crate::{
2
+ prost_dur,
2
3
  test_help::{
3
4
  build_fake_worker, build_mock_pollers, canned_histories, mock_manual_poller, mock_worker,
4
5
  MockPollCfg, MockWorkerInputs, MocksHolder, ResponseType,
@@ -35,7 +36,7 @@ async fn after_shutdown_of_worker_get_shutdown_err() {
35
36
  run_id.clone(),
36
37
  workflow_command::Variant::StartTimer(StartTimer {
37
38
  seq: 1,
38
- start_to_fire_timeout: Some(Duration::from_secs(1).into()),
39
+ start_to_fire_timeout: Some(prost_dur!(from_secs(1))),
39
40
  }),
40
41
  ))
41
42
  .await
@@ -104,7 +105,7 @@ async fn worker_shutdown_during_poll_doesnt_deadlock() {
104
105
  mock_client
105
106
  .expect_complete_workflow_task()
106
107
  .returning(|_| Ok(RespondWorkflowTaskCompletedResponse::default()));
107
- let worker = mock_worker(MocksHolder::from_mock_worker(mock_client.into(), mw));
108
+ let worker = mock_worker(MocksHolder::from_mock_worker(mock_client, mw));
108
109
  let pollfut = worker.poll_workflow_activation();
109
110
  let shutdownfut = async {
110
111
  worker.shutdown().await;
@@ -15,7 +15,7 @@ use crate::{
15
15
  use futures::{stream, FutureExt};
16
16
  use rstest::{fixture, rstest};
17
17
  use std::{
18
- collections::VecDeque,
18
+ collections::{HashMap, VecDeque},
19
19
  sync::{
20
20
  atomic::{AtomicU64, Ordering},
21
21
  Arc,
@@ -33,11 +33,15 @@ use temporal_sdk_core_protos::{
33
33
  },
34
34
  workflow_commands::{
35
35
  ActivityCancellationType, CancelTimer, CompleteWorkflowExecution,
36
- FailWorkflowExecution, RequestCancelActivity, ScheduleActivity,
36
+ ContinueAsNewWorkflowExecution, FailWorkflowExecution, RequestCancelActivity,
37
+ ScheduleActivity,
37
38
  },
38
39
  workflow_completion::WorkflowActivationCompletion,
39
40
  },
41
+ default_wes_attribs,
40
42
  temporal::api::{
43
+ command::v1::command::Attributes,
44
+ common::v1::{Payload, RetryPolicy},
41
45
  enums::v1::{EventType, WorkflowTaskFailedCause},
42
46
  failure::v1::Failure,
43
47
  history::v1::{history_event, TimerFiredEventAttributes},
@@ -2000,3 +2004,67 @@ async fn no_race_acquiring_permits() {
2000
2004
  };
2001
2005
  join!(poll_1_f, poll_2_f, other_f);
2002
2006
  }
2007
+
2008
+ #[tokio::test]
2009
+ async fn continue_as_new_preserves_some_values() {
2010
+ let wfid = "fake_wf_id";
2011
+ let memo = HashMap::<String, Payload>::from([("enchi".to_string(), b"cat".into())]).into();
2012
+ let search = HashMap::<String, Payload>::from([("noisy".to_string(), b"kitty".into())]).into();
2013
+ let retry_policy = RetryPolicy {
2014
+ backoff_coefficient: 13.37,
2015
+ ..Default::default()
2016
+ };
2017
+ let mut wes_attrs = default_wes_attribs();
2018
+ wes_attrs.memo = Some(memo);
2019
+ wes_attrs.search_attributes = Some(search);
2020
+ wes_attrs.retry_policy = Some(retry_policy);
2021
+ let mut mock_client = mock_workflow_client();
2022
+ let hist = {
2023
+ let mut t = TestHistoryBuilder::default();
2024
+ t.add(
2025
+ EventType::WorkflowExecutionStarted,
2026
+ wes_attrs.clone().into(),
2027
+ );
2028
+ t.add_full_wf_task();
2029
+ t
2030
+ };
2031
+ mock_client
2032
+ .expect_poll_workflow_task()
2033
+ .returning(move |_, _| {
2034
+ Ok(hist_to_poll_resp(
2035
+ &hist,
2036
+ wfid.to_owned(),
2037
+ ResponseType::AllHistory,
2038
+ TEST_Q.to_string(),
2039
+ )
2040
+ .resp)
2041
+ });
2042
+ mock_client
2043
+ .expect_complete_workflow_task()
2044
+ .returning(move |mut c| {
2045
+ let can_cmd = c.commands.pop().unwrap().attributes.unwrap();
2046
+ if let Attributes::ContinueAsNewWorkflowExecutionCommandAttributes(a) = can_cmd {
2047
+ assert_eq!(a.workflow_type.unwrap().name, "meow");
2048
+ assert_eq!(a.memo, wes_attrs.memo);
2049
+ assert_eq!(a.search_attributes, wes_attrs.search_attributes);
2050
+ assert_eq!(a.retry_policy, wes_attrs.retry_policy);
2051
+ } else {
2052
+ panic!("Wrong attributes type");
2053
+ }
2054
+ Ok(Default::default())
2055
+ });
2056
+
2057
+ let worker = Worker::new_test(test_worker_cfg().build().unwrap(), mock_client);
2058
+ let r = worker.poll_workflow_activation().await.unwrap();
2059
+ worker
2060
+ .complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
2061
+ r.run_id,
2062
+ ContinueAsNewWorkflowExecution {
2063
+ workflow_type: "meow".to_string(),
2064
+ ..Default::default()
2065
+ }
2066
+ .into(),
2067
+ ))
2068
+ .await
2069
+ .unwrap();
2070
+ }