@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.
- package/Cargo.lock +765 -128
- package/Cargo.toml +2 -2
- package/common.js +7 -3
- package/index.d.ts +118 -5
- package/index.js +2 -6
- package/package.json +2 -3
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
- package/releases/x86_64-apple-darwin/index.node +0 -0
- package/releases/x86_64-pc-windows-msvc/index.node +0 -0
- package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
- package/scripts/build.js +4 -3
- package/sdk-core/.buildkite/docker/Dockerfile +2 -1
- package/sdk-core/.buildkite/pipeline.yml +2 -0
- package/sdk-core/.cargo/config.toml +1 -1
- package/sdk-core/ARCHITECTURE.md +2 -2
- package/sdk-core/README.md +12 -0
- package/sdk-core/bridge-ffi/Cargo.toml +2 -2
- package/sdk-core/bridge-ffi/src/lib.rs +2 -2
- package/sdk-core/client/Cargo.toml +7 -5
- package/sdk-core/client/src/lib.rs +354 -226
- package/sdk-core/client/src/metrics.rs +13 -11
- package/sdk-core/client/src/raw.rs +352 -107
- package/sdk-core/client/src/retry.rs +188 -147
- package/sdk-core/client/src/workflow_handle/mod.rs +1 -1
- package/sdk-core/core/Cargo.toml +28 -15
- package/sdk-core/core/src/core_tests/activity_tasks.rs +98 -33
- package/sdk-core/core/src/core_tests/child_workflows.rs +125 -3
- package/sdk-core/core/src/core_tests/local_activities.rs +6 -6
- package/sdk-core/core/src/core_tests/workers.rs +3 -2
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +70 -2
- package/sdk-core/core/src/ephemeral_server/mod.rs +515 -0
- package/sdk-core/core/src/lib.rs +62 -28
- package/sdk-core/core/src/pollers/mod.rs +2 -0
- package/sdk-core/core/src/pollers/poll_buffer.rs +4 -4
- package/sdk-core/core/src/replay/mod.rs +3 -3
- package/sdk-core/core/src/retry_logic.rs +10 -9
- package/sdk-core/core/src/telemetry/metrics.rs +48 -39
- package/sdk-core/core/src/telemetry/mod.rs +46 -12
- package/sdk-core/core/src/telemetry/prometheus_server.rs +17 -13
- package/sdk-core/core/src/test_help/mod.rs +18 -8
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +10 -10
- package/sdk-core/core/src/worker/activities/local_activities.rs +13 -13
- package/sdk-core/core/src/worker/activities.rs +6 -12
- package/sdk-core/core/src/worker/client/mocks.rs +1 -0
- package/sdk-core/core/src/worker/client.rs +193 -64
- package/sdk-core/core/src/worker/mod.rs +14 -19
- package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -0
- package/sdk-core/core/src/worker/workflow/history_update.rs +5 -5
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +133 -85
- package/sdk-core/core/src/worker/workflow/machines/mod.rs +3 -2
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +160 -105
- package/sdk-core/core/src/worker/workflow/managed_run.rs +2 -1
- package/sdk-core/core/src/worker/workflow/mod.rs +62 -58
- package/sdk-core/core/src/worker/workflow/run_cache.rs +5 -3
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +7 -5
- package/sdk-core/core-api/Cargo.toml +3 -3
- package/sdk-core/core-api/src/errors.rs +3 -11
- package/sdk-core/core-api/src/worker.rs +7 -0
- package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +1 -1
- package/sdk-core/protos/api_upstream/.github/CODEOWNERS +1 -1
- package/sdk-core/protos/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +2 -6
- package/sdk-core/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +29 -0
- package/sdk-core/protos/api_upstream/Makefile +2 -2
- package/sdk-core/protos/api_upstream/buf.yaml +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +86 -0
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +46 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +7 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +14 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +51 -0
- package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +18 -0
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +57 -1
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +1 -3
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -2
- package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +11 -0
- package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +23 -0
- package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +46 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +172 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +30 -0
- package/sdk-core/protos/grpc/health/v1/health.proto +63 -0
- package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +18 -15
- package/sdk-core/protos/testsrv_upstream/Makefile +80 -0
- package/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
- package/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
- package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
- package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
- package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
- package/sdk-core/sdk/Cargo.toml +2 -2
- package/sdk-core/sdk/src/lib.rs +2 -2
- package/sdk-core/sdk/src/workflow_context/options.rs +36 -8
- package/sdk-core/sdk/src/workflow_context.rs +30 -6
- package/sdk-core/sdk/src/workflow_future.rs +4 -4
- package/sdk-core/sdk-core-protos/Cargo.toml +5 -5
- package/sdk-core/sdk-core-protos/build.rs +9 -1
- package/sdk-core/sdk-core-protos/src/history_builder.rs +6 -1
- package/sdk-core/sdk-core-protos/src/lib.rs +93 -32
- package/sdk-core/test-utils/Cargo.toml +3 -3
- package/sdk-core/test-utils/src/canned_histories.rs +58 -0
- package/sdk-core/test-utils/src/lib.rs +35 -12
- package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +128 -0
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +55 -5
- package/sdk-core/tests/integ_tests/polling_tests.rs +2 -1
- package/sdk-core/tests/integ_tests/queries_tests.rs +5 -5
- package/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -10
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +14 -14
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +2 -6
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +12 -12
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +12 -1
- package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +3 -3
- package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +8 -2
- package/sdk-core/tests/integ_tests/workflow_tests.rs +19 -4
- package/sdk-core/tests/load_tests.rs +2 -1
- package/sdk-core/tests/main.rs +17 -0
- package/sdk-core/tests/runner.rs +93 -0
- package/src/conversions.rs +157 -94
- package/src/helpers.rs +190 -0
- package/src/lib.rs +10 -912
- package/src/runtime.rs +436 -0
- package/src/testing.rs +67 -0
- 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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
624
|
-
|
|
625
|
-
|
|
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:
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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(
|
|
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
|
-
|
|
656
|
-
|
|
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:
|
|
659
|
-
activity_id: "
|
|
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
|
|
669
|
-
|
|
670
|
-
|
|
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
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
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(
|
|
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::{
|
|
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
|
|
9
|
-
|
|
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.
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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,
|
|
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
|
+
}
|