@temporalio/core-bridge 1.1.0 → 1.4.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 (124) hide show
  1. package/Cargo.lock +765 -128
  2. package/Cargo.toml +2 -2
  3. package/common.js +7 -3
  4. package/index.d.ts +118 -5
  5. package/index.js +2 -6
  6. package/package.json +2 -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/.buildkite/pipeline.yml +2 -0
  15. package/sdk-core/.cargo/config.toml +1 -1
  16. package/sdk-core/ARCHITECTURE.md +2 -2
  17. package/sdk-core/README.md +12 -0
  18. package/sdk-core/bridge-ffi/Cargo.toml +2 -2
  19. package/sdk-core/bridge-ffi/src/lib.rs +2 -2
  20. package/sdk-core/client/Cargo.toml +7 -5
  21. package/sdk-core/client/src/lib.rs +354 -226
  22. package/sdk-core/client/src/metrics.rs +13 -11
  23. package/sdk-core/client/src/raw.rs +352 -107
  24. package/sdk-core/client/src/retry.rs +188 -147
  25. package/sdk-core/client/src/workflow_handle/mod.rs +1 -1
  26. package/sdk-core/core/Cargo.toml +28 -15
  27. package/sdk-core/core/src/core_tests/activity_tasks.rs +98 -33
  28. package/sdk-core/core/src/core_tests/child_workflows.rs +125 -3
  29. package/sdk-core/core/src/core_tests/local_activities.rs +6 -6
  30. package/sdk-core/core/src/core_tests/workers.rs +3 -2
  31. package/sdk-core/core/src/core_tests/workflow_tasks.rs +70 -2
  32. package/sdk-core/core/src/ephemeral_server/mod.rs +515 -0
  33. package/sdk-core/core/src/lib.rs +62 -28
  34. package/sdk-core/core/src/pollers/mod.rs +2 -0
  35. package/sdk-core/core/src/pollers/poll_buffer.rs +4 -4
  36. package/sdk-core/core/src/replay/mod.rs +3 -3
  37. package/sdk-core/core/src/retry_logic.rs +10 -9
  38. package/sdk-core/core/src/telemetry/metrics.rs +48 -39
  39. package/sdk-core/core/src/telemetry/mod.rs +46 -12
  40. package/sdk-core/core/src/telemetry/prometheus_server.rs +17 -13
  41. package/sdk-core/core/src/test_help/mod.rs +18 -8
  42. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +10 -10
  43. package/sdk-core/core/src/worker/activities/local_activities.rs +13 -13
  44. package/sdk-core/core/src/worker/activities.rs +6 -12
  45. package/sdk-core/core/src/worker/client/mocks.rs +1 -0
  46. package/sdk-core/core/src/worker/client.rs +193 -64
  47. package/sdk-core/core/src/worker/mod.rs +14 -19
  48. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -0
  49. package/sdk-core/core/src/worker/workflow/history_update.rs +5 -5
  50. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +133 -85
  51. package/sdk-core/core/src/worker/workflow/machines/mod.rs +3 -2
  52. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +160 -105
  53. package/sdk-core/core/src/worker/workflow/managed_run.rs +2 -1
  54. package/sdk-core/core/src/worker/workflow/mod.rs +62 -58
  55. package/sdk-core/core/src/worker/workflow/run_cache.rs +5 -3
  56. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +7 -5
  57. package/sdk-core/core-api/Cargo.toml +3 -3
  58. package/sdk-core/core-api/src/errors.rs +3 -11
  59. package/sdk-core/core-api/src/worker.rs +7 -0
  60. package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +1 -1
  61. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +1 -1
  62. package/sdk-core/protos/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +2 -6
  63. package/sdk-core/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +29 -0
  64. package/sdk-core/protos/api_upstream/Makefile +2 -2
  65. package/sdk-core/protos/api_upstream/buf.yaml +1 -0
  66. package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +86 -0
  67. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
  68. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +46 -0
  69. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +7 -0
  70. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +14 -0
  71. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +51 -0
  72. package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +18 -0
  73. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +57 -1
  74. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +1 -3
  75. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -2
  76. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +11 -0
  77. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +23 -0
  78. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +46 -0
  79. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +1 -0
  80. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +172 -0
  81. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +30 -0
  82. package/sdk-core/protos/grpc/health/v1/health.proto +63 -0
  83. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +18 -15
  84. package/sdk-core/protos/testsrv_upstream/Makefile +80 -0
  85. package/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
  86. package/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
  87. package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
  88. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
  89. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
  90. package/sdk-core/sdk/Cargo.toml +2 -2
  91. package/sdk-core/sdk/src/lib.rs +2 -2
  92. package/sdk-core/sdk/src/workflow_context/options.rs +36 -8
  93. package/sdk-core/sdk/src/workflow_context.rs +30 -6
  94. package/sdk-core/sdk/src/workflow_future.rs +4 -4
  95. package/sdk-core/sdk-core-protos/Cargo.toml +5 -5
  96. package/sdk-core/sdk-core-protos/build.rs +9 -1
  97. package/sdk-core/sdk-core-protos/src/history_builder.rs +6 -1
  98. package/sdk-core/sdk-core-protos/src/lib.rs +93 -32
  99. package/sdk-core/test-utils/Cargo.toml +3 -3
  100. package/sdk-core/test-utils/src/canned_histories.rs +58 -0
  101. package/sdk-core/test-utils/src/lib.rs +35 -12
  102. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +128 -0
  103. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +55 -5
  104. package/sdk-core/tests/integ_tests/polling_tests.rs +2 -1
  105. package/sdk-core/tests/integ_tests/queries_tests.rs +5 -5
  106. package/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
  107. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -10
  108. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  109. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +14 -14
  110. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +2 -6
  111. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +12 -12
  112. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +12 -1
  113. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +3 -3
  114. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +8 -2
  115. package/sdk-core/tests/integ_tests/workflow_tests.rs +19 -4
  116. package/sdk-core/tests/load_tests.rs +2 -1
  117. package/sdk-core/tests/main.rs +17 -0
  118. package/sdk-core/tests/runner.rs +93 -0
  119. package/src/conversions.rs +157 -94
  120. package/src/helpers.rs +190 -0
  121. package/src/lib.rs +10 -912
  122. package/src/runtime.rs +436 -0
  123. package/src/testing.rs +67 -0
  124. 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,
@@ -10,6 +10,7 @@ use crate::{
10
10
  ActivityHeartbeat, Worker, WorkerConfigBuilder,
11
11
  };
12
12
  use futures::FutureExt;
13
+ use itertools::Itertools;
13
14
  use std::{
14
15
  cell::RefCell,
15
16
  collections::{hash_map::Entry, HashMap, VecDeque},
@@ -23,6 +24,7 @@ use std::{
23
24
  use temporal_client::WorkflowOptions;
24
25
  use temporal_sdk::{ActivityOptions, WfContext};
25
26
  use temporal_sdk_core_api::{errors::CompleteActivityError, Worker as WorkerTrait};
27
+ use temporal_sdk_core_protos::temporal::api::command::v1::ScheduleActivityTaskCommandAttributes;
26
28
  use temporal_sdk_core_protos::{
27
29
  coresdk::{
28
30
  activity_result::{
@@ -149,14 +151,14 @@ async fn heartbeats_report_cancels_only_once() {
149
151
  PollActivityTaskQueueResponse {
150
152
  task_token: vec![1],
151
153
  activity_id: "act1".to_string(),
152
- heartbeat_timeout: Some(Duration::from_millis(1).into()),
154
+ heartbeat_timeout: Some(prost_dur!(from_millis(1))),
153
155
  ..Default::default()
154
156
  }
155
157
  .into(),
156
158
  PollActivityTaskQueueResponse {
157
159
  task_token: vec![2],
158
160
  activity_id: "act2".to_string(),
159
- heartbeat_timeout: Some(Duration::from_millis(1).into()),
161
+ heartbeat_timeout: Some(prost_dur!(from_millis(1))),
160
162
  ..Default::default()
161
163
  }
162
164
  .into(),
@@ -226,7 +228,7 @@ async fn activity_cancel_interrupts_poll() {
226
228
  async {
227
229
  Some(Ok(PollActivityTaskQueueResponse {
228
230
  task_token: vec![1],
229
- heartbeat_timeout: Some(Duration::from_secs(1).into()),
231
+ heartbeat_timeout: Some(prost_dur!(from_secs(1))),
230
232
  ..Default::default()
231
233
  }))
232
234
  }
@@ -263,7 +265,7 @@ async fn activity_cancel_interrupts_poll() {
263
265
  act_poller: Some(Box::from(mock_poller)),
264
266
  ..Default::default()
265
267
  };
266
- let core = mock_worker(MocksHolder::from_mock_worker(mock_client.into(), mw));
268
+ let core = mock_worker(MocksHolder::from_mock_worker(mock_client, mw));
267
269
  let last_finisher = AtomicUsize::new(0);
268
270
  // Perform first poll to get the activity registered
269
271
  let act = core.poll_activity_task().await.unwrap();
@@ -315,7 +317,7 @@ async fn activity_poll_timeout_retries() {
315
317
  act_poller: Some(Box::from(mock_act_poller)),
316
318
  ..Default::default()
317
319
  };
318
- let core = mock_worker(MocksHolder::from_mock_worker(mock_client.into(), mw));
320
+ let core = mock_worker(MocksHolder::from_mock_worker(mock_client, mw));
319
321
  let r = core.poll_activity_task().await.unwrap();
320
322
  assert_matches!(r.task_token.as_slice(), b"hello!");
321
323
  }
@@ -333,7 +335,7 @@ async fn many_concurrent_heartbeat_cancels() {
333
335
  async move {
334
336
  Ok(PollActivityTaskQueueResponse {
335
337
  task_token: i.to_be_bytes().to_vec(),
336
- heartbeat_timeout: Some(Duration::from_millis(200).into()),
338
+ heartbeat_timeout: Some(prost_dur!(from_millis(200))),
337
339
  ..Default::default()
338
340
  })
339
341
  }
@@ -506,7 +508,7 @@ async fn can_heartbeat_acts_during_shutdown() {
506
508
  [PollActivityTaskQueueResponse {
507
509
  task_token: vec![1],
508
510
  activity_id: "act1".to_string(),
509
- heartbeat_timeout: Some(Duration::from_millis(1).into()),
511
+ heartbeat_timeout: Some(prost_dur!(from_millis(1))),
510
512
  ..Default::default()
511
513
  }
512
514
  .into()],
@@ -559,7 +561,7 @@ async fn complete_act_with_fail_flushes_heartbeat() {
559
561
  [PollActivityTaskQueueResponse {
560
562
  task_token: vec![1],
561
563
  activity_id: "act1".to_string(),
562
- heartbeat_timeout: Some(Duration::from_secs(10).into()),
564
+ heartbeat_timeout: Some(prost_dur!(from_secs(10))),
563
565
  ..Default::default()
564
566
  }
565
567
  .into()],
@@ -616,31 +618,61 @@ async fn max_tq_acts_set_passed_to_poll_properly() {
616
618
  /// delivered via polling.
617
619
  #[tokio::test]
618
620
  async fn activity_tasks_from_completion_are_delivered() {
621
+ // Construct the history - one task with 5 activities, 4 on the same task queue, and 1 on a
622
+ // different queue, 3 activities will be executed eagerly as specified by the
623
+ // MAX_EAGER_ACTIVITY_RESERVATIONS_PER_WORKFLOW_TASK constant.
619
624
  let wfid = "fake_wf_id";
620
625
  let mut t = TestHistoryBuilder::default();
621
626
  t.add_by_type(EventType::WorkflowExecutionStarted);
622
627
  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());
628
+ let act_same_queue_scheduled_ids = (1..4)
629
+ .map(|i| t.add_activity_task_scheduled(format!("act_id_{}_same_queue", i)))
630
+ .collect_vec();
631
+ t.add_activity_task_scheduled("act_id_same_queue_not_eager");
632
+ t.add_activity_task_scheduled("act_id_different_queue");
633
+ for scheduled_event_id in act_same_queue_scheduled_ids {
634
+ let started_event_id = t.add_activity_task_started(scheduled_event_id);
635
+ t.add_activity_task_completed(scheduled_event_id, started_event_id, b"hi".into());
636
+ }
626
637
  t.add_full_wf_task();
627
638
  t.add_workflow_execution_completed();
628
639
 
640
+ let num_eager_requested = Arc::new(AtomicUsize::new(0));
641
+ // Clone it to move into the callback below
642
+ let num_eager_requested_clone = num_eager_requested.clone();
643
+
629
644
  let mut mock = mock_workflow_client();
630
645
  mock.expect_complete_workflow_task()
631
646
  .times(1)
632
- .returning(move |_| {
647
+ .returning(move |req| {
648
+ // Store the number of eager activities requested to be checked below
649
+ let count = req
650
+ .commands
651
+ .into_iter()
652
+ .filter(|c| match c.attributes {
653
+ Some(Attributes::ScheduleActivityTaskCommandAttributes(
654
+ ScheduleActivityTaskCommandAttributes {
655
+ request_eager_execution,
656
+ ..
657
+ },
658
+ )) => request_eager_execution,
659
+ _ => false,
660
+ })
661
+ .count();
662
+ num_eager_requested_clone.store(count, Ordering::Relaxed);
633
663
  Ok(RespondWorkflowTaskCompletedResponse {
634
664
  workflow_task: None,
635
- activity_tasks: vec![PollActivityTaskQueueResponse {
636
- task_token: vec![1],
637
- activity_id: "act1".to_string(),
638
- ..Default::default()
639
- }],
665
+ activity_tasks: (1..4)
666
+ .map(|i| PollActivityTaskQueueResponse {
667
+ task_token: vec![i],
668
+ activity_id: format!("act_id_{}_same_queue", i),
669
+ ..Default::default()
670
+ })
671
+ .collect_vec(),
640
672
  })
641
673
  });
642
674
  mock.expect_complete_activity_task()
643
- .times(1)
675
+ .times(3)
644
676
  .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
645
677
  let mut mock = single_hist_mock_sg(wfid, t, [1], mock, true);
646
678
  let mut mock_poller = mock_manual_poller();
@@ -651,32 +683,65 @@ async fn activity_tasks_from_completion_are_delivered() {
651
683
  mock.worker_cfg(|wc| wc.max_cached_workflows = 2);
652
684
  let core = mock_worker(mock);
653
685
 
686
+ // Test start
654
687
  let wf_task = core.poll_workflow_activation().await.unwrap();
655
- core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
656
- wf_task.run_id,
688
+ let mut cmds = (1..4)
689
+ .map(|seq| {
690
+ ScheduleActivity {
691
+ seq,
692
+ activity_id: format!("act_id_{}_same_queue", seq),
693
+ task_queue: TEST_Q.to_string(),
694
+ cancellation_type: ActivityCancellationType::TryCancel as i32,
695
+ ..Default::default()
696
+ }
697
+ .into()
698
+ })
699
+ .collect_vec();
700
+ cmds.push(
657
701
  ScheduleActivity {
658
- seq: 1,
659
- activity_id: "act_id".to_string(),
702
+ seq: 4,
703
+ activity_id: "act_id_same_queue_not_eager".to_string(),
704
+ task_queue: TEST_Q.to_string(),
660
705
  cancellation_type: ActivityCancellationType::TryCancel as i32,
661
706
  ..Default::default()
662
707
  }
663
708
  .into(),
709
+ );
710
+ cmds.push(
711
+ ScheduleActivity {
712
+ seq: 5,
713
+ activity_id: "act_id_different_queue".to_string(),
714
+ task_queue: "different_queue".to_string(),
715
+ cancellation_type: ActivityCancellationType::Abandon as i32,
716
+ ..Default::default()
717
+ }
718
+ .into(),
719
+ );
720
+
721
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
722
+ wf_task.run_id,
723
+ cmds,
664
724
  ))
665
725
  .await
666
726
  .unwrap();
667
727
 
668
- // We should see the activity when we poll now
669
- let act_task = core.poll_activity_task().await.unwrap();
670
- assert_eq!(act_task.task_token, vec![1]);
728
+ // We should see the 3 eager activities when we poll now
729
+ for i in 1..4 {
730
+ let act_task = core.poll_activity_task().await.unwrap();
731
+ assert_eq!(act_task.task_token, vec![i]);
671
732
 
672
- core.complete_activity_task(ActivityTaskCompletion {
673
- task_token: act_task.task_token.clone(),
674
- result: Some(ActivityExecutionResult::ok("hi".into())),
675
- })
676
- .await
677
- .unwrap();
733
+ core.complete_activity_task(ActivityTaskCompletion {
734
+ task_token: act_task.task_token.clone(),
735
+ result: Some(ActivityExecutionResult::ok("hi".into())),
736
+ })
737
+ .await
738
+ .unwrap();
739
+ }
678
740
 
679
741
  core.shutdown().await;
742
+
743
+ // Verify only a single eager activity was scheduled (the one on our worker's task queue)
744
+ assert_eq!(num_eager_requested.load(Ordering::Relaxed), 3);
680
745
  }
681
746
 
682
747
  #[tokio::test]
@@ -818,7 +883,7 @@ async fn retryable_net_error_exhaustion_is_nonfatal() {
818
883
  [PollActivityTaskQueueResponse {
819
884
  task_token: vec![1],
820
885
  activity_id: "act1".to_string(),
821
- heartbeat_timeout: Some(Duration::from_secs(10).into()),
886
+ heartbeat_timeout: Some(prost_dur!(from_secs(10))),
822
887
  ..Default::default()
823
888
  }
824
889
  .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
+ }